2
0
Эх сурвалжийг харах

Turn `core:math/bìg` tests into regular `core:testing` tests.

`core:math/big` has been verified against Python's big integer implementation long enough.
Turn it into a regular regression test using the `core:testing` framework, testing against
a generated corpus of test vectors.
Jeroen van Rijn 2 сар өмнө
parent
commit
9dafd77bc0

+ 0 - 6
.github/workflows/ci.yml

@@ -257,12 +257,6 @@ jobs:
           call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
           cd tests\documentation
           call build.bat
-      - name: core:math/big tests
-        shell: cmd
-        run: |
-          call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
-          cd tests\core\math\big
-          call build.bat
       - name: Odin check examples/all for Windows 32bits
         shell: cmd
         run: |

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

@@ -1660,13 +1660,13 @@ internal_int_sqrt :: proc(dest, src: ^Int, allocator := context.allocator) -> (e
 
 		if internal_gte(y, x) {
 			internal_swap(dest, x)
-			return nil
+			return internal_clamp(dest)
 		}
 		internal_swap(x, y)
 	}
 
 	internal_swap(dest, x)
-	return err
+	return internal_clamp(dest)
 }
 internal_sqrt :: proc { internal_int_sqrt, }
 

+ 1 - 1
core/math/big/radix.odin

@@ -310,7 +310,7 @@ int_atoi :: proc(res: ^Int, input: string, radix := i8(10), allocator := context
 		res.sign = sign
 	}
 
-	return nil
+	return internal_clamp(res)
 }
 
 

+ 0 - 16
tests/core/math/big/build.bat

@@ -1,16 +0,0 @@
-@echo off
-rem math/big tests
-set PATH_TO_ODIN==..\..\..\..\odin
-set TEST_ARGS=-fast-tests
-set TEST_ARGS=-no-random
-set TEST_ARGS=
-set OUT_NAME=math_big_test_library.dll
-set COMMON=-build-mode:shared -show-timings -no-bounds-check -define:MATH_BIG_EXE=false -vet -strict-style
-echo ---
-echo Running core:math/big tests
-echo ---
-
-%PATH_TO_ODIN% build . %COMMON% -o:speed -out:%OUT_NAME%
-python3 test.py %TEST_ARGS%
-
-%PATH_TO_ODIN% test test_core_math_big.odin -file

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 522
tests/core/math/big/generate_tests.py


+ 0 - 362
tests/core/math/big/test.odin

@@ -1,362 +0,0 @@
-/*
-	Copyright 2021 Jeroen van Rijn <[email protected]>.
-	Made available under Odin's BSD-3 license.
-
-	An arbitrary precision mathematics implementation in Odin.
-	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.
-
-	This file exports procedures for use with the test.py test suite.
-*/
-package test_core_math_big
-
-/*
-	TODO: Write tests for `internal_*` and test reusing parameters with the public implementations.
-*/
-
-import "base:runtime"
-import "core:strings"
-import "core:math/big"
-
-PyRes :: struct {
-	res: cstring,
-	err: big.Error,
-}
-
-print_to_buffer :: proc(val: ^big.Int) -> cstring {
-	context = runtime.default_context()
-	r, _ := big.int_itoa_cstring(val, 16, context.allocator)
-	return r
-}
-
-@export test_initialize_constants :: proc "c" () -> (res: u64) {
-	context = runtime.default_context()
-	_  = big.initialize_constants()
-
-	return u64(big._DIGIT_NAILS)
-}
-
-@export test_error_string :: proc "c" (err: big.Error) -> (res: cstring) {
-	context = runtime.default_context()
-	es := big.Error_String
-	return strings.clone_to_cstring(es[err], context.allocator)
-}
-
-@export test_add :: proc "c" (a, b: cstring) -> (res: PyRes) {
-	context = runtime.default_context()
-	err: big.Error
-
-	aa, bb, sum := &big.Int{}, &big.Int{}, &big.Int{}
-	defer big.internal_destroy(aa, bb, sum)
-
-	if err = big.atoi(aa, string(a), 16); err != nil { return PyRes{res=":add:atoi(a):", err=err} }
-	if err = big.atoi(bb, string(b), 16); err != nil { return PyRes{res=":add:atoi(b):", err=err} }
-	if bb.used == 1 {
-		if err = #force_inline big.internal_add(sum, aa, bb.digit[0]); err != nil { return PyRes{res=":add:add(sum,a,b):", err=err} }	
-	} else {
-		if err = #force_inline big.internal_add(sum, aa, bb); err != nil { return PyRes{res=":add:add(sum,a,b):", err=err} }
-	}
-
-	r := print_to_buffer(sum)
-
-	return PyRes{res = r, err = nil}
-}
-
-@export test_sub :: proc "c" (a, b: cstring) -> (res: PyRes) {
-	context = runtime.default_context()
-	err: big.Error
-
-	aa, bb, sum := &big.Int{}, &big.Int{}, &big.Int{}
-	defer big.internal_destroy(aa, bb, sum)
-
-	if err = big.atoi(aa, string(a), 16); err != nil { return PyRes{res=":sub:atoi(a):", err=err} }
-	if err = big.atoi(bb, string(b), 16); err != nil { return PyRes{res=":sub:atoi(b):", err=err} }
-	if bb.used == 1 {
-		if err = #force_inline big.internal_sub(sum, aa, bb.digit[0]); err != nil { return PyRes{res=":sub:sub(sum,a,b):", err=err} }
-	} else {
-		if err = #force_inline big.internal_sub(sum, aa, bb); err != nil { return PyRes{res=":sub:sub(sum,a,b):", err=err} }
-	}
-
-	r := print_to_buffer(sum)
-	if err != nil { return PyRes{res=":sub:itoa(sum):", err=err} }
-	return PyRes{res = r, err = nil}
-}
-
-@export test_mul :: proc "c" (a, b: cstring) -> (res: PyRes) {
-	context = runtime.default_context()
-	err: big.Error
-
-	aa, bb, product := &big.Int{}, &big.Int{}, &big.Int{}
-	defer big.internal_destroy(aa, bb, product)
-
-	if err = big.atoi(aa, string(a), 16); err != nil { return PyRes{res=":mul:atoi(a):", err=err} }
-	if err = big.atoi(bb, string(b), 16); err != nil { return PyRes{res=":mul:atoi(b):", err=err} }
-	if err = #force_inline big.internal_mul(product, aa, bb); err != nil { return PyRes{res=":mul:mul(product,a,b):", err=err} }
-
-	r := print_to_buffer(product)
-	return PyRes{res = r, err = nil}
-}
-
-@export test_sqr :: proc "c" (a: cstring) -> (res: PyRes) {
-	context = runtime.default_context()
-	err: big.Error
-
-	aa, square := &big.Int{}, &big.Int{}
-	defer big.internal_destroy(aa, square)
-
-	if err = big.atoi(aa, string(a), 16); err != nil { return PyRes{res=":sqr:atoi(a):", err=err} }
-	if err = #force_inline big.internal_sqr(square, aa); err != nil { return PyRes{res=":sqr:sqr(square,a):", err=err} }
-
-	r := print_to_buffer(square)
-	return PyRes{res = r, err = nil}
-}
-
-/*
-	NOTE(Jeroen): For simplicity, we don't return the quotient and the remainder, just the quotient.
-*/
-@export test_div :: proc "c" (a, b: cstring) -> (res: PyRes) {
-	context = runtime.default_context()
-	err: big.Error
-
-	aa, bb, quotient := &big.Int{}, &big.Int{}, &big.Int{}
-	defer big.internal_destroy(aa, bb, quotient)
-
-	if err = big.atoi(aa, string(a), 16); err != nil { return PyRes{res=":div:atoi(a):", err=err} }
-	if err = big.atoi(bb, string(b), 16); err != nil { return PyRes{res=":div:atoi(b):", err=err} }
-	if err = #force_inline big.internal_div(quotient, aa, bb); err != nil { return PyRes{res=":div:div(quotient,a,b):", err=err} }
-
-	r := print_to_buffer(quotient)
-	return PyRes{res = r, err = nil}
-}
-
-/*
-	res = log(a, base)
-*/
-@export test_log :: proc "c" (a: cstring, base := big.DIGIT(2)) -> (res: PyRes) {
-	context = runtime.default_context()
-	err: big.Error
-	l: int
-
-	aa := &big.Int{}
-	defer big.internal_destroy(aa)
-
-	if err = big.atoi(aa, string(a), 16); err != nil { return PyRes{res=":log:atoi(a):", err=err} }
-	if l, err = #force_inline big.internal_log(aa, base); err != nil { return PyRes{res=":log:log(a, base):", err=err} }
-
-	#force_inline big.internal_zero(aa)
-	aa.digit[0] = big.DIGIT(l)  & big._MASK
-	aa.digit[1] = big.DIGIT(l) >> big._DIGIT_BITS
-	aa.used = 2
-	big.clamp(aa)
-
-	r := print_to_buffer(aa)
-	return PyRes{res = r, err = nil}
-}
-
-/*
-	dest = base^power
-*/
-@export test_pow :: proc "c" (base: cstring, power := int(2)) -> (res: PyRes) {
-	context = runtime.default_context()
-	err: big.Error
-
-	dest, bb := &big.Int{}, &big.Int{}
-	defer big.internal_destroy(dest, bb)
-
-	if err = big.atoi(bb, string(base), 16); err != nil { return PyRes{res=":pow:atoi(base):", err=err} }
-	if err = #force_inline big.internal_pow(dest, bb, power); err != nil { return PyRes{res=":pow:pow(dest, base, power):", err=err} }
-
-	r := print_to_buffer(dest)
-	return PyRes{res = r, err = nil}
-}
-
-/*
-	dest = sqrt(src)
-*/
-@export test_sqrt :: proc "c" (source: cstring) -> (res: PyRes) {
-	context = runtime.default_context()
-	err: big.Error
-
-	src := &big.Int{}
-	defer big.internal_destroy(src)
-
-	if err = big.atoi(src, string(source), 16); err != nil { return PyRes{res=":sqrt:atoi(src):", err=err} }
-	if err = #force_inline big.internal_sqrt(src, src); err != nil { return PyRes{res=":sqrt:sqrt(src):", err=err} }
-
-	r := print_to_buffer(src)
-	return PyRes{res = r, err = nil}
-}
-
-/*
-	dest = root_n(src, power)
-*/
-@export test_root_n :: proc "c" (source: cstring, power: int) -> (res: PyRes) {
-	context = runtime.default_context()
-	err: big.Error
-
-	src := &big.Int{}
-	defer big.internal_destroy(src)
-
-	if err = big.atoi(src, string(source), 16); err != nil { return PyRes{res=":root_n:atoi(src):", err=err} }
-	if err = #force_inline big.internal_root_n(src, src, power); err != nil { return PyRes{res=":root_n:root_n(src):", err=err} }
-
-	r := print_to_buffer(src)
-	return PyRes{res = r, err = nil}
-}
-
-/*
-	dest = shr_digit(src, digits)
-*/
-@export test_shr_leg :: proc "c" (source: cstring, digits: int) -> (res: PyRes) {
-	context = runtime.default_context()
-	err: big.Error
-
-	src := &big.Int{}
-	defer big.internal_destroy(src)
-
-	if err = big.atoi(src, string(source), 16); err != nil { return PyRes{res=":shr_digit:atoi(src):", err=err} }
-	if err = #force_inline big._private_int_shr_leg(src, digits); err != nil { return PyRes{res=":shr_digit:shr_digit(src):", err=err} }
-
-	r := print_to_buffer(src)
-	return PyRes{res = r, err = nil}
-}
-
-/*
-	dest = shl_digit(src, digits)
-*/
-@export test_shl_leg :: proc "c" (source: cstring, digits: int) -> (res: PyRes) {
-	context = runtime.default_context()
-	err: big.Error
-
-	src := &big.Int{}
-	defer big.internal_destroy(src)
-
-	if err = big.atoi(src, string(source), 16); err != nil { return PyRes{res=":shl_digit:atoi(src):", err=err} }
-	if err = #force_inline big._private_int_shl_leg(src, digits); err != nil { return PyRes{res=":shl_digit:shr_digit(src):", err=err} }
-
-	r := print_to_buffer(src)
-	return PyRes{res = r, err = nil}
-}
-
-/*
-	dest = shr(src, bits)
-*/
-@export test_shr :: proc "c" (source: cstring, bits: int) -> (res: PyRes) {
-	context = runtime.default_context()
-	err: big.Error
-
-	src := &big.Int{}
-	defer big.internal_destroy(src)
-
-	if err = big.atoi(src, string(source), 16); err != nil { return PyRes{res=":shr:atoi(src):", err=err} }
-	if err = #force_inline big.internal_shr(src, src, bits); err != nil { return PyRes{res=":shr:shr(src, bits):", err=err} }
-
-	r := print_to_buffer(src)
-	return PyRes{res = r, err = nil}
-}
-
-/*
-	dest = shr_signed(src, bits)
-*/
-@export test_shr_signed :: proc "c" (source: cstring, bits: int) -> (res: PyRes) {
-	context = runtime.default_context()
-	err: big.Error
-
-	src := &big.Int{}
-	defer big.internal_destroy(src)
-
-	if err = big.atoi(src, string(source), 16); err != nil { return PyRes{res=":shr_signed:atoi(src):", err=err} }
-	if err = #force_inline big.internal_shr_signed(src, src, bits); err != nil { return PyRes{res=":shr_signed:shr_signed(src, bits):", err=err} }
-
-	r := print_to_buffer(src)
-	return PyRes{res = r, err = nil}
-}
-
-/*
-	dest = shl(src, bits)
-*/
-@export test_shl :: proc "c" (source: cstring, bits: int) -> (res: PyRes) {
-	context = runtime.default_context()
-	err: big.Error
-
-	src := &big.Int{}
-	defer big.internal_destroy(src)
-
-	if err = big.atoi(src, string(source), 16); err != nil { return PyRes{res=":shl:atoi(src):", err=err} }
-	if err = #force_inline big.internal_shl(src, src, bits); err != nil { return PyRes{res=":shl:shl(src, bits):", err=err} }
-
-	r := print_to_buffer(src)
-	return PyRes{res = r, err = nil}
-}
-
-/*
-	dest = factorial(n)
-*/
-@export test_factorial :: proc "c" (n: int) -> (res: PyRes) {
-	context = runtime.default_context()
-	err: big.Error
-
-	dest := &big.Int{}
-	defer big.internal_destroy(dest)
-
-	if err = #force_inline big.internal_int_factorial(dest, n); err != nil { return PyRes{res=":factorial:factorial(n):", err=err} }
-
-	r := print_to_buffer(dest)
-	return PyRes{res = r, err = nil}
-}
-
-/*
-	dest = gcd(a, b)
-*/
-@export test_gcd :: proc "c" (a, b: cstring) -> (res: PyRes) {
-	context = runtime.default_context()
-	err: big.Error
-
-	ai, bi, dest := &big.Int{}, &big.Int{}, &big.Int{}
-	defer big.internal_destroy(ai, bi, dest)
-
-	if err = big.atoi(ai, string(a), 16); err != nil { return PyRes{res=":gcd:atoi(a):", err=err} }
-	if err = big.atoi(bi, string(b), 16); err != nil { return PyRes{res=":gcd:atoi(b):", err=err} }
-	if err = #force_inline big.internal_int_gcd_lcm(dest, nil, ai, bi); err != nil { return PyRes{res=":gcd:gcd(a, b):", err=err} }
-
-	r := print_to_buffer(dest)
-	return PyRes{res = r, err = nil}
-}
-
-/*
-	dest = lcm(a, b)
-*/
-@export test_lcm :: proc "c" (a, b: cstring) -> (res: PyRes) {
-	context = runtime.default_context()
-	err: big.Error
-
-	ai, bi, dest := &big.Int{}, &big.Int{}, &big.Int{}
-	defer big.internal_destroy(ai, bi, dest)
-
-	if err = big.atoi(ai, string(a), 16); err != nil { return PyRes{res=":lcm:atoi(a):", err=err} }
-	if err = big.atoi(bi, string(b), 16); err != nil { return PyRes{res=":lcm:atoi(b):", err=err} }
-	if err = #force_inline big.internal_int_gcd_lcm(nil, dest, ai, bi); err != nil { return PyRes{res=":lcm:lcm(a, b):", err=err} }
-
-	r := print_to_buffer(dest)
-	return PyRes{res = r, err = nil}
-}
-
-/*
-	dest = lcm(a, b)
-*/
-@export test_is_square :: proc "c" (a: cstring) -> (res: PyRes) {
-	context = runtime.default_context()
-	err:    big.Error
-	square: bool
-
-	ai := &big.Int{}
-	defer big.internal_destroy(ai)
-
-	if err = big.atoi(ai, string(a), 16); err != nil { return PyRes{res=":is_square:atoi(a):", err=err} }
-	if square, err = #force_inline big.internal_int_is_square(ai); err != nil { return PyRes{res=":is_square:is_square(a):", err=err} }
-
-	if square {
-		return PyRes{"True", nil}
-	}
-	return PyRes{"False", nil}
-}

+ 184 - 0
tests/core/math/big/test_core_math_big.odin

@@ -1,6 +1,7 @@
 package test_core_math_big
 
 import "core:math/big"
+import "core:strconv"
 import "core:testing"
 
 @(test)
@@ -82,4 +83,187 @@ test_rational_to_float :: proc(t: ^testing.T) {
 			testing.expect(t, err   == nil)
 		}
 	}
+}
+
+import "core:log"
+
+@(test)
+test_big_math_vectors :: proc(t: ^testing.T) {
+	for vec in big_test_vectors {
+		a, b, res, expected := &big.Int{}, &big.Int{}, &big.Int{}, &big.Int{}
+		defer big.destroy(a, b, res, expected)
+
+		atoi(t, a,        vec.a)   or_continue
+		atoi(t, b,        vec.b)   or_continue
+		atoi(t, expected, vec.exp) or_continue
+
+		#partial switch vec.op {
+		case .Add:
+			err := big.add(res, a, b)
+			testing.expect(t, err == vec.err)
+
+			expect_ab(t, "Expected add(%v, %v) to be %v, got %v", a, b, expected, res, err)
+
+		case .Sub:
+			err := big.sub(res, a, b)
+			testing.expect(t, err == vec.err)
+
+			expect_ab(t, "Expected sub(%v, %v) to be %v, got %v", a, b, expected, res, err)
+
+		case .Mul:
+			err := big.mul(res, a, b)
+			testing.expect(t, err == vec.err)
+
+			expect_ab(t, "Expected mul(%v, %v) to be %v, got %v", a, b, expected, res, err)
+
+		case .Div:
+			err := big.div(res, a, b)
+			testing.expect(t, err == vec.err)
+
+			expect_ab(t, "Expected div(%v, %v) to be %v, got %v", a, b, expected, res, err)
+
+		case .Sqr:
+			err := big.sqr(res, a)
+			testing.expect(t, err == vec.err)
+
+			expect_a(t, "Expected sqr(%v) to be %v, got %v", a, expected, res, err)
+
+		case .Log:
+			base, base_ok := strconv.parse_i64_of_base(vec.b, 16)
+			testing.expect(t, base_ok == true)
+
+			log_res, err := big.log(a, big.DIGIT(base))
+			testing.expect(t, err == vec.err)
+
+			big.set(res, log_res)
+			expect_ab(t, "Expected log(%v, %v) to be %v, got %v", a, b, expected, res, err)
+
+		case .Sqrt:
+			err := big.sqrt(res, a)
+			testing.expect(t, err == vec.err)
+
+			expect_a(t, "Expected sqrt(%v) to be %v, got %v", a, expected, res, err)
+
+		case .Pow:
+			power, power_ok := strconv.parse_i64_of_base(vec.b, 16)
+			testing.expect(t, power_ok == true)
+
+			err := big.pow(res, a, int(power))
+			testing.expect(t, err == vec.err)
+
+			expect_ab(t, "Expected pow(%v, %v) to be '%v', got %v", a, b, expected, res, err)
+
+		case .Root:
+			n, n_ok := strconv.parse_i64_of_base(vec.b, 16)
+			testing.expect(t, n_ok == true)
+
+			err := big.root_n(res, a, int(n))
+			testing.expect(t, err == vec.err)
+
+			expect_ab(t, "Expected root_n(%v, %v) to be '%v', got %v", a, b, expected, res, err)
+
+		case .Shl:
+			bits, bits_ok := strconv.parse_i64_of_base(vec.b, 16)
+			testing.expect(t, bits_ok == true)
+
+			err := big.internal_int_shl(res, a, int(bits))
+			testing.expect(t, err == vec.err)
+
+			expect_ab(t, "Expected internal_int_shl(%v, %v) to be '%v', got %v", a, b, expected, res, err)
+
+		case .Shr:
+			bits, bits_ok := strconv.parse_i64_of_base(vec.b, 16)
+			testing.expect(t, bits_ok == true)
+
+			err := big.internal_int_shr(res, a, int(bits))
+			testing.expect(t, err == vec.err)
+
+			expect_ab(t, "Expected internal_int_shr(%v, %v) to be '%v', got %v", a, b, expected, res, err)
+
+		case .Shr_Signed:
+			bits, bits_ok := strconv.parse_i64_of_base(vec.b, 16)
+			testing.expect(t, bits_ok == true)
+
+			big.set(res, a)
+			err := big.internal_int_shr_signed(res, res, int(bits))
+			testing.expect(t, err == vec.err)
+
+			expect_ab(t, "Expected internal_int_shr_signed(%v, %v) to be '%v', got %v", a, b, expected, res, err)
+
+		case .Factorial:
+			n, n_ok := strconv.parse_i64_of_base(vec.a, 16)
+			testing.expect(t, n_ok == true)
+
+			err := big.factorial(res, int(n))
+			testing.expect(t, err == vec.err)
+
+			expect_a(t, "Expected factorial(%v) to be '%v', got %v", a, expected, res, err)
+
+		case .Gcd:
+			err := big.internal_int_gcd_lcm(res, nil, a, b)
+			testing.expect(t, err == vec.err)
+
+			expect_ab(t, "Expected gcd(%v, %v) to be '%v', got %v", a, b, expected, res, err)
+
+		case .Lcm:
+			err := big.internal_int_gcd_lcm(nil, res, a, b)
+			testing.expect(t, err == vec.err)
+
+			expect_ab(t, "Expected lcm(%v, %v) to be '%v', got %v", a, b, expected, res, err)
+
+		case .Is_Square:
+			square, err := big.internal_int_is_square(a)
+			testing.expect(t, err == vec.err)
+
+			big.set(res, 1 if square else 0)
+			expect_a(t, "Expected is_square(%v) to be '%v', got %v", a, expected, res, err)
+
+		case:
+			log.assertf(false, "Unhandled op: %v", vec.op)
+		}
+	}
+}
+
+expect_a :: proc(t: ^testing.T, format: string, a, expected, res: ^big.Int, err: big.Error, loc := #caller_location) {
+	if err != .Okay { return }
+
+	equal, _ := big.equals(res, expected)
+	if !equal {
+		as, _ := big.itoa(a)
+		rs, _ := big.itoa(res)
+		es, _ := big.itoa(expected)
+
+		defer delete(as)
+		defer delete(rs)
+		defer delete(es)
+
+		testing.expectf(t, equal, format, as, es, rs, loc=loc)
+		assert(equal)
+	}
+}
+
+expect_ab :: proc(t: ^testing.T, format: string, a, b, expected, res: ^big.Int, err: big.Error, loc := #caller_location) {
+	if err != .Okay { return }
+
+	equal, _ := big.equals(res, expected)
+	if !equal {
+		as, _ := big.itoa(a)
+		bs, _ := big.itoa(b)
+		rs, _ := big.itoa(res)
+		es, _ := big.itoa(expected)
+
+		defer delete(as)
+		defer delete(bs)
+		defer delete(rs)
+		defer delete(es)
+
+		testing.expectf(t, equal, format, as, bs, es, rs, loc=loc)
+		assert(equal)
+	}
+}
+
+atoi :: proc(t: ^testing.T, i: ^big.Int, a: string, loc := #caller_location) -> bool {
+	err := big.atoi(i, a, 16)
+	testing.expect(t, err == .Okay, loc=loc)
+	return err == .Okay
 }

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 42 - 0
tests/core/math/big/test_vectors.odin


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 42 - 0
tests/core/math/test_vectors.odin


+ 2 - 1
tests/core/speed.odin

@@ -3,4 +3,5 @@ package tests_core
 
 @(require) import "crypto"
 @(require) import "hash"
-@(require) import "image"
+@(require) import "image"
+@(require) import "math/big"

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно