Browse Source

big: Add `gcd_lcm` fast path in wrapper.

Jeroen van Rijn 4 years ago
parent
commit
320387c4ee
4 changed files with 40 additions and 31 deletions
  1. 35 23
      core/math/big/basic.odin
  2. 2 3
      core/math/big/build.bat
  3. 1 3
      core/math/big/example.odin
  4. 2 2
      core/math/big/test.odin

+ 35 - 23
core/math/big/basic.odin

@@ -1271,26 +1271,45 @@ _int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (remain
 */
 */
 int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) {
 int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) {
 	if err = clear_if_uninitialized(res_gcd, res_lcm, a, b); err != .None { return err; }
 	if err = clear_if_uninitialized(res_gcd, res_lcm, a, b); err != .None { return err; }
+
+	az, _ := is_zero(a); bz, _ := is_zero(b);
+	if az && bz {
+		if res_gcd != nil {
+			if err = zero(res_gcd); err != .None { return err; }
+		}
+		if res_lcm != nil {
+			if err = zero(res_lcm); err != .None { return err; }
+		}
+		return .None;
+	}
+	else if az {
+		if res_gcd != nil {
+			if err = abs(res_gcd, b); err != .None { return err; }
+		}
+		if res_lcm != nil {
+			if err = zero(res_lcm);   err != .None { return err; }
+		}
+		return .None;
+	}
+	else if bz {
+		if res_gcd != nil {
+			if err = abs(res_gcd, a); err != .None { return err; }
+		}
+		if res_lcm != nil {
+			if err = zero(res_lcm);   err != .None { return err; }
+		}
+		return .None;
+	}
+
 	return #force_inline _int_gcd_lcm(res_gcd, res_lcm, a, b);
 	return #force_inline _int_gcd_lcm(res_gcd, res_lcm, a, b);
 }
 }
 gcd_lcm :: proc { int_gcd_lcm, };
 gcd_lcm :: proc { int_gcd_lcm, };
 
 
 /*
 /*
-	Greatest Common Divisor using the binary method.
+	Greatest Common Divisor.
 */
 */
 int_gcd :: proc(res, a, b: ^Int) -> (err: Error) {
 int_gcd :: proc(res, a, b: ^Int) -> (err: Error) {
-	if err = clear_if_uninitialized(res, a, b); err != .None { return err; }
-
-	/*
-		If both `a` and `b` are zero, return zero.
-		If either `a` or `b`, return the other one.
-	*/
-	az, _ := is_zero(a); bz, _ := is_zero(b);
-	if az && bz { return zero(res); }
-	else if az {  return abs(res, b); }
-	else if bz {  return abs(res, a); }
-
-	return #force_inline _int_gcd_lcm(res, nil, a, b);
+	return #force_inline int_gcd_lcm(res, nil, a, b);
 }
 }
 gcd :: proc { int_gcd, };
 gcd :: proc { int_gcd, };
 
 
@@ -1298,20 +1317,13 @@ gcd :: proc { int_gcd, };
 	Least Common Multiple.
 	Least Common Multiple.
 */
 */
 int_lcm :: proc(res, a, b: ^Int) -> (err: Error) {
 int_lcm :: proc(res, a, b: ^Int) -> (err: Error) {
-	if err = clear_if_uninitialized(res, a, b); err != .None { return err; }
-
-	/*
-		If both `a` and `b` are zero, return zero.
-	*/
-	az, _ := is_zero(a); bz, _ := is_zero(b);
-	if az || bz { return zero(res); }
-
-	return #force_inline _int_gcd_lcm(nil, res, a, b);
+	return #force_inline int_gcd_lcm(nil, res, a, b);
 }
 }
 lcm :: proc { int_lcm, };
 lcm :: proc { int_lcm, };
 
 
 /*
 /*
-	Internal function computing both GCD and (if target isn't `nil`) also LCM.
+	Internal function computing both GCD using the binary method,
+	and, if target isn't `nil`, also LCM.
 	Expects the arguments to have been initialized.
 	Expects the arguments to have been initialized.
 */
 */
 _int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) {
 _int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) {

+ 2 - 3
core/math/big/build.bat

@@ -1,10 +1,9 @@
 @echo off
 @echo off
-:odin run . -vet
+odin run . -vet
 :odin build . -build-mode:shared -show-timings -o:minimal -use-separate-modules
 :odin build . -build-mode:shared -show-timings -o:minimal -use-separate-modules
 :odin build . -build-mode:shared -show-timings -o:size -use-separate-modules -no-bounds-check
 :odin build . -build-mode:shared -show-timings -o:size -use-separate-modules -no-bounds-check
 :odin build . -build-mode:shared -show-timings -o:size -use-separate-modules
 :odin build . -build-mode:shared -show-timings -o:size -use-separate-modules
 :odin build . -build-mode:shared -show-timings -o:speed -use-separate-modules -no-bounds-check
 :odin build . -build-mode:shared -show-timings -o:speed -use-separate-modules -no-bounds-check
 :odin build . -build-mode:shared -show-timings -o:speed -use-separate-modules
 :odin build . -build-mode:shared -show-timings -o:speed -use-separate-modules
 
 
-python test.py
-:del big.*
+:python test.py

+ 1 - 3
core/math/big/example.odin

@@ -115,7 +115,7 @@ demo :: proc() {
 	defer destroy(a, b, c, d, e, f);
 	defer destroy(a, b, c, d, e, f);
 
 
 	set(a, 125);
 	set(a, 125);
-	set(b, 0);
+	set(b, 75);
 
 
 	err = gcd_lcm(c, d, a, b);
 	err = gcd_lcm(c, d, a, b);
 	fmt.printf("gcd_lcm(");
 	fmt.printf("gcd_lcm(");
@@ -138,8 +138,6 @@ demo :: proc() {
 	print(", b =", b, 10, false, true, false);
 	print(", b =", b, 10, false, true, false);
 	print(") =",   c, 10, false, true, false);
 	print(") =",   c, 10, false, true, false);
 	fmt.printf(" (err = %v)\n", err);
 	fmt.printf(" (err = %v)\n", err);
-
-
 }
 }
 
 
 main :: proc() {
 main :: proc() {

+ 2 - 2
core/math/big/test.odin

@@ -324,8 +324,8 @@ PyRes :: struct {
 	ai, bi, dest := &Int{}, &Int{}, &Int{};
 	ai, bi, dest := &Int{}, &Int{}, &Int{};
 	defer destroy(ai, bi, dest);
 	defer destroy(ai, bi, dest);
 
 
-	if err = atoi(ai, string(a), 16); err != .None { return PyRes{res=":gcd:atoi(a):", err=err}; }
-	if err = atoi(bi, string(b), 16); err != .None { return PyRes{res=":gcd:atoi(b):", err=err}; }
+	if err = atoi(ai, string(a), 16); err != .None { return PyRes{res=":lcm:atoi(a):", err=err}; }
+	if err = atoi(bi, string(b), 16); err != .None { return PyRes{res=":lcm:atoi(b):", err=err}; }
 	if err = lcm(dest, ai, bi); err != .None { return PyRes{res=":lcm:lcm(a, b):", err=err}; }	
 	if err = lcm(dest, ai, bi); err != .None { return PyRes{res=":lcm:lcm(a, b):", err=err}; }	
 
 
 	r: cstring;
 	r: cstring;