From 0605a9f239857d90b605601cab77b1dcc68ca037 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 7 Sep 2021 13:17:47 +0200 Subject: [PATCH 1/6] big: Run tests under CI. --- .github/workflows/ci.yml | 8 +++++++- core/math/big/tests/build.bat | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9a27acc7c..71645d824 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,5 +64,11 @@ jobs: call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat odin run examples/demo/demo.odin timeout-minutes: 10 - + - name: core:math/big tests + shell: cmd + run: | + call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat + cd core\math\big\tests + call build.bat + timeout-minutes: 10 diff --git a/core/math/big/tests/build.bat b/core/math/big/tests/build.bat index 6a82106db..bebd40aac 100644 --- a/core/math/big/tests/build.bat +++ b/core/math/big/tests/build.bat @@ -4,5 +4,5 @@ set TEST_ARGS= set OUT_NAME=test_library set COMMON=-build-mode:shared -show-timings -no-bounds-check -define:MATH_BIG_EXE=false -vet -strict-style :odin build . %COMMON% -o:minimal -out:%OUT_NAME% && python test.py %TEST_ARGS% -odin build . %COMMON% -o:size -out:%OUT_NAME% && python test.py %TEST_ARGS% -:odin build . %COMMON% -o:speed -out:%OUT_NAME% && python test.py %TEST_ARGS% \ No newline at end of file +:odin build . %COMMON% -o:size -out:%OUT_NAME% && python test.py %TEST_ARGS% +odin build . %COMMON% -o:speed -out:%OUT_NAME% && python test.py %TEST_ARGS% \ No newline at end of file From 86cfb2ea828c6904a54938281cb6fde659b73f39 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 7 Sep 2021 13:22:44 +0200 Subject: [PATCH 2/6] big CI: Set relative path to Odin. --- core/math/big/tests/build.bat | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/math/big/tests/build.bat b/core/math/big/tests/build.bat index bebd40aac..c2a5ada18 100644 --- a/core/math/big/tests/build.bat +++ b/core/math/big/tests/build.bat @@ -3,6 +3,7 @@ set TEST_ARGS=-fast-tests set TEST_ARGS= set OUT_NAME=test_library set COMMON=-build-mode:shared -show-timings -no-bounds-check -define:MATH_BIG_EXE=false -vet -strict-style -:odin build . %COMMON% -o:minimal -out:%OUT_NAME% && python test.py %TEST_ARGS% -:odin build . %COMMON% -o:size -out:%OUT_NAME% && python test.py %TEST_ARGS% -odin build . %COMMON% -o:speed -out:%OUT_NAME% && python test.py %TEST_ARGS% \ No newline at end of file +set PATH_TO_ODIN==..\..\..\..\odin +%PATH_TO_ODIN% build . %COMMON% -o:minimal -out:%OUT_NAME% && python test.py %TEST_ARGS% +%PATH_TO_ODIN% build . %COMMON% -o:size -out:%OUT_NAME% && python test.py %TEST_ARGS% +%PATH_TO_ODIN% build . %COMMON% -o:speed -out:%OUT_NAME% && python test.py %TEST_ARGS% \ No newline at end of file From c4ec459d289efd85071e64aea01fe81c258c0a4f Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 7 Sep 2021 13:26:34 +0200 Subject: [PATCH 3/6] bit: Force Python3 for CI. --- core/math/big/tests/build.bat | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/math/big/tests/build.bat b/core/math/big/tests/build.bat index c2a5ada18..3fd9f26f8 100644 --- a/core/math/big/tests/build.bat +++ b/core/math/big/tests/build.bat @@ -4,6 +4,6 @@ set TEST_ARGS= set OUT_NAME=test_library set COMMON=-build-mode:shared -show-timings -no-bounds-check -define:MATH_BIG_EXE=false -vet -strict-style set PATH_TO_ODIN==..\..\..\..\odin -%PATH_TO_ODIN% build . %COMMON% -o:minimal -out:%OUT_NAME% && python test.py %TEST_ARGS% -%PATH_TO_ODIN% build . %COMMON% -o:size -out:%OUT_NAME% && python test.py %TEST_ARGS% -%PATH_TO_ODIN% build . %COMMON% -o:speed -out:%OUT_NAME% && python test.py %TEST_ARGS% \ No newline at end of file +%PATH_TO_ODIN% build . %COMMON% -o:minimal -out:%OUT_NAME% && python3 test.py %TEST_ARGS% +%PATH_TO_ODIN% build . %COMMON% -o:size -out:%OUT_NAME% && python3 test.py %TEST_ARGS% +%PATH_TO_ODIN% build . %COMMON% -o:speed -out:%OUT_NAME% && python3 test.py %TEST_ARGS% \ No newline at end of file From 4db56830113197a920c7814aa679458badde2a29 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 7 Sep 2021 13:31:12 +0200 Subject: [PATCH 4/6] big: CI print Python3 version. --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 71645d824..896877b4b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,6 +69,7 @@ jobs: run: | call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat cd core\math\big\tests + python3 --version call build.bat timeout-minutes: 10 From abb15ddb380b187007bd26b5099323d2c4478a38 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 7 Sep 2021 13:55:37 +0200 Subject: [PATCH 5/6] big: Implement isqrt in Python for the tests. --- core/math/big/tests/build.bat | 4 ++-- core/math/big/tests/test.py | 24 ++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/core/math/big/tests/build.bat b/core/math/big/tests/build.bat index 3fd9f26f8..a9c0c2e6e 100644 --- a/core/math/big/tests/build.bat +++ b/core/math/big/tests/build.bat @@ -4,6 +4,6 @@ set TEST_ARGS= set OUT_NAME=test_library set COMMON=-build-mode:shared -show-timings -no-bounds-check -define:MATH_BIG_EXE=false -vet -strict-style set PATH_TO_ODIN==..\..\..\..\odin -%PATH_TO_ODIN% build . %COMMON% -o:minimal -out:%OUT_NAME% && python3 test.py %TEST_ARGS% -%PATH_TO_ODIN% build . %COMMON% -o:size -out:%OUT_NAME% && python3 test.py %TEST_ARGS% +:%PATH_TO_ODIN% build . %COMMON% -o:minimal -out:%OUT_NAME% && python3 test.py %TEST_ARGS% +:%PATH_TO_ODIN% build . %COMMON% -o:size -out:%OUT_NAME% && python3 test.py %TEST_ARGS% %PATH_TO_ODIN% build . %COMMON% -o:speed -out:%OUT_NAME% && python3 test.py %TEST_ARGS% \ No newline at end of file diff --git a/core/math/big/tests/test.py b/core/math/big/tests/test.py index 0c346a9bf..79580b620 100644 --- a/core/math/big/tests/test.py +++ b/core/math/big/tests/test.py @@ -235,6 +235,26 @@ def arg_to_odin(a): s = '-' + hex(a)[3:] return s.encode('utf-8') + +def integer_sqrt(src): + # The Python version on Github's CI doesn't offer math.isqrt. + # We implement our own + count = src.bit_length() + a, b = count >> 1, count & 1 + + x = 1 << (a + b) + + while True: + # y = (x + n // x) // 2 + t1 = src // x + t2 = t1 + x + y = t2 >> 1 + + if y >= x: + return x + + x, y = y, x + def test_add(a = 0, b = 0, expected_error = Error.Okay): args = [arg_to_odin(a), arg_to_odin(b)] res = add(*args) @@ -338,7 +358,7 @@ def test_sqrt(number = 0, expected_error = Error.Okay): if number < 0: expected_result = 0 else: - expected_result = int(math.isqrt(number)) + expected_result = integer_sqrt(number) return test("test_sqrt", res, [number], expected_error, expected_result) def root_n(number, root): @@ -450,7 +470,7 @@ def test_is_square(a = 0, b = 0, expected_error = Error.Okay): res = is_square(*args) expected_result = None if expected_error == Error.Okay: - expected_result = str(math.isqrt(a) ** 2 == a) if a > 0 else "False" + expected_result = str(integer_sqrt(a) ** 2 == a) if a > 0 else "False" return test("test_is_square", res, [a], expected_error, expected_result) From fbebf4bc4e042986ff834587be04eb43c240302f Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 7 Sep 2021 14:17:14 +0200 Subject: [PATCH 6/6] big: Add Python implementation of LCM. --- core/math/big/private.odin | 4 +-- core/math/big/tests/test.py | 59 ++++++++++++++++++++++++++----------- 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/core/math/big/private.odin b/core/math/big/private.odin index b3d4d80e5..14a27f600 100644 --- a/core/math/big/private.odin +++ b/core/math/big/private.odin @@ -1642,8 +1642,8 @@ _private_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int, allocator := context. /* Store quotient in `t2` such that `t2 * a` is the LCM. */ - internal_div(res_lcm, a, temp_gcd_res) or_return - err = internal_mul(res_lcm, res_lcm, b) + internal_div(res_lcm, b, temp_gcd_res) or_return + err = internal_mul(res_lcm, res_lcm, a) } if res_gcd != nil { diff --git a/core/math/big/tests/test.py b/core/math/big/tests/test.py index 79580b620..44659a638 100644 --- a/core/math/big/tests/test.py +++ b/core/math/big/tests/test.py @@ -236,24 +236,40 @@ def arg_to_odin(a): return s.encode('utf-8') -def integer_sqrt(src): - # The Python version on Github's CI doesn't offer math.isqrt. - # We implement our own - count = src.bit_length() - a, b = count >> 1, count & 1 +def big_integer_sqrt(src): + # The Python version on Github's CI doesn't offer math.isqrt. + # We implement our own + count = src.bit_length() + a, b = count >> 1, count & 1 - x = 1 << (a + b) + x = 1 << (a + b) - while True: - # y = (x + n // x) // 2 - t1 = src // x - t2 = t1 + x - y = t2 >> 1 + while True: + # y = (x + n // x) // 2 + t1 = src // x + t2 = t1 + x + y = t2 >> 1 - if y >= x: - return x + if y >= x: + return x - x, y = y, x + x, y = y, x + +def big_integer_lcm(a, b): + # Computes least common multiple as `|a*b|/gcd(a,b)` + # Divide the smallest by the GCD. + + if a == 0 or b == 0: + return 0 + + if abs(a) < abs(b): + # Store quotient in `t2` such that `t2 * b` is the LCM. + lcm = a // math.gcd(a, b) + return abs(b * lcm) + else: + # Store quotient in `t2` such that `t2 * a` is the LCM. + lcm = b // math.gcd(a, b) + return abs(a * lcm) def test_add(a = 0, b = 0, expected_error = Error.Okay): args = [arg_to_odin(a), arg_to_odin(b)] @@ -358,7 +374,7 @@ def test_sqrt(number = 0, expected_error = Error.Okay): if number < 0: expected_result = 0 else: - expected_result = integer_sqrt(number) + expected_result = big_integer_sqrt(number) return test("test_sqrt", res, [number], expected_error, expected_result) def root_n(number, root): @@ -461,7 +477,7 @@ def test_lcm(a = 0, b = 0, expected_error = Error.Okay): res = int_lcm(*args) expected_result = None if expected_error == Error.Okay: - expected_result = math.lcm(a, b) + expected_result = big_integer_lcm(a, b) return test("test_lcm", res, [a, b], expected_error, expected_result) @@ -470,7 +486,7 @@ def test_is_square(a = 0, b = 0, expected_error = Error.Okay): res = is_square(*args) expected_result = None if expected_error == Error.Okay: - expected_result = str(integer_sqrt(a) ** 2 == a) if a > 0 else "False" + expected_result = str(big_integer_sqrt(a) ** 2 == a) if a > 0 else "False" return test("test_is_square", res, [a], expected_error, expected_result) @@ -703,6 +719,15 @@ if __name__ == '__main__': b = randint(0, min(BITS, 120)) elif test_proc == test_is_square: a = randint(0, 1 << BITS) + elif test_proc == test_lcm: + smallest = min(a, b) + biggest = max(a, b) + + # Randomly swap biggest and smallest + if randint(1, 11) % 2 == 0: + smallest, biggest = biggest, smallest + + a, b = smallest, biggest else: b = randint(0, 1 << BITS)