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.
 	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.
 	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"
 import "core:mem"
@@ -337,6 +337,132 @@ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) {
 	return clamp(dest);
 	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    
 		Low-level routines    

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

@@ -58,12 +58,17 @@ demo :: proc() {
 	defer destroy(a, b, c);
 	defer destroy(a, b, c);
 
 
 	err = set(a, 512);
 	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);
 	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 ===");
 	fmt.println("=== a = a & b ===");
 	err = and(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:mem"
 import "core:intrinsics"
 import "core:intrinsics"
+
 /*
 /*
 	Deallocates the backing memory of one or more `Int`s.
 	Deallocates the backing memory of one or more `Int`s.
 */
 */
@@ -366,6 +367,49 @@ count_bits :: proc(a: ^Int) -> (count: int, err: Error) {
 	return;
 	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.
 	Internal helpers.
 */
 */
@@ -402,7 +446,6 @@ clamp :: proc(a: ^Int) -> (err: Error) {
 	if err = clear_if_uninitialized(a); err != .None {
 	if err = clear_if_uninitialized(a); err != .None {
 		return err;
 		return err;
 	}
 	}
-
 	for a.used > 0 && a.digit[a.used - 1] == 0 {
 	for a.used > 0 && a.digit[a.used - 1] == 0 {
 		a.used -= 1;
 		a.used -= 1;
 	}
 	}
@@ -410,6 +453,5 @@ clamp :: proc(a: ^Int) -> (err: Error) {
 	if z, _ := is_zero(a); z {
 	if z, _ := is_zero(a); z {
 		a.sign = .Zero_or_Positive;
 		a.sign = .Zero_or_Positive;
 	}
 	}
-
 	return .None;
 	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.used = used;
 	dest.sign = .Negative if neg else .Zero_or_Positive;
 	dest.sign = .Negative if neg else .Zero_or_Positive;
 	return clamp(dest);
 	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, };