Browse Source

big: add shl1, shr1.

Jeroen van Rijn 4 years ago
parent
commit
1d0b37c1d8
4 changed files with 213 additions and 8 deletions
  1. 127 1
      core/math/big/basic.odin
  2. 9 4
      core/math/big/example.odin
  3. 44 2
      core/math/big/helpers.odin
  4. 33 1
      core/math/big/logical.odin

+ 127 - 1
core/math/big/basic.odin

@@ -8,7 +8,7 @@ package big
 	For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
 	The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
 
-	This file contains basic arithmetic operations like `add` and `sub`.
+	This file contains basic arithmetic operations like `add`, `sub`, `div`, ...
 */
 
 import "core:mem"
@@ -337,6 +337,132 @@ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) {
 	return clamp(dest);
 }
 
+
+/*
+	dest = src  / 2
+	dest = src >> 1
+*/
+int_halve :: proc(dest, src: ^Int) -> (err: Error) {
+	dest := dest; src := src;
+	if err = clear_if_uninitialized(dest); err != .None {
+		return err;
+	}
+	/*
+		Grow destination as required.
+	*/
+	if dest != src {
+		if err = grow(dest, src.used); err != .None {
+			return err;
+		}
+	}
+
+	old_used  := dest.used;
+	dest.used  = src.used;
+
+	/*
+		Carry
+	*/
+	fwd_carry := DIGIT(0);
+
+	for x := dest.used; x >= 0; x -= 1 {
+		/*
+			Get the carry for the next iteration.
+		*/
+		src_digit := src.digit[x];
+		carry     := src_digit & 1;
+		/*
+			Shift the current digit, add in carry and store.
+		*/
+		dest.digit[x] = (src_digit >> 1) | (fwd_carry << (_DIGIT_BITS - 1));
+      	/*
+      		Forward carry to next iteration.
+      	*/
+      	fwd_carry = carry;
+   	}
+
+ 	zero_count := old_used - dest.used;
+	/*
+		Zero remainder.
+	*/
+	if zero_count > 0 {
+		mem.zero_slice(dest.digit[dest.used:][:zero_count]);
+	}
+	/*
+		Adjust dest.used based on leading zeroes.
+	*/
+	dest.sign = src.sign;
+	return clamp(dest);
+}
+halve :: proc { int_halve, };
+shr1  :: halve;
+
+/*
+	dest = src  * 2
+	dest = src << 1
+*/
+int_double :: proc(dest, src: ^Int) -> (err: Error) {
+	dest := dest; src := src;
+	if err = clear_if_uninitialized(dest); err != .None {
+		return err;
+	}
+	/*
+		Grow destination as required.
+	*/
+	if dest != src {
+		if err = grow(dest, src.used + 1); err != .None {
+			return err;
+		}
+	}
+
+	old_used  := dest.used;
+	dest.used  = src.used + 1;
+
+	/*
+		Forward carry
+	*/
+	carry := DIGIT(0);
+	for x := 0; x < src.used; x += 1 {
+		/*
+			Get what will be the *next* carry bit from the MSB of the current digit.
+		*/
+		src_digit := src.digit[x];
+		fwd_carry := src_digit >> (_DIGIT_BITS - 1);
+
+		/*
+			Now shift up this digit, add in the carry [from the previous]
+		*/
+		dest.digit[x] = (src_digit << 1 | carry) & _MASK;
+
+		/*
+			Update carry
+		*/
+		carry = fwd_carry;
+	}
+	/*
+		New leading digit?
+	*/
+	if carry != 0 {
+		/*
+			Add a MSB which is always 1 at this point.
+		*/
+		dest.digit[dest.used] = 1;
+	}
+ 	zero_count := old_used - dest.used;
+	/*
+		Zero remainder.
+	*/
+	if zero_count > 0 {
+		mem.zero_slice(dest.digit[dest.used:][:zero_count]);
+	}
+	/*
+		Adjust dest.used based on leading zeroes.
+	*/
+	dest.sign = src.sign;
+	return clamp(dest);
+}
+double :: proc { int_double, };
+shl1   :: double;
+
 /*
 	==========================
 		Low-level routines    

+ 9 - 4
core/math/big/example.odin

@@ -58,12 +58,17 @@ demo :: proc() {
 	defer destroy(a, b, c);
 
 	err = set(a, 512);
-	err = set(b, a);
+	err = set(b, 1);
+	for x := 0; x < 11; x += 1 {
+		err = shl1(b, b);
+		print("b", b, 16);
+	}
+	fmt.println();
 	err = set(c, -4);
 
-	print("a", a, 2);
-	print("b", b, 2);
-	print("c", c, 2);
+	print("a", a, 10);
+	print("b", b, 10);
+	print("c", c, 10);
 
 	fmt.println("=== a = a & b ===");
 	err = and(a, a, b);

+ 44 - 2
core/math/big/helpers.odin

@@ -11,6 +11,7 @@ package big
 
 import "core:mem"
 import "core:intrinsics"
+
 /*
 	Deallocates the backing memory of one or more `Int`s.
 */
@@ -366,6 +367,49 @@ count_bits :: proc(a: ^Int) -> (count: int, err: Error) {
 	return;
 }
 
+/*
+	Counts the number of LSBs which are zero before the first zero bit
+*/
+count_lsb :: proc(a: ^Int) -> (count: int, err: Error) {
+	if err = clear_if_uninitialized(a); err != .None {
+		return 0, err;
+	}
+
+	lnz := []u8{4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0};
+
+	q: DIGIT;
+
+	/*
+		Early out for zero.
+	*/
+	if z, _ := is_zero(a); z {
+		return 0, .None;
+	}
+
+	/*
+		Scan lower digits until non-zero.
+	*/
+	for count = 0; (count < a.used && a.digit[count] == 0); count += 1 {}
+	q = a.digit[count];
+	count *= _DIGIT_BITS;
+
+   	/*
+   		Now scan this digit until a 1 is found.
+   	*/
+   	if q & 1 == 0 {
+    	p: DIGIT;
+      	for {
+        	p = q & 15;
+        	count += int(lnz[p]);
+        	q >>= 4;
+        	if p != 0 {
+	         	break;
+        	}
+		}
+	}
+   	return count, .None;
+}
+
 /*
 	Internal helpers.
 */
@@ -402,7 +446,6 @@ clamp :: proc(a: ^Int) -> (err: Error) {
 	if err = clear_if_uninitialized(a); err != .None {
 		return err;
 	}
-
 	for a.used > 0 && a.digit[a.used - 1] == 0 {
 		a.used -= 1;
 	}
@@ -410,6 +453,5 @@ clamp :: proc(a: ^Int) -> (err: Error) {
 	if z, _ := is_zero(a); z {
 		a.sign = .Zero_or_Positive;
 	}
-
 	return .None;
 }

+ 33 - 1
core/math/big/logical.odin

@@ -212,4 +212,36 @@ xor :: proc(dest, a, b: ^Int) -> (err: Error) {
 	dest.used = used;
 	dest.sign = .Negative if neg else .Zero_or_Positive;
 	return clamp(dest);
-}
+}
+
+/*
+	dest = ~src
+*/
+int_complement :: proc(dest, src: ^Int) -> (err: Error) {
+	/*
+		Check that src and dest are usable.
+	*/
+	if err = clear_if_uninitialized(src); err != .None {
+		return err;
+	}
+	if err = clear_if_uninitialized(dest); err != .None {
+		return err;
+	}
+
+	/*
+		Temporarily fix sign.
+	*/
+	old_sign := src.sign;
+	z, _ := is_zero(src);
+
+	src.sign = .Negative if (src.sign == .Zero_or_Positive || z) else .Zero_or_Positive;
+
+	err = sub(dest, src, 1);
+	/*
+		Restore sign.
+	*/
+	src.sign = old_sign;
+
+	return err;
+}
+complement :: proc { int_complement, };