mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-05 07:08:48 +00:00
big: Add lcm and its test.
This commit is contained in:
@@ -1353,6 +1353,49 @@ int_gcd :: proc(res, a, b: ^Int) -> (err: Error) {
|
|||||||
gcd :: proc { int_gcd, };
|
gcd :: proc { int_gcd, };
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Least Common Multiple.
|
||||||
|
Computes least common multiple as `|a*b|/(a, b)`
|
||||||
|
|
||||||
|
TODO(Jeroen):
|
||||||
|
- Maybe combine with GCD and have an `_int_gcd_lcm` proc that can return both with work shared.
|
||||||
|
*/
|
||||||
|
int_lcm :: proc(res, a, b: ^Int) -> (err: Error) {
|
||||||
|
if err = clear_if_uninitialized(a, b, res); err != .None { return err; }
|
||||||
|
|
||||||
|
t1, t2 := &Int{}, &Int{};
|
||||||
|
defer destroy(t1, t2);
|
||||||
|
|
||||||
|
/*
|
||||||
|
t1 = get the GCD of the two inputs.
|
||||||
|
*/
|
||||||
|
if err = gcd(t1, a, b); err != .None { return err; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
Divide the smallest by the GCD.
|
||||||
|
*/
|
||||||
|
if c, _ := cmp_mag(a, b); c == -1 {
|
||||||
|
/*
|
||||||
|
Store quotient in `t2` such that `t2 * b` is the LCM.
|
||||||
|
*/
|
||||||
|
if err = div(t2, a, t1); err != .None { return err; }
|
||||||
|
err = mul(res, t2, b);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
Store quotient in `t2` such that `t2 * a` is the LCM.
|
||||||
|
*/
|
||||||
|
if err = div(t2, a, t1); err != .None { return err; }
|
||||||
|
err = mul(res, t2, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Fix the sign to positive and return.
|
||||||
|
*/
|
||||||
|
res.sign = .Zero_or_Positive;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
lcm :: proc { int_lcm, };
|
||||||
|
|
||||||
when size_of(rawptr) == 8 {
|
when size_of(rawptr) == 8 {
|
||||||
_factorial_table := [35]_WORD{
|
_factorial_table := [35]_WORD{
|
||||||
/* f(00): */ 1,
|
/* f(00): */ 1,
|
||||||
|
|||||||
+12
-12
@@ -110,19 +110,20 @@ print :: proc(name: string, a: ^Int, base := i8(10), print_extra_info := false,
|
|||||||
|
|
||||||
demo :: proc() {
|
demo :: proc() {
|
||||||
|
|
||||||
// err: Error;
|
err: Error;
|
||||||
// a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{};
|
a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{};
|
||||||
// defer destroy(a, b, c, d, e, f);
|
defer destroy(a, b, c, d, e, f);
|
||||||
|
|
||||||
// set(a, 25);
|
set(a, 25);
|
||||||
// set(b, 15);
|
set(b, 15);
|
||||||
|
|
||||||
|
err = gcd(c, a, b);
|
||||||
|
fmt.printf("gcd(");
|
||||||
|
print("a =", a, 10, false, true, false);
|
||||||
|
print(", b =", b, 10, false, true, false);
|
||||||
|
print(") =", c, 10, false, true, false);
|
||||||
|
fmt.printf(" (err = %v)\n", err);
|
||||||
|
|
||||||
// err = gcd(c, a, b);
|
|
||||||
// fmt.printf("gcd(");
|
|
||||||
// print("a =", a, 10, false, true, false);
|
|
||||||
// print(", b =", b, 10, false, true, false);
|
|
||||||
// print(") =", c, 10, false, true, false);
|
|
||||||
// fmt.printf(" (err = %v)\n", err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
@@ -134,7 +135,6 @@ main :: proc() {
|
|||||||
demo();
|
demo();
|
||||||
print_timings();
|
print_timings();
|
||||||
|
|
||||||
|
|
||||||
if len(ta.allocation_map) > 0 {
|
if len(ta.allocation_map) > 0 {
|
||||||
for _, v in ta.allocation_map {
|
for _, v in ta.allocation_map {
|
||||||
fmt.printf("Leaked %v bytes @ %v\n", v.size, v.location);
|
fmt.printf("Leaked %v bytes @ %v\n", v.size, v.location);
|
||||||
|
|||||||
@@ -314,3 +314,23 @@ PyRes :: struct {
|
|||||||
return PyRes{res = r, err = .None};
|
return PyRes{res = r, err = .None};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
dest = lcm(a, b)
|
||||||
|
*/
|
||||||
|
@export test_lcm :: proc "c" (a, b: cstring) -> (res: PyRes) {
|
||||||
|
context = runtime.default_context();
|
||||||
|
err: Error;
|
||||||
|
|
||||||
|
ai, bi, dest := &Int{}, &Int{}, &Int{};
|
||||||
|
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 = lcm(dest, ai, bi); err != .None { return PyRes{res=":lcm:lcm(a, b):", err=err}; }
|
||||||
|
|
||||||
|
r: cstring;
|
||||||
|
r, err = int_itoa_cstring(dest, 16, context.temp_allocator);
|
||||||
|
if err != .None { return PyRes{res=":lcm:itoa(res):", err=err}; }
|
||||||
|
return PyRes{res = r, err = .None};
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-2
@@ -136,7 +136,7 @@ int_shr_signed = load(l.test_shr_signed, [c_char_p, c_longlong], Res)
|
|||||||
|
|
||||||
int_factorial = load(l.test_factorial, [c_uint64], Res)
|
int_factorial = load(l.test_factorial, [c_uint64], Res)
|
||||||
int_gcd = load(l.test_gcd, [c_char_p, c_char_p], Res)
|
int_gcd = load(l.test_gcd, [c_char_p, c_char_p], Res)
|
||||||
|
int_lcm = load(l.test_lcm, [c_char_p, c_char_p], Res)
|
||||||
|
|
||||||
def test(test_name: "", res: Res, param=[], expected_error = Error.Okay, expected_result = "", radix=16):
|
def test(test_name: "", res: Res, param=[], expected_error = Error.Okay, expected_result = "", radix=16):
|
||||||
passed = True
|
passed = True
|
||||||
@@ -343,6 +343,14 @@ def test_gcd(a = 0, b = 0, expected_error = Error.Okay):
|
|||||||
|
|
||||||
return test("test_gcd", res, [a, b], expected_error, expected_result)
|
return test("test_gcd", res, [a, b], expected_error, expected_result)
|
||||||
|
|
||||||
|
def test_lcm(a = 0, b = 0, expected_error = Error.Okay):
|
||||||
|
args = [arg_to_odin(a), arg_to_odin(b)]
|
||||||
|
res = int_lcm(*args)
|
||||||
|
expected_result = None
|
||||||
|
if expected_error == Error.Okay:
|
||||||
|
expected_result = math.lcm(a, b)
|
||||||
|
|
||||||
|
return test("test_lcm", res, [a, b], expected_error, expected_result)
|
||||||
|
|
||||||
# TODO(Jeroen): Make sure tests cover edge cases, fast paths, and so on.
|
# TODO(Jeroen): Make sure tests cover edge cases, fast paths, and so on.
|
||||||
#
|
#
|
||||||
@@ -425,6 +433,10 @@ TESTS = {
|
|||||||
[ 123, 25, ],
|
[ 123, 25, ],
|
||||||
[ 125, 25, ],
|
[ 125, 25, ],
|
||||||
],
|
],
|
||||||
|
test_lcm: [
|
||||||
|
[ 123, 25, ],
|
||||||
|
[ 125, 25, ],
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
total_passes = 0
|
total_passes = 0
|
||||||
@@ -437,7 +449,7 @@ RANDOM_TESTS = [
|
|||||||
test_add, test_sub, test_mul, test_div,
|
test_add, test_sub, test_mul, test_div,
|
||||||
test_log, test_pow, test_sqrt, test_root_n,
|
test_log, test_pow, test_sqrt, test_root_n,
|
||||||
test_shl_digit, test_shr_digit, test_shl, test_shr_signed,
|
test_shl_digit, test_shr_digit, test_shl, test_shr_signed,
|
||||||
test_gcd,
|
test_gcd, test_lcm,
|
||||||
]
|
]
|
||||||
SKIP_LARGE = [
|
SKIP_LARGE = [
|
||||||
test_pow, test_root_n, # test_gcd,
|
test_pow, test_root_n, # test_gcd,
|
||||||
|
|||||||
Reference in New Issue
Block a user