shithub: mc

Download patch

ref: 372a754791164af783fe0949f99bb00629cf1333
parent: f88d2664b6c80eb13ae0041ba1713a57a384ca20
author: Ori Bernstein <[email protected]>
date: Tue Jan 21 18:31:13 EST 2014

Add initial implementation of bigint.

--- a/libstd/Makefile
+++ b/libstd/Makefile
@@ -1,6 +1,7 @@
 MYRLIB=std
 MYRSRC= \
     alloc.myr \
+    bigint.myr \
     blat.myr \
     chartype.myr \
     cmp.myr \
--- /dev/null
+++ b/libstd/bigint.myr
@@ -1,0 +1,358 @@
+use "alloc.use"
+use "cmp.use"
+use "die.use"
+use "extremum.use"
+use "fmt.use"
+use "option.use"
+use "slcp.use"
+use "sldup.use"
+use "slpush.use"
+use "types.use"
+use "utf.use"
+
+pkg std =
+	type bigint = struct
+		dig	: uint32[:] 	/* little endian, no leading zeros. */
+		sign	: int		/* -1 for -ve, 0 for zero, 1 for +ve. */
+	;;
+
+	/* administrivia */
+	const mkbigint	: (v : int32 -> bigint#)
+	const bigfree	: (a : bigint# -> void)
+	const bigdup	: (a : bigint# -> bigint#)
+	const bigparse	: (a : bigint# -> option(byte[:]))
+	const bigfmt	: (b : byte[:], a : bigint# -> size)
+
+	/* some useful predicates */
+	const bigiszero	: (a : bigint# -> bool)
+	const bigcmp	: (a : bigint#, b : bigint# -> order)
+
+	/* bigint*bigint -> bigint ops */
+	const bigadd	: (a : bigint#, b : bigint# -> bigint#)
+	const bigsub	: (a : bigint#, b : bigint# -> bigint#)
+	const bigmul	: (a : bigint#, b : bigint# -> bigint#)
+	const bigdiv	: (a : bigint#, b : bigint# -> bigint#)
+	const bigshl	: (a : bigint#, b : bigint# -> bigint#)
+
+	/* bigint*int -> bigint ops */
+	const bigaddi	: (a : bigint#, b : int64 -> bigint#)
+	const bigsubi	: (a : bigint#, b : int64 -> bigint#)
+	const bigmuli	: (a : bigint#, b : int64 -> bigint#)
+	const bigdivi	: (a : bigint#, b : int64 -> bigint#)
+	const bigshli	: (a : bigint#, b : uint64 -> bigint#)
+	const bigshri	: (a : bigint#, b : uint64 -> bigint#)
+	const bigsari	: (a : bigint#, b : uint64 -> bigint#)
+;;
+
+const mkbigint = {v
+	var a
+	a = zalloc()
+
+	a.dig = slalloc(1)
+	if v < 0
+		a.sign = -1
+		v = -v
+	elif v > 0
+		a.sign = 1
+	;;
+	a.dig[0] = (v castto(uint32))
+	-> trim(a)
+}
+
+const bigfree = {a
+	slfree(a.dig)
+	free(a)
+}
+
+const bigdup = {a
+	var v
+
+	v = zalloc()
+	v.dig = sldup(a.dig)
+	v.sign = a.sign
+	-> v
+}
+
+/* for now, just dump out something for debugging... */
+const bigfmt = {buf, val
+	const digitchars = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f']
+	var n, i, pow
+
+	n = 0
+	if val.sign == -1
+		n += encode(buf, '-')
+	;;
+
+	pow = 0
+	for v in val.dig
+		if pow > 0
+			n += bfmt(buf[n:], "(")
+			for i = 0; i < pow; i++
+				n += bfmt(buf[n:], "4294967296")
+				if i != pow - 1
+					n += bfmt(buf[n:], "*")
+				;;
+			;;
+			n += bfmt(buf[n:], ")*")
+		;;
+		pow++
+		n += bfmt(buf[n:], "%ui + ", v)
+	;;
+
+	if val.sign == 0
+		n += encode(buf, '0')
+	;;
+	-> n
+}
+
+/*
+const bigparse = {str
+	var c, i
+	var val, base
+	var a
+
+	if hasprefix(str, "0x") || hasprefix(str, "0X")
+		base = 16
+	if hasprefix(str, "0o") || hasprefix(str, "0O")
+		base = 8
+	if hasprefix(str, "0b") || hasprefix(str, "0B")
+		base = 2
+	else
+		base = 10
+	;;
+
+	a = mkbigint()
+	while str.len != 0
+		(c, str) = striter(str)
+		val = charval(c)
+		if val < 0
+			bigfree(a)
+			-> `None
+		;;
+		bigmuli(a, base)
+		bigaddi(a, val)
+	;;
+	-> `Some a
+}
+*/
+
+const bigiszero = {v
+	-> v.sign == 0
+}
+
+const bigcmp = {a, b
+	var i
+
+	if a.sign < b.sign
+		-> `Before
+	elif a.sign > b.sign
+		-> `After
+	else
+		/* the one with more digits has greater magnitude */
+		if a.dig.len > b.dig.len
+			-> signedmagorder(a.sign)
+		;;
+		/* otherwise, the one with the first larger digit is bigger */
+		for i = a.dig.len; i > 0; i--
+			if a.dig[i - 1] > b.dig[i - 1]
+				-> signedmagorder(a.sign)
+			elif b.dig[i - 1] > a.dig[i - 1]
+				-> signedmagorder(a.sign)
+			;;
+		;;
+	;;
+	-> `Equal
+}
+
+const signedmagorder = {sign
+	if sign < 0
+		-> `Before
+	else
+		-> `After
+	;;
+}
+
+/* a += b */
+const bigadd = {a, b
+	if a.sign == b.sign
+		-> uadd(a, b)
+	else
+		match bigcmp(a, b)
+		| `Before: /* a is negative */
+		    a.sign = b.sign
+		    -> usub(b, a)
+		| `After: /* b is negative */
+		    -> usub(a, b)
+		| `Equal:
+			die("Impossible. Equal vals with different sign.")
+		;;
+	;;
+}
+
+/* adds two unsigned values together. */
+const uadd = {a, b
+	var v, i
+	var carry
+	var n
+
+	carry = 0
+	n = min(a.dig.len, b.dig.len)
+	/* guaranteed to carry no more than one value */
+	a.dig = slpush(a.dig, 0)
+	for i = 0; i < n; i++
+		v = (a.dig[i] castto(uint64)) + (b.dig[i] castto(uint64)) + carry;
+		if v > (0xffffffff castto(uint64))
+			carry = 1
+		else
+			carry = 0
+		;;
+		a.dig[i] = v castto(uint32)
+	;;
+	a.dig[i] += carry castto(uint32)
+	-> trim(a)
+}
+
+/* a -= b */
+const bigsub = {a, b
+	if a.sign != b.sign
+		-> uadd(a, b)
+	else
+		match bigcmp(a, b)
+		| `Before: /* a is negative */
+		    a.sign = b.sign
+		    -> usub(b, a)
+		| `After: /* b is negative */
+		    -> usub(a, b)
+		| `Equal:
+			die("Impossible. Equal vals with different sign.")
+		;;
+	;;
+	-> a
+}
+
+/* subtracts two unsigned values, where 'a' is strictly greater than 'b' */
+const usub = {a, b
+	var carry
+	var v, i
+
+	carry = 0
+	for i = 0; i < a.dig.len; i++
+		v = (a.dig[i] castto(int64)) - (b.dig[i] castto(int64)) - carry
+		if v < 0
+			carry = 1
+		else
+			carry = 0
+		;;
+		a.dig[i] = v castto(uint32)
+	;;
+	-> trim(a)
+}
+
+/* a *= b */
+const bigmul = {a, b
+	var i, j
+	var ai, bj, wij
+	var carry, t
+	var w
+
+	if a.sign != b.sign
+		a.sign = -1
+	else
+		a.sign = 1
+	;;
+	w  = slzalloc(a.dig.len + b.dig.len)
+	for j = 0; j < b.dig.len; j++
+		if a.dig[j] == 0
+			w[j] = 0
+			continue
+		;;
+		carry = 0
+		for i = 0; i < a.dig.len; i++
+			ai = a.dig[i] castto(uint64)
+			bj = b.dig[j] castto(uint64)
+			wij = w[i+j] castto(uint64)
+			t = ai * bj + wij + carry
+			w[i + j] = (t castto(uint32))
+			carry = t >> 32
+		;;
+		w[i+j] = carry castto(uint32)
+	;;
+	slfree(a.dig)
+	a.dig = w
+	-> trim(a)
+}
+
+/* a /= b */
+const bigdiv = {a, b
+	if bigiszero(b)
+		die("divide by zero\n")
+	;;
+
+	if a.sign != b.sign
+		a.sign = -1
+	else
+		a.sign = 1
+	;;
+
+	die("big division not yet implemented\n")
+	-> trim(a)
+}
+
+/* a <<= b */
+const bigshl = {a, b
+	match b.dig.len
+	| 0:	-> a
+	| 1:	-> bigshli(a, b.dig[0] castto(uint64))
+	| n:	die("shift by way too much\n")
+	;;
+}
+
+/*
+const bigsll = {a, b
+}
+
+const bigshr = {a, b
+}
+*/
+
+const trim = {a
+	var i
+
+	for i = a.dig.len; i > 0; i--
+		if a.dig[i - 1] != 0
+			break
+		;;
+	;;
+	a.dig = slgrow(a.dig, i)
+	if i == 0
+		a.sign = 0
+	;;
+	-> a
+}
+
+const bigshli = {a, s
+	var off, shift
+	var t, carry
+	var i
+
+	off = s/32
+	shift = s % 32
+
+	a.dig = slzgrow(a.dig, 1 + a.dig.len + off castto(size))
+	/* blit over the base values */
+	put("off = %z\n", off)
+	for i = a.dig.len; i > off; i--
+		put("a.dig[%z] = a.dig[%z] (%i)\n", i - 1, i - 1 - off, a.dig[i - 1 - off])
+		a.dig[i - 1] = a.dig[i - 1 - off]
+	;;
+	for i = 0; i < off; i++
+		a.dig[i] = 0
+	;;
+	/* and shift over by the remainder */
+	for i = 0; i < a.dig.len; i++
+		t = (a.dig[i] castto(uint64)) << shift
+		a.dig[i] = (t | carry) castto(uint32) 
+		carry = t >> 32
+	;;
+	-> trim(a)
+}