mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-05 15:18:49 +00:00
big: Add gcd.
This commit is contained in:
@@ -25,8 +25,8 @@ import "core:intrinsics"
|
|||||||
*/
|
*/
|
||||||
int_add :: proc(dest, a, b: ^Int) -> (err: Error) {
|
int_add :: proc(dest, a, b: ^Int) -> (err: Error) {
|
||||||
dest := dest; x := a; y := b;
|
dest := dest; x := a; y := b;
|
||||||
if err = clear_if_uninitialized(a); err != .None { return err; }
|
if err = clear_if_uninitialized(x); err != .None { return err; }
|
||||||
if err = clear_if_uninitialized(b); err != .None { return err; }
|
if err = clear_if_uninitialized(y); err != .None { return err; }
|
||||||
if err = clear_if_uninitialized(dest); err != .None { return err; }
|
if err = clear_if_uninitialized(dest); err != .None { return err; }
|
||||||
/*
|
/*
|
||||||
All parameters have been initialized.
|
All parameters have been initialized.
|
||||||
@@ -773,6 +773,9 @@ int_factorial :: proc(res: ^Int, n: DIGIT) -> (err: Error) {
|
|||||||
}
|
}
|
||||||
factorial :: proc { int_factorial, };
|
factorial :: proc { int_factorial, };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
==========================
|
==========================
|
||||||
Low-level routines
|
Low-level routines
|
||||||
@@ -1264,6 +1267,92 @@ _int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (remain
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Greatest Common Divisor using the binary method.
|
||||||
|
|
||||||
|
TODO(Jeroen):
|
||||||
|
- Maybe combine with LCM and have an `_int_gcd_lcm` proc that can return both with work shared.
|
||||||
|
*/
|
||||||
|
int_gcd :: proc(res, a, b: ^Int) -> (err: Error) {
|
||||||
|
if err = clear_if_uninitialized(a, b, res); err != .None { return err; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
If either `a` or `b`, return the other one.
|
||||||
|
*/
|
||||||
|
if z, _ := is_zero(a); z { return abs(res, b); }
|
||||||
|
if z, _ := is_zero(b); z { return abs(res, a); }
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get copies of `a` and `b` we can modify.
|
||||||
|
*/
|
||||||
|
u, v := &Int{}, &Int{};
|
||||||
|
defer destroy(u, v);
|
||||||
|
if err = copy(u, a); err != .None { return err; }
|
||||||
|
if err = copy(v, b); err != .None { return err; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
Must be positive for the remainder of the algorithm.
|
||||||
|
*/
|
||||||
|
u.sign = .Zero_or_Positive; v.sign = .Zero_or_Positive;
|
||||||
|
|
||||||
|
/*
|
||||||
|
B1. Find the common power of two for `u` and `v`.
|
||||||
|
*/
|
||||||
|
u_lsb, _ := count_lsb(u);
|
||||||
|
v_lsb, _ := count_lsb(v);
|
||||||
|
k := min(u_lsb, v_lsb);
|
||||||
|
|
||||||
|
if k > 0 {
|
||||||
|
/*
|
||||||
|
Divide the power of two out.
|
||||||
|
*/
|
||||||
|
if err = shr(u, u, k); err != .None { return err; }
|
||||||
|
if err = shr(v, v, k); err != .None { return err; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Divide any remaining factors of two out.
|
||||||
|
*/
|
||||||
|
if u_lsb != k {
|
||||||
|
if err = shr(u, u, u_lsb - k); err != .None { return err; }
|
||||||
|
}
|
||||||
|
if v_lsb != k {
|
||||||
|
if err = shr(v, v, v_lsb - k); err != .None { return err; }
|
||||||
|
}
|
||||||
|
|
||||||
|
for v.used != 0 {
|
||||||
|
/*
|
||||||
|
Make sure `v` is the largest.
|
||||||
|
*/
|
||||||
|
if c, _ := cmp_mag(u, v); c == 1 {
|
||||||
|
/*
|
||||||
|
Swap `u` and `v` to make sure `v` is >= `u`.
|
||||||
|
*/
|
||||||
|
swap(u, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Subtract smallest from largest.
|
||||||
|
*/
|
||||||
|
if err = sub(v, v, u); err != .None { return err; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
Divide out all factors of two.
|
||||||
|
*/
|
||||||
|
b, _ := count_lsb(v);
|
||||||
|
if err = shr(v, v, b); err != .None { return err; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Multiply by 2**k which we divided out at the beginning.
|
||||||
|
*/
|
||||||
|
if err = shl(res, u, k); err != .None { return err; }
|
||||||
|
res.sign = .Zero_or_Positive;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
gcd :: proc { int_gcd, };
|
||||||
|
|
||||||
|
|
||||||
when size_of(rawptr) == 8 {
|
when size_of(rawptr) == 8 {
|
||||||
_factorial_table := [35]_WORD{
|
_factorial_table := [35]_WORD{
|
||||||
/* f(00): */ 1,
|
/* f(00): */ 1,
|
||||||
|
|||||||
+33
-35
@@ -504,48 +504,36 @@ count_bits :: proc(a: ^Int) -> (count: int, err: Error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Counts the number of LSBs which are zero before the first zero bit
|
Returns the number of trailing zeroes before the first one.
|
||||||
|
Differs from regular `ctz` in that 0 returns 0.
|
||||||
*/
|
*/
|
||||||
count_lsb :: proc(a: ^Int) -> (count: int, err: Error) {
|
int_count_lsb :: proc(a: ^Int) -> (count: int, err: Error) {
|
||||||
if err = clear_if_uninitialized(a); err != .None {
|
if err = clear_if_uninitialized(a); err != .None { return -1, err; }
|
||||||
return 0, err;
|
|
||||||
}
|
|
||||||
|
|
||||||
lnz := []u8{4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0};
|
|
||||||
|
|
||||||
q: DIGIT;
|
|
||||||
|
|
||||||
|
_ctz :: intrinsics.count_trailing_zeros;
|
||||||
/*
|
/*
|
||||||
Early out for zero.
|
Easy out.
|
||||||
*/
|
*/
|
||||||
if z, _ := is_zero(a); z {
|
if z, _ := is_zero(a); z { return 0, .None; }
|
||||||
return 0, .None;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Scan lower digits until non-zero.
|
Scan lower digits until non-zero.
|
||||||
*/
|
*/
|
||||||
for count = 0; (count < a.used && a.digit[count] == 0); count += 1 {}
|
x: int;
|
||||||
q = a.digit[count];
|
for x = 0; x < a.used && a.digit[x] == 0; x += 1 {}
|
||||||
count *= _DIGIT_BITS;
|
|
||||||
|
|
||||||
/*
|
q := a.digit[x];
|
||||||
Now scan this digit until a 1 is found.
|
x *= _DIGIT_BITS;
|
||||||
*/
|
return x + count_lsb(q), .None;
|
||||||
if q & 1 == 0 {
|
|
||||||
p: DIGIT;
|
|
||||||
for {
|
|
||||||
p = q & 15;
|
|
||||||
count += int(lnz[p]);
|
|
||||||
q >>= 4;
|
|
||||||
if p != 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count, .None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
platform_count_lsb :: #force_inline proc(a: $T) -> (count: int)
|
||||||
|
where intrinsics.type_is_integer(T) && intrinsics.type_is_unsigned(T) {
|
||||||
|
return int(intrinsics.count_trailing_zeros(a)) if a > 0 else 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
count_lsb :: proc { int_count_lsb, platform_count_lsb, };
|
||||||
|
|
||||||
int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) {
|
int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) {
|
||||||
when _DIGIT_BITS == 60 { // DIGIT = u64
|
when _DIGIT_BITS == 60 { // DIGIT = u64
|
||||||
return DIGIT(rnd.uint64(r)) & _MASK;
|
return DIGIT(rnd.uint64(r)) & _MASK;
|
||||||
@@ -602,14 +590,24 @@ _zero_unused :: proc(a: ^Int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clear_if_uninitialized :: proc(dest: ^Int, minimize := false) -> (err: Error) {
|
clear_if_uninitialized_single :: proc(arg: ^Int) -> (err: Error) {
|
||||||
if !is_initialized(dest) {
|
if !is_initialized(arg) {
|
||||||
return grow(dest, _MIN_DIGIT_COUNT if minimize else _DEFAULT_DIGIT_COUNT);
|
return grow(arg, _DEFAULT_DIGIT_COUNT);
|
||||||
}
|
}
|
||||||
return .None;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clear_if_uninitialized_multi :: proc(args: ..^Int) -> (err: Error) {
|
||||||
|
for i in args {
|
||||||
|
if i != nil && !is_initialized(i) {
|
||||||
|
e := grow(i, _DEFAULT_DIGIT_COUNT);
|
||||||
|
if e != .None { err = e; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear_if_uninitialized :: proc {clear_if_uninitialized_single, clear_if_uninitialized_multi, };
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Allocates several `Int`s at once.
|
Allocates several `Int`s at once.
|
||||||
|
|||||||
@@ -475,6 +475,9 @@ if __name__ == '__main__':
|
|||||||
TIMINGS = {}
|
TIMINGS = {}
|
||||||
|
|
||||||
UNTIL_ITERS = ITERATIONS
|
UNTIL_ITERS = ITERATIONS
|
||||||
|
if test_proc == test_root_n and BITS == 1_200:
|
||||||
|
UNTIL_ITERS /= 10
|
||||||
|
|
||||||
UNTIL_TIME = TOTAL_TIME + BITS / TIMED_BITS_PER_SECOND
|
UNTIL_TIME = TOTAL_TIME + BITS / TIMED_BITS_PER_SECOND
|
||||||
# We run each test for a second per 20k bits
|
# We run each test for a second per 20k bits
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user