Merge branch 'master' into optional-semicolons

This commit is contained in:
gingerBill
2021-09-06 16:46:57 +01:00
19 changed files with 3420 additions and 441 deletions
+3
View File
@@ -28,6 +28,7 @@ jobs:
cp LLVM-C.dll dist
cp -r shared dist
cp -r core dist
cp -r vendor dist
cp -r bin dist
cp -r examples dist
- name: Upload artifact
@@ -51,6 +52,7 @@ jobs:
cp odin dist
cp -r shared dist
cp -r core dist
cp -r vendor dist
cp -r examples dist
- name: Upload artifact
uses: actions/upload-artifact@v1
@@ -77,6 +79,7 @@ jobs:
cp odin dist
cp -r shared dist
cp -r core dist
cp -r vendor dist
cp -r examples dist
- name: Upload artifact
uses: actions/upload-artifact@v1
+7 -1
View File
@@ -2,6 +2,9 @@
//+ignore
package intrinsics
// Package-Related
is_package_imported :: proc(package_name: string) -> bool ---
// Types
simd_vector :: proc($N: int, $T: typeid) -> type/#simd[N]T
soa_struct :: proc($N: int, $T: typeid) -> type/#soa[N]T
@@ -16,7 +19,7 @@ trap :: proc() -> ! ---
// Instructions
alloca :: proc(size, align: int) -> ^u8 ---
alloca :: proc(size, align: int) -> [^]u8 ---
cpu_relax :: proc() ---
read_cycle_counter :: proc() -> i64 ---
@@ -46,6 +49,9 @@ fixed_point_div_sat :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is
// Compiler Hints
expect :: proc(val, expected_val: T) -> T ---
// Linux and Darwin Only
syscall :: proc(id: uintptr, args: ..uintptr) -> uintptr ---
// Atomics
atomic_fence :: proc() ---
+4 -3
View File
@@ -1,14 +1,15 @@
package math_big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-2 license.
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 collects public proc maps and their aliases.
*/
package math_big
/*
=== === === === === === === === === === === === === === === === === === === === === === === ===
Basic arithmetic.
+4 -2
View File
@@ -1,9 +1,11 @@
@echo off
:odin run . -vet
odin run . -vet
: -define:MATH_BIG_USE_FROBENIUS_TEST=true
set TEST_ARGS=-fast-tests
:set TEST_ARGS=
:odin build . -build-mode:shared -show-timings -o:minimal -no-bounds-check -define:MATH_BIG_EXE=false && python test.py %TEST_ARGS%
odin build . -build-mode:shared -show-timings -o:size -no-bounds-check -define:MATH_BIG_EXE=false && python test.py %TEST_ARGS%
:odin build . -build-mode:shared -show-timings -o:size -no-bounds-check -define:MATH_BIG_EXE=false && python test.py %TEST_ARGS%
:odin build . -build-mode:shared -show-timings -o:size -define:MATH_BIG_EXE=false && python test.py %TEST_ARGS%
:odin build . -build-mode:shared -show-timings -o:speed -no-bounds-check -define:MATH_BIG_EXE=false && python test.py %TEST_ARGS%
:odin build . -build-mode:shared -show-timings -o:speed -define:MATH_BIG_EXE=false && python test.py -fast-tests %TEST_ARGS%
+53 -15
View File
@@ -1,5 +1,3 @@
package math_big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
@@ -8,6 +6,7 @@ package math_big
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.
*/
package math_big
import "core:intrinsics"
@@ -57,10 +56,10 @@ when #config(MATH_BIG_EXE, true) {
debugged where necessary.
*/
_DEFAULT_MUL_KARATSUBA_CUTOFF :: #config(MUL_KARATSUBA_CUTOFF, 80)
_DEFAULT_SQR_KARATSUBA_CUTOFF :: #config(SQR_KARATSUBA_CUTOFF, 120)
_DEFAULT_MUL_TOOM_CUTOFF :: #config(MUL_TOOM_CUTOFF, 350)
_DEFAULT_SQR_TOOM_CUTOFF :: #config(SQR_TOOM_CUTOFF, 400)
_DEFAULT_MUL_KARATSUBA_CUTOFF :: #config(MATH_BIG_MUL_KARATSUBA_CUTOFF, 80)
_DEFAULT_SQR_KARATSUBA_CUTOFF :: #config(MATH_BIG_SQR_KARATSUBA_CUTOFF, 120)
_DEFAULT_MUL_TOOM_CUTOFF :: #config(MATH_BIG_MUL_TOOM_CUTOFF, 350)
_DEFAULT_SQR_TOOM_CUTOFF :: #config(MATH_BIG_SQR_TOOM_CUTOFF, 400)
MAX_ITERATIONS_ROOT_N := 500
@@ -76,6 +75,29 @@ FACTORIAL_MAX_N := 1_000_000
FACTORIAL_BINARY_SPLIT_CUTOFF := 6100
FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS := 100
/*
`internal_int_is_prime` switchables.
Use Frobenius-Underwood for primality testing, or use Lucas-Selfridge (default).
*/
MATH_BIG_USE_LUCAS_SELFRIDGE_TEST :: #config(MATH_BIG_USE_LUCAS_SELFRIDGE_TEST, false)
MATH_BIG_USE_FROBENIUS_TEST :: !MATH_BIG_USE_LUCAS_SELFRIDGE_TEST
/*
Runtime tunable to use Miller-Rabin primality testing only and skip the above.
*/
USE_MILLER_RABIN_ONLY := false
/*
How many times we'll call `internal_int_random` during random prime generation before we bail out.
Set to 0 or less to try indefinitely.
*/
MAX_ITERATIONS_RANDOM_PRIME := 1_000_000
/*
How many iterations we used for the last random prime.
*/
@thread_local RANDOM_PRIME_ITERATIONS_USED: int
/*
We don't allow these to be switched at runtime for two reasons:
@@ -85,15 +107,22 @@ FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS := 100
2) Optimizations thanks to precomputed masks wouldn't work.
*/
MATH_BIG_FORCE_64_BIT :: #config(MATH_BIG_FORCE_64_BIT, false)
MATH_BIG_FORCE_32_BIT :: #config(MATH_BIG_FORCE_32_BIT, false)
MATH_BIG_FORCE_64_BIT :: #config(MATH_BIG_FORCE_64_BIT, false)
MATH_BIG_FORCE_32_BIT :: #config(MATH_BIG_FORCE_32_BIT, false)
when (MATH_BIG_FORCE_32_BIT && MATH_BIG_FORCE_64_BIT) { #panic("Cannot force 32-bit and 64-bit big backend simultaneously.") }
_LOW_MEMORY :: #config(BIGINT_SMALL_MEMORY, false)
/*
Trade a smaller memory footprint for more processing overhead?
*/
_LOW_MEMORY :: #config(MATH_BIG_SMALL_MEMORY, false)
when _LOW_MEMORY {
_DEFAULT_DIGIT_COUNT :: 8
_DEFAULT_DIGIT_COUNT :: 8
_TAB_SIZE :: 32
_MAX_WIN_SIZE :: 5
} else {
_DEFAULT_DIGIT_COUNT :: 32
_DEFAULT_DIGIT_COUNT :: 32
_TAB_SIZE :: 256
_MAX_WIN_SIZE :: 0
}
/*
@@ -137,6 +166,10 @@ Error :: enum int {
Division_by_Zero = 8,
Math_Domain_Error = 9,
Cannot_Open_File = 50,
Cannot_Read_File = 51,
Cannot_Write_File = 52,
Unimplemented = 127,
}
@@ -153,13 +186,17 @@ Error_String :: #partial [Error]string{
.Division_by_Zero = "Division by zero",
.Math_Domain_Error = "Math domain error",
.Cannot_Open_File = "Cannot_Open_File",
.Cannot_Read_File = "Cannot_Read_File",
.Cannot_Write_File = "Cannot_Write_File",
.Unimplemented = "Unimplemented",
}
Primality_Flag :: enum u8 {
Blum_Blum_Shub = 0, /* BBS style prime */
Safe = 1, /* Safe prime (p-1)/2 == prime */
Second_MSB_On = 3, /* force 2nd MSB to 1 */
Blum_Blum_Shub = 0, // Make prime congruent to 3 mod 4
Safe = 1, // Make sure (p-1)/2 is prime as well (implies .Blum_Blum_Shub)
Second_MSB_On = 3, // Make the 2nd highest bit one
}
Primality_Flags :: bit_set[Primality_Flag; u8]
@@ -198,7 +235,8 @@ when MATH_BIG_FORCE_64_BIT || (!MATH_BIG_FORCE_32_BIT && size_of(rawptr) == 8) {
_DIGIT_TYPE_BITS :: 8 * size_of(DIGIT)
_WORD_TYPE_BITS :: 8 * size_of(_WORD)
_DIGIT_BITS :: _DIGIT_TYPE_BITS - 4
_DIGIT_NAILS :: 4
_DIGIT_BITS :: _DIGIT_TYPE_BITS - _DIGIT_NAILS
_WORD_BITS :: 2 * _DIGIT_BITS
_MASK :: (DIGIT(1) << DIGIT(_DIGIT_BITS)) - DIGIT(1)
+52 -132
View File
@@ -1,6 +1,4 @@
//+ignore
package math_big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
@@ -9,6 +7,8 @@ package math_big
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.
*/
package math_big
import "core:fmt"
import "core:mem"
@@ -18,11 +18,15 @@ print_configation :: proc() {
`
Configuration:
_DIGIT_BITS %v
_SMALL_MEMORY %v
_MIN_DIGIT_COUNT %v
_MAX_DIGIT_COUNT %v
_DEFAULT_DIGIT_COUNT %v
_MAX_COMBA %v
_WARRAY %v
_TAB_SIZE %v
_MAX_WIN_SIZE %v
MATH_BIG_USE_LUCAS_SELFRIDGE_TEST %v
Runtime tunable:
MUL_KARATSUBA_CUTOFF %v
SQR_KARATSUBA_CUTOFF %v
@@ -32,13 +36,20 @@ Runtime tunable:
FACTORIAL_MAX_N %v
FACTORIAL_BINARY_SPLIT_CUTOFF %v
FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS %v
USE_MILLER_RABIN_ONLY %v
MAX_ITERATIONS_RANDOM_PRIME %v
`, _DIGIT_BITS,
_LOW_MEMORY,
_MIN_DIGIT_COUNT,
_MAX_DIGIT_COUNT,
_DEFAULT_DIGIT_COUNT,
_MAX_COMBA,
_WARRAY,
_TAB_SIZE,
_MAX_WIN_SIZE,
MATH_BIG_USE_LUCAS_SELFRIDGE_TEST,
MUL_KARATSUBA_CUTOFF,
SQR_KARATSUBA_CUTOFF,
MUL_TOOM_CUTOFF,
@@ -47,6 +58,8 @@ MAX_ITERATIONS_ROOT_N,
FACTORIAL_MAX_N,
FACTORIAL_BINARY_SPLIT_CUTOFF,
FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS,
USE_MILLER_RABIN_ONLY,
MAX_ITERATIONS_RANDOM_PRIME,
)
}
@@ -73,138 +86,45 @@ print :: proc(name: string, a: ^Int, base := i8(10), print_name := true, newline
}
}
int_to_byte :: proc(v: ^Int) {
err: Error
size: int
print("v: ", v)
fmt.println()
t := &Int{}
defer destroy(t)
if size, err = int_to_bytes_size(v); err != nil {
fmt.printf("int_to_bytes_size returned: %v\n", err)
return
}
b1 := make([]u8, size, context.temp_allocator)
err = int_to_bytes_big(v, b1)
int_from_bytes_big(t, b1)
fmt.printf("big: %v | err: %v\n", b1, err)
int_from_bytes_big(t, b1)
if internal_cmp_mag(t, v) != 0 {
print("\tError parsing t: ", t)
}
if size, err = int_to_bytes_size(v); err != nil {
fmt.printf("int_to_bytes_size returned: %v\n", err)
return
}
b2 := make([]u8, size, context.temp_allocator)
err = int_to_bytes_big_python(v, b2)
fmt.printf("big python: %v | err: %v\n", b2, err)
if err == nil {
int_from_bytes_big_python(t, b2)
if internal_cmp_mag(t, v) != 0 {
print("\tError parsing t: ", t)
}
}
if size, err = int_to_bytes_size(v, true); err != nil {
fmt.printf("int_to_bytes_size returned: %v\n", err)
return
}
b3 := make([]u8, size, context.temp_allocator)
err = int_to_bytes_big(v, b3, true)
fmt.printf("big signed: %v | err: %v\n", b3, err)
int_from_bytes_big(t, b3, true)
if internal_cmp(t, v) != 0 {
print("\tError parsing t: ", t)
}
if size, err = int_to_bytes_size(v, true); err != nil {
fmt.printf("int_to_bytes_size returned: %v\n", err)
return
}
b4 := make([]u8, size, context.temp_allocator)
err = int_to_bytes_big_python(v, b4, true)
fmt.printf("big signed python: %v | err: %v\n", b4, err)
int_from_bytes_big_python(t, b4, true)
if internal_cmp(t, v) != 0 {
print("\tError parsing t: ", t)
}
}
int_to_byte_little :: proc(v: ^Int) {
err: Error
size: int
print("v: ", v)
fmt.println()
t := &Int{}
defer destroy(t)
if size, err = int_to_bytes_size(v); err != nil {
fmt.printf("int_to_bytes_size returned: %v\n", err)
return
}
b1 := make([]u8, size, context.temp_allocator)
err = int_to_bytes_little(v, b1)
fmt.printf("little: %v | err: %v\n", b1, err)
int_from_bytes_little(t, b1)
if internal_cmp_mag(t, v) != 0 {
print("\tError parsing t: ", t)
}
if size, err = int_to_bytes_size(v); err != nil {
fmt.printf("int_to_bytes_size returned: %v\n", err)
return
}
b2 := make([]u8, size, context.temp_allocator)
err = int_to_bytes_little_python(v, b2)
fmt.printf("little python: %v | err: %v\n", b2, err)
if err == nil {
int_from_bytes_little_python(t, b2)
if internal_cmp_mag(t, v) != 0 {
print("\tError parsing t: ", t)
}
}
if size, err = int_to_bytes_size(v, true); err != nil {
fmt.printf("int_to_bytes_size returned: %v\n", err)
return
}
b3 := make([]u8, size, context.temp_allocator)
err = int_to_bytes_little(v, b3, true)
fmt.printf("little signed: %v | err: %v\n", b3, err)
int_from_bytes_little(t, b3, true)
if internal_cmp(t, v) != 0 {
print("\tError parsing t: ", t)
}
if size, err = int_to_bytes_size(v, true); err != nil {
fmt.printf("int_to_bytes_size returned: %v\n", err)
return
}
b4 := make([]u8, size, context.temp_allocator)
err = int_to_bytes_little_python(v, b4, true)
fmt.printf("little signed python: %v | err: %v\n", b4, err)
int_from_bytes_little_python(t, b4, true)
if internal_cmp(t, v) != 0 {
print("\tError parsing t: ", t)
}
}
// printf :: fmt.printf;
demo :: proc() {
a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}
defer destroy(a, b, c, d, e, f)
a, b, c, d, e, f, res := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}
defer destroy(a, b, c, d, e, f, res)
bits := 111
trials := -1
flags := Primality_Flags{}
fmt.printf("Trying to generate a %v bit prime using %v Miller-Rabin trials and options %v.\n", bits, trials, flags)
err: Error
{
SCOPED_TIMING(.random_prime)
err = internal_random_prime(a, bits, trials, flags)
}
print("a(10): ", a, 10, true, true, true)
fmt.printf("err: %v\n", err)
fmt.printf("RANDOM_PRIME_ITERATIONS_USED: %v\n", RANDOM_PRIME_ITERATIONS_USED)
nails := 0
count := internal_int_pack_count(a, u8, nails)
buf := make([]u8, count)
defer delete(buf)
written: int
order := Order.LSB_First
fmt.printf("\na.digit: %v\n", a.digit[:a.used])
written, err = internal_int_pack(a, buf, nails, order)
fmt.printf("\nPacked into buf: %v | err: %v | written: %v\n", buf, err, written)
err = internal_int_unpack(b, buf, nails, order)
print("\nUnpacked into b: ", b)
fmt.printf("err: %v\n", err)
fmt.printf("b.digit: %v\n", b.digit[:b.used])
}
main :: proc() {
+11 -12
View File
@@ -1,5 +1,3 @@
package math_big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
@@ -8,6 +6,7 @@ package math_big
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.
*/
package math_big
import "core:intrinsics"
import rnd "core:math/rand"
@@ -363,15 +362,15 @@ int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) {
return 0 // We shouldn't get here.
}
int_rand :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil, allocator := context.allocator) -> (err: Error) {
int_random :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil, allocator := context.allocator) -> (err: Error) {
/*
Check that `a` is usable.
*/
assert_if_nil(dest)
return #force_inline internal_int_rand(dest, bits, r, allocator)
return #force_inline internal_int_random(dest, bits, r, allocator)
}
rand :: proc { int_rand, }
random :: proc { int_random, }
/*
Internal helpers.
@@ -496,7 +495,7 @@ int_to_bytes_little :: proc(a: ^Int, buf: []u8, signed := false, allocator := co
if signed {
buf[l - 1] = 1 if a.sign == .Negative else 0
}
for offset := 0; offset < size_in_bits; offset += 8 {
#no_bounds_check for offset := 0; offset < size_in_bits; offset += 8 {
bits, _ := internal_int_bitfield_extract(a, offset, 8)
buf[i] = u8(bits & 255); i += 1
}
@@ -520,7 +519,7 @@ int_to_bytes_big :: proc(a: ^Int, buf: []u8, signed := false, allocator := conte
if signed {
buf[0] = 1 if a.sign == .Negative else 0
}
for offset := 0; offset < size_in_bits; offset += 8 {
#no_bounds_check for offset := 0; offset < size_in_bits; offset += 8 {
bits, _ := internal_int_bitfield_extract(a, offset, 8)
buf[i] = u8(bits & 255); i -= 1
}
@@ -547,7 +546,7 @@ int_to_bytes_little_python :: proc(a: ^Int, buf: []u8, signed := false, allocato
size_in_bits := internal_count_bits(t)
i := 0
for offset := 0; offset < size_in_bits; offset += 8 {
#no_bounds_check for offset := 0; offset < size_in_bits; offset += 8 {
bits, _ := internal_int_bitfield_extract(t, offset, 8)
buf[i] = 255 - u8(bits & 255); i += 1
}
@@ -555,7 +554,7 @@ int_to_bytes_little_python :: proc(a: ^Int, buf: []u8, signed := false, allocato
} else {
size_in_bits := internal_count_bits(a)
i := 0
for offset := 0; offset < size_in_bits; offset += 8 {
#no_bounds_check for offset := 0; offset < size_in_bits; offset += 8 {
bits, _ := internal_int_bitfield_extract(a, offset, 8)
buf[i] = u8(bits & 255); i += 1
}
@@ -584,7 +583,7 @@ int_to_bytes_big_python :: proc(a: ^Int, buf: []u8, signed := false, allocator :
size_in_bits := internal_count_bits(t)
i := l - 1
for offset := 0; offset < size_in_bits; offset += 8 {
#no_bounds_check for offset := 0; offset < size_in_bits; offset += 8 {
bits, _ := internal_int_bitfield_extract(t, offset, 8)
buf[i] = 255 - u8(bits & 255); i -= 1
}
@@ -621,7 +620,7 @@ int_from_bytes_big :: proc(a: ^Int, buf: []u8, signed := false, allocator := con
buf = buf[1:]
}
for v in buf {
#no_bounds_check for v in buf {
internal_shl(a, a, 8) or_return
a.digit[0] |= DIGIT(v)
}
@@ -658,7 +657,7 @@ int_from_bytes_big_python :: proc(a: ^Int, buf: []u8, signed := false, allocator
buf = buf[1:]
}
for v in buf {
#no_bounds_check for v in buf {
internal_shl(a, a, 8) or_return
if signed && sign == .Negative {
a.digit[0] |= DIGIT(255 - v)
+295 -32
View File
@@ -1,6 +1,4 @@
//+ignore
package math_big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
@@ -31,6 +29,7 @@ package math_big
TODO: Handle +/- Infinity and NaN.
*/
package math_big
import "core:mem"
import "core:intrinsics"
@@ -137,7 +136,7 @@ internal_int_add_signed :: proc(dest, a, b: ^Int, allocator := context.allocator
Subtract the one with the greater magnitude from the other.
The result gets the sign of the one with the greater magnitude.
*/
if #force_inline internal_cmp_mag(a, b) == -1 {
if #force_inline internal_lt_abs(a, b) {
x, y = y, x
}
@@ -359,7 +358,7 @@ internal_int_sub_signed :: proc(dest, number, decrease: ^Int, allocator := conte
Subtract a positive from a positive, OR negative from a negative.
First, take the difference between their magnitudes, then...
*/
if #force_inline internal_cmp_mag(number, decrease) == -1 {
if #force_inline internal_lt_abs(number, decrease) {
/*
The second has a larger magnitude.
The result has the *opposite* sign from the first number.
@@ -545,6 +544,25 @@ internal_int_shl1 :: proc(dest, src: ^Int, allocator := context.allocator) -> (e
return internal_clamp(dest)
}
/*
Multiply bigint `a` with int `d` and put the result in `dest`.
Like `internal_int_mul_digit` but with an integer as the small input.
*/
internal_int_mul_integer :: proc(dest, a: ^Int, b: $T, allocator := context.allocator) -> (err: Error)
where intrinsics.type_is_integer(T) && T != DIGIT {
context.allocator = allocator
t := &Int{}
defer internal_destroy(t)
/*
DIGIT might be smaller than a long, which excludes the use of `internal_int_mul_digit` here.
*/
internal_set(t, b) or_return
internal_mul(dest, a, t) or_return
return
}
/*
Multiply by a DIGIT.
*/
@@ -698,7 +716,7 @@ internal_int_mul :: proc(dest, src, multiplier: ^Int, allocator := context.alloc
return err
}
internal_mul :: proc { internal_int_mul, internal_int_mul_digit, }
internal_mul :: proc { internal_int_mul, internal_int_mul_digit, internal_int_mul_integer }
internal_sqr :: proc (dest, src: ^Int, allocator := context.allocator) -> (res: Error) {
/*
@@ -719,7 +737,7 @@ internal_int_divmod :: proc(quotient, remainder, numerator, denominator: ^Int, a
/*
If numerator < denominator then quotient = 0, remainder = numerator.
*/
if #force_inline internal_cmp_mag(numerator, denominator) == -1 {
if #force_inline internal_lt_abs(numerator, denominator) {
if remainder != nil {
internal_copy(remainder, numerator) or_return
}
@@ -732,7 +750,6 @@ internal_int_divmod :: proc(quotient, remainder, numerator, denominator: ^Int, a
if (denominator.used > 2 * MUL_KARATSUBA_CUTOFF) && (denominator.used <= (numerator.used / 3) * 2) {
assert(denominator.used >= 160 && numerator.used >= 240, "MUL_KARATSUBA_CUTOFF global not properly set.")
err = _private_int_div_recursive(quotient, remainder, numerator, denominator)
// err = #force_inline _private_int_div_school(quotient, remainder, numerator, denominator);
} else {
when true {
err = #force_inline _private_int_div_school(quotient, remainder, numerator, denominator)
@@ -856,14 +873,14 @@ internal_int_mod :: proc(remainder, numerator, denominator: ^Int, allocator := c
if remainder.used == 0 || denominator.sign == remainder.sign { return nil }
return #force_inline internal_add(remainder, remainder, numerator, allocator)
return #force_inline internal_add(remainder, remainder, denominator, allocator)
}
internal_int_mod_digit :: proc(numerator: ^Int, denominator: DIGIT, allocator := context.allocator) -> (remainder: DIGIT, err: Error) {
return internal_int_divmod_digit(nil, numerator, denominator, allocator)
}
internal_mod :: proc{ internal_int_mod, internal_int_mod_digit}
internal_mod :: proc{ internal_int_mod, internal_int_mod_digit, }
/*
remainder = (number + addend) % modulus.
@@ -942,6 +959,14 @@ internal_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int, allocator := context.
return #force_inline _private_int_gcd_lcm(res_gcd, res_lcm, a, b, allocator)
}
internal_int_gcd :: proc(res_gcd, a, b: ^Int, allocator := context.allocator) -> (err: Error) {
return #force_inline _private_int_gcd_lcm(res_gcd, nil, a, b, allocator)
}
internal_int_lcm :: proc(res_lcm, a, b: ^Int, allocator := context.allocator) -> (err: Error) {
return #force_inline _private_int_gcd_lcm(nil, res_lcm, a, b, allocator)
}
/*
remainder = numerator % (1 << bits)
@@ -992,13 +1017,21 @@ internal_int_mod_bits :: proc(remainder, numerator: ^Int, bits: int, allocator :
public ones that have already satisfied these constraints.
*/
/*
This procedure returns the allocated capacity of an Int.
Assumes `a` not to be `nil`.
*/
internal_int_allocated_cap :: #force_inline proc(a: ^Int) -> (cap: int) {
raw := transmute(mem.Raw_Dynamic_Array)a.digit
return raw.cap
}
/*
This procedure will return `true` if the `Int` is initialized, `false` if not.
Assumes `a` not to be `nil`.
*/
internal_int_is_initialized :: #force_inline proc(a: ^Int) -> (initialized: bool) {
raw := transmute(mem.Raw_Dynamic_Array)a.digit
return raw.cap >= _MIN_DIGIT_COUNT
return internal_int_allocated_cap(a) >= _MIN_DIGIT_COUNT
}
internal_is_initialized :: proc { internal_int_is_initialized, }
@@ -1091,6 +1124,7 @@ internal_is_power_of_two :: proc { internal_int_is_power_of_two, }
Expects `a` and `b` both to be valid `Int`s, i.e. initialized and not `nil`.
*/
internal_int_compare :: #force_inline proc(a, b: ^Int) -> (comparison: int) {
assert_if_nil(a, b)
a_is_negative := #force_inline internal_is_negative(a)
/*
@@ -1114,6 +1148,7 @@ internal_cmp :: internal_compare
Expects: `a` and `b` both to be valid `Int`s, i.e. initialized and not `nil`.
*/
internal_int_compare_digit :: #force_inline proc(a: ^Int, b: DIGIT) -> (comparison: int) {
assert_if_nil(a)
a_is_negative := #force_inline internal_is_negative(a)
switch {
@@ -1145,6 +1180,7 @@ internal_cmp_digit :: internal_compare_digit
Compare the magnitude of two `Int`s, unsigned.
*/
internal_int_compare_magnitude :: #force_inline proc(a, b: ^Int) -> (comparison: int) {
assert_if_nil(a, b)
/*
Compare based on used digits.
*/
@@ -1172,6 +1208,177 @@ internal_int_compare_magnitude :: #force_inline proc(a, b: ^Int) -> (comparison:
internal_compare_magnitude :: proc { internal_int_compare_magnitude, }
internal_cmp_mag :: internal_compare_magnitude
/*
bool := a < b
*/
internal_int_less_than :: #force_inline proc(a, b: ^Int) -> (less_than: bool) {
return internal_cmp(a, b) == -1
}
/*
bool := a < b
*/
internal_int_less_than_digit :: #force_inline proc(a: ^Int, b: DIGIT) -> (less_than: bool) {
return internal_cmp_digit(a, b) == -1
}
/*
bool := |a| < |b|
Compares the magnitudes only, ignores the sign.
*/
internal_int_less_than_abs :: #force_inline proc(a, b: ^Int) -> (less_than: bool) {
return internal_cmp_mag(a, b) == -1
}
internal_less_than :: proc {
internal_int_less_than,
internal_int_less_than_digit,
}
internal_lt :: internal_less_than
internal_less_than_abs :: proc {
internal_int_less_than_abs,
}
internal_lt_abs :: internal_less_than_abs
/*
bool := a <= b
*/
internal_int_less_than_or_equal :: #force_inline proc(a, b: ^Int) -> (less_than_or_equal: bool) {
return internal_cmp(a, b) <= 0
}
/*
bool := a <= b
*/
internal_int_less_than_or_equal_digit :: #force_inline proc(a: ^Int, b: DIGIT) -> (less_than_or_equal: bool) {
return internal_cmp_digit(a, b) <= 0
}
/*
bool := |a| <= |b|
Compares the magnitudes only, ignores the sign.
*/
internal_int_less_than_or_equal_abs :: #force_inline proc(a, b: ^Int) -> (less_than_or_equal: bool) {
return internal_cmp_mag(a, b) <= 0
}
internal_less_than_or_equal :: proc {
internal_int_less_than_or_equal,
internal_int_less_than_or_equal_digit,
}
internal_lte :: internal_less_than_or_equal
internal_less_than_or_equal_abs :: proc {
internal_int_less_than_or_equal_abs,
}
internal_lte_abs :: internal_less_than_or_equal_abs
/*
bool := a == b
*/
internal_int_equals :: #force_inline proc(a, b: ^Int) -> (equals: bool) {
return internal_cmp(a, b) == 0
}
/*
bool := a == b
*/
internal_int_equals_digit :: #force_inline proc(a: ^Int, b: DIGIT) -> (equals: bool) {
return internal_cmp_digit(a, b) == 0
}
/*
bool := |a| == |b|
Compares the magnitudes only, ignores the sign.
*/
internal_int_equals_abs :: #force_inline proc(a, b: ^Int) -> (equals: bool) {
return internal_cmp_mag(a, b) == 0
}
internal_equals :: proc {
internal_int_equals,
internal_int_equals_digit,
}
internal_eq :: internal_equals
internal_equals_abs :: proc {
internal_int_equals_abs,
}
internal_eq_abs :: internal_equals_abs
/*
bool := a >= b
*/
internal_int_greater_than_or_equal :: #force_inline proc(a, b: ^Int) -> (greater_than_or_equal: bool) {
return internal_cmp(a, b) >= 0
}
/*
bool := a >= b
*/
internal_int_greater_than_or_equal_digit :: #force_inline proc(a: ^Int, b: DIGIT) -> (greater_than_or_equal: bool) {
return internal_cmp_digit(a, b) >= 0
}
/*
bool := |a| >= |b|
Compares the magnitudes only, ignores the sign.
*/
internal_int_greater_than_or_equal_abs :: #force_inline proc(a, b: ^Int) -> (greater_than_or_equal: bool) {
return internal_cmp_mag(a, b) >= 0
}
internal_greater_than_or_equal :: proc {
internal_int_greater_than_or_equal,
internal_int_greater_than_or_equal_digit,
}
internal_gte :: internal_greater_than_or_equal
internal_greater_than_or_equal_abs :: proc {
internal_int_greater_than_or_equal_abs,
}
internal_gte_abs :: internal_greater_than_or_equal_abs
/*
bool := a > b
*/
internal_int_greater_than :: #force_inline proc(a, b: ^Int) -> (greater_than: bool) {
return internal_cmp(a, b) == 1
}
/*
bool := a > b
*/
internal_int_greater_than_digit :: #force_inline proc(a: ^Int, b: DIGIT) -> (greater_than: bool) {
return internal_cmp_digit(a, b) == 1
}
/*
bool := |a| > |b|
Compares the magnitudes only, ignores the sign.
*/
internal_int_greater_than_abs :: #force_inline proc(a, b: ^Int) -> (greater_than: bool) {
return internal_cmp_mag(a, b) == 1
}
internal_greater_than :: proc {
internal_int_greater_than,
internal_int_greater_than_digit,
}
internal_gt :: internal_greater_than
internal_greater_than_abs :: proc {
internal_int_greater_than_abs,
}
internal_gt_abs :: internal_greater_than_abs
/*
Check if remainders are possible squares - fast exclude non-squares.
@@ -1229,7 +1436,7 @@ internal_int_is_square :: proc(a: ^Int, allocator := context.allocator) -> (squa
sqrt(t, a) or_return
sqr(t, t) or_return
square = internal_cmp_mag(t, a) == 0
square = internal_eq_abs(t, a)
return
}
@@ -1461,7 +1668,7 @@ internal_int_sqrt :: proc(dest, src: ^Int, allocator := context.allocator) -> (e
internal_add(t2, t1, x) or_return
internal_shr(y, t2, 1) or_return
if c := internal_cmp(y, x); c == 0 || c == 1 {
if internal_gte(y, x) {
internal_swap(dest, x)
return nil
}
@@ -1576,8 +1783,8 @@ internal_int_root_n :: proc(dest, src: ^Int, n: int, allocator := context.alloca
Number of rounds is at most log_2(root). If it is more it
got stuck, so break out of the loop and do the rest manually.
*/
if ilog2 -= 1; ilog2 == 0 { break }
if internal_cmp(t1, t2) == 0 { break }
if ilog2 -= 1; ilog2 == 0 { break }
if internal_eq(t1, t2) { break }
iterations += 1
if iterations == MAX_ITERATIONS_ROOT_N {
@@ -1615,7 +1822,7 @@ internal_int_root_n :: proc(dest, src: ^Int, n: int, allocator := context.alloca
for {
internal_pow(t2, t1, n) or_return
if internal_cmp(t2, a) != 1 { break }
if internal_lt(t2, a) { break }
internal_sub(t1, t1, DIGIT(1)) or_return
@@ -1651,8 +1858,7 @@ internal_int_destroy :: proc(integers: ..^Int) {
integers := integers
for a in &integers {
raw := transmute(mem.Raw_Dynamic_Array)a.digit
if raw.cap > 0 {
if internal_int_allocated_cap(a) > 0 {
mem.zero_slice(a.digit[:])
free(&a.digit[0])
}
@@ -1692,7 +1898,7 @@ internal_int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, al
return nil
}
internal_set :: proc { internal_int_set_from_integer, internal_int_copy }
internal_set :: proc { internal_int_set_from_integer, internal_int_copy, int_atoi }
internal_copy_digits :: #force_inline proc(dest, src: ^Int, digits: int, offset := int(0)) -> (err: Error) {
#force_inline internal_error_if_immutable(dest) or_return
@@ -1821,12 +2027,12 @@ internal_int_inverse_modulo :: proc(dest, a, b: ^Int, allocator := context.alloc
/*
For all n in N and n > 0, n = 0 mod 1.
*/
if internal_is_positive(a) && internal_cmp(b, 1) == 0 { return internal_zero(dest) }
if internal_is_positive(a) && internal_eq(b, 1) { return internal_zero(dest) }
/*
`b` cannot be negative and has to be > 1
*/
if internal_is_negative(b) && internal_cmp(b, 1) != 1 { return .Invalid_Argument }
if internal_is_negative(b) || internal_gt(b, 1) { return .Invalid_Argument }
/*
If the modulus is odd we can use a faster routine instead.
@@ -1839,9 +2045,20 @@ internal_invmod :: proc{ internal_int_inverse_modulo, }
/*
Helpers to extract values from the `Int`.
Offset is zero indexed.
*/
internal_int_bitfield_extract_bool :: proc(a: ^Int, offset: int) -> (val: bool, err: Error) {
limb := offset / _DIGIT_BITS
if limb < 0 || limb >= a.used { return false, .Invalid_Argument }
i := _WORD(1 << _WORD((offset % _DIGIT_BITS)))
return bool(_WORD(a.digit[limb]) & i), nil
}
internal_int_bitfield_extract_single :: proc(a: ^Int, offset: int) -> (bit: _WORD, err: Error) {
return #force_inline int_bitfield_extract(a, offset, 1)
limb := offset / _DIGIT_BITS
if limb < 0 || limb >= a.used { return 0, .Invalid_Argument }
i := _WORD(1 << _WORD((offset % _DIGIT_BITS)))
return 1 if ((_WORD(a.digit[limb]) & i) != 0) else 0, nil
}
internal_int_bitfield_extract :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) #no_bounds_check {
@@ -1899,6 +2116,34 @@ internal_int_bitfield_extract :: proc(a: ^Int, offset, count: int) -> (res: _WOR
return res, nil
}
/*
Helpers to (un)set a bit in an Int.
Offset is zero indexed.
*/
internal_int_bitfield_set_single :: proc(a: ^Int, offset: int) -> (err: Error) {
limb := offset / _DIGIT_BITS
if limb < 0 || limb >= a.used { return .Invalid_Argument }
i := DIGIT(1 << uint((offset % _DIGIT_BITS)))
a.digit[limb] |= i
return
}
internal_int_bitfield_unset_single :: proc(a: ^Int, offset: int) -> (err: Error) {
limb := offset / _DIGIT_BITS
if limb < 0 || limb >= a.used { return .Invalid_Argument }
i := DIGIT(1 << uint((offset % _DIGIT_BITS)))
a.digit[limb] &= _MASK - i
return
}
internal_int_bitfield_toggle_single :: proc(a: ^Int, offset: int) -> (err: Error) {
limb := offset / _DIGIT_BITS
if limb < 0 || limb >= a.used { return .Invalid_Argument }
i := DIGIT(1 << uint((offset % _DIGIT_BITS)))
a.digit[limb] ~= i
return
}
/*
Resize backing store.
We don't need to pass the allocator, because the storage itself stores it.
@@ -1914,23 +2159,23 @@ internal_int_shrink :: proc(a: ^Int) -> (err: Error) {
internal_shrink :: proc { internal_int_shrink, }
internal_int_grow :: proc(a: ^Int, digits: int, allow_shrink := false, allocator := context.allocator) -> (err: Error) {
raw := transmute(mem.Raw_Dynamic_Array)a.digit
/*
We need at least _MIN_DIGIT_COUNT or a.used digits, whichever is bigger.
The caller is asking for `digits`. Let's be accomodating.
*/
cap := internal_int_allocated_cap(a)
needed := max(_MIN_DIGIT_COUNT, a.used, digits)
if !allow_shrink {
needed = max(needed, raw.cap)
needed = max(needed, cap)
}
/*
If not yet iniialized, initialize the `digit` backing with the allocator we were passed.
*/
if raw.cap == 0 {
if cap == 0 {
a.digit = make([dynamic]DIGIT, needed, allocator)
} else if raw.cap != needed {
} else if cap != needed {
/*
`[dynamic]DIGIT` already knows what allocator was used for it, so resize will do the right thing.
*/
@@ -2558,9 +2803,27 @@ internal_int_count_lsb :: proc(a: ^Int) -> (count: int, err: Error) {
x: int
#no_bounds_check for x = 0; x < a.used && a.digit[x] == 0; x += 1 {}
q := a.digit[x]
x *= _DIGIT_BITS
x += internal_count_lsb(q)
when true {
q := a.digit[x]
x *= _DIGIT_BITS
x += internal_count_lsb(q)
} else {
lnz := []int{
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
}
q := a.digit[x]
x *= _DIGIT_BITS
if q & 1 == 0 {
p: DIGIT
for {
p = q & 15
x += lnz[p]
q >>= 4
if p != 0 { break }
}
}
}
return x, nil
}
@@ -2583,7 +2846,7 @@ internal_int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) {
return 0 // We shouldn't get here.
}
internal_int_rand :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil, allocator := context.allocator) -> (err: Error) {
internal_int_random :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil, allocator := context.allocator) -> (err: Error) {
context.allocator = allocator
bits := bits
@@ -2608,7 +2871,7 @@ internal_int_rand :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil, allocator :
dest.used = digits
return nil
}
internal_rand :: proc { internal_int_rand, }
internal_random :: proc { internal_int_random, }
/*
Internal helpers.
+1 -2
View File
@@ -1,5 +1,3 @@
package math_big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
@@ -10,6 +8,7 @@ package math_big
This file contains logical operations like `and`, `or` and `xor`.
*/
package math_big
/*
The `and`, `or` and `xor` binops differ in two lines only.
+1297 -141
View File
File diff suppressed because it is too large Load Diff
+1032 -30
View File
File diff suppressed because it is too large Load Diff
+299 -2
View File
@@ -1,5 +1,3 @@
package math_big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
@@ -10,6 +8,9 @@ package math_big
This file contains basic arithmetic operations like `add`, `sub`, `mul`, `div`, ...
*/
package math_big
import "core:intrinsics"
/*
===========================
@@ -384,6 +385,10 @@ digit_log :: proc(a: DIGIT, base: DIGIT) -> (log: int, err: Error) {
}
log :: proc { int_log, digit_log, }
ilog2 :: proc(value: $T) -> (log2: T) {
return (size_of(T) * 8) - intrinsics.count_leading_zeros(value)
}
/*
Calculate `dest = base^power` using a square-multiply algorithm.
*/
@@ -556,6 +561,298 @@ int_compare_magnitude :: proc(a, b: ^Int, allocator := context.allocator) -> (re
return #force_inline internal_cmp_mag(a, b), nil
}
int_cmp_mag :: int_compare_magnitude
/*
bool := a < b
*/
int_less_than :: #force_inline proc(a, b: ^Int, allocator := context.allocator) -> (less_than: bool, err: Error) {
assert_if_nil(a, b)
context.allocator = allocator
internal_clear_if_uninitialized(a, b) or_return
c: int
c, err = cmp(a, b)
return c == -1, err
}
/*
bool := a < b
*/
int_less_than_digit :: #force_inline proc(a: ^Int, b: DIGIT, allocator := context.allocator) -> (less_than: bool, err: Error) {
assert_if_nil(a)
context.allocator = allocator
internal_clear_if_uninitialized(a) or_return
c: int
c, err = cmp(a, b)
return c == -1, err
}
/*
bool := |a| < |b|
Compares the magnitudes only, ignores the sign.
*/
int_less_than_abs :: #force_inline proc(a, b: ^Int, allocator := context.allocator) -> (less_than: bool, err: Error) {
assert_if_nil(a, b)
context.allocator = allocator
internal_clear_if_uninitialized(a, b) or_return
c: int
c, err = cmp_mag(a, b)
return c == -1, err
}
less_than :: proc {
int_less_than,
int_less_than_digit,
}
lt :: less_than
less_than_abs :: proc {
int_less_than_abs,
}
lt_abs :: less_than_abs
/*
bool := a <= b
*/
int_less_than_or_equal :: #force_inline proc(a, b: ^Int, allocator := context.allocator) -> (less_than_or_equal: bool, err: Error) {
assert_if_nil(a, b)
context.allocator = allocator
internal_clear_if_uninitialized(a, b) or_return
c: int
c, err = cmp(a, b)
return c <= 0, err
}
/*
bool := a <= b
*/
int_less_than_or_equal_digit :: #force_inline proc(a: ^Int, b: DIGIT, allocator := context.allocator) -> (less_than_or_equal: bool, err: Error) {
assert_if_nil(a)
context.allocator = allocator
internal_clear_if_uninitialized(a) or_return
c: int
c, err = cmp(a, b)
return c <= 0, err
}
/*
bool := |a| <= |b|
Compares the magnitudes only, ignores the sign.
*/
int_less_than_or_equal_abs :: #force_inline proc(a, b: ^Int, allocator := context.allocator) -> (less_than_or_equal: bool, err: Error) {
assert_if_nil(a, b)
context.allocator = allocator
internal_clear_if_uninitialized(a, b) or_return
c: int
c, err = cmp_mag(a, b)
return c <= 0, err
}
less_than_or_equal :: proc {
int_less_than_or_equal,
int_less_than_or_equal_digit,
}
lteq :: less_than_or_equal
less_than_or_equal_abs :: proc {
int_less_than_or_equal_abs,
}
lteq_abs :: less_than_or_equal_abs
/*
bool := a == b
*/
int_equals :: #force_inline proc(a, b: ^Int, allocator := context.allocator) -> (equals: bool, err: Error) {
assert_if_nil(a, b)
context.allocator = allocator
internal_clear_if_uninitialized(a, b) or_return
c: int
c, err = cmp(a, b)
return c == 0, err
}
/*
bool := a == b
*/
int_equals_digit :: #force_inline proc(a: ^Int, b: DIGIT, allocator := context.allocator) -> (equals: bool, err: Error) {
assert_if_nil(a)
context.allocator = allocator
internal_clear_if_uninitialized(a) or_return
c: int
c, err = cmp(a, b)
return c == 0, err
}
/*
bool := |a| == |b|
Compares the magnitudes only, ignores the sign.
*/
int_equals_abs :: #force_inline proc(a, b: ^Int, allocator := context.allocator) -> (equals: bool, err: Error) {
assert_if_nil(a, b)
context.allocator = allocator
internal_clear_if_uninitialized(a, b) or_return
c: int
c, err = cmp_mag(a, b)
return c == 0, err
}
equals :: proc {
int_equals,
int_equals_digit,
}
eq :: equals
equals_abs :: proc {
int_equals_abs,
}
eq_abs :: equals_abs
/*
bool := a >= b
*/
int_greater_than_or_equal :: #force_inline proc(a, b: ^Int, allocator := context.allocator) -> (greater_than_or_equal: bool, err: Error) {
assert_if_nil(a, b)
context.allocator = allocator
internal_clear_if_uninitialized(a, b) or_return
c: int
c, err = cmp(a, b)
return c >= 0, err
}
/*
bool := a >= b
*/
int_greater_than_or_equal_digit :: #force_inline proc(a: ^Int, b: DIGIT, allocator := context.allocator) -> (greater_than_or_equal: bool, err: Error) {
assert_if_nil(a)
context.allocator = allocator
internal_clear_if_uninitialized(a) or_return
c: int
c, err = cmp(a, b)
return c >= 0, err
}
/*
bool := |a| >= |b|
Compares the magnitudes only, ignores the sign.
*/
int_greater_than_or_equal_abs :: #force_inline proc(a, b: ^Int, allocator := context.allocator) -> (greater_than_or_equal: bool, err: Error) {
assert_if_nil(a, b)
context.allocator = allocator
internal_clear_if_uninitialized(a, b) or_return
c: int
c, err = cmp_mag(a, b)
return c >= 0, err
}
greater_than_or_equal :: proc {
int_greater_than_or_equal,
int_greater_than_or_equal_digit,
}
gteq :: greater_than_or_equal
greater_than_or_equal_abs :: proc {
int_greater_than_or_equal_abs,
}
gteq_abs :: greater_than_or_equal_abs
/*
bool := a > b
*/
int_greater_than :: #force_inline proc(a, b: ^Int, allocator := context.allocator) -> (greater_than: bool, err: Error) {
assert_if_nil(a, b)
context.allocator = allocator
internal_clear_if_uninitialized(a, b) or_return
c: int
c, err = cmp(a, b)
return c > 0, err
}
/*
bool := a > b
*/
int_greater_than_digit :: #force_inline proc(a: ^Int, b: DIGIT, allocator := context.allocator) -> (greater_than: bool, err: Error) {
assert_if_nil(a)
context.allocator = allocator
internal_clear_if_uninitialized(a) or_return
c: int
c, err = cmp(a, b)
return c > 0, err
}
/*
bool := |a| > |b|
Compares the magnitudes only, ignores the sign.
*/
int_greater_than_abs :: #force_inline proc(a, b: ^Int, allocator := context.allocator) -> (greater_than: bool, err: Error) {
assert_if_nil(a, b)
context.allocator = allocator
internal_clear_if_uninitialized(a, b) or_return
c: int
c, err = cmp_mag(a, b)
return c > 0, err
}
greater_than :: proc {
int_greater_than,
int_greater_than_digit,
}
gt :: greater_than
greater_than_abs :: proc {
int_greater_than_abs,
}
gt_abs :: greater_than_abs
/*
Check if remainders are possible squares - fast exclude non-squares.
+187 -22
View File
@@ -1,5 +1,3 @@
package math_big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
@@ -14,23 +12,22 @@ package math_big
- Use Barrett reduction for non-powers-of-two.
- Also look at extracting and splatting several digits at once.
*/
package math_big
import "core:intrinsics"
import "core:mem"
import "core:os"
/*
This version of `itoa` allocates one behalf of the caller. The caller must free the string.
This version of `itoa` allocates on behalf of the caller. The caller must free the string.
The radix defaults to 10.
*/
int_itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator := context.allocator) -> (res: string, err: Error) {
int_itoa_string :: proc(a: ^Int, radix := i8(10), zero_terminate := false, allocator := context.allocator) -> (res: string, err: Error) {
assert_if_nil(a)
context.allocator = allocator
a := a; radix := radix
clear_if_uninitialized(a) or_return
/*
Radix defaults to 10.
*/
radix = radix if radix > 0 else 10
/*
TODO: If we want to write a prefix for some of the radixes, we can oversize the buffer.
@@ -58,18 +55,15 @@ int_itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, alloc
}
/*
This version of `itoa` allocates one behalf of the caller. The caller must free the string.
This version of `itoa` allocates on behalf of the caller. The caller must free the string.
The radix defaults to 10.
*/
int_itoa_cstring :: proc(a: ^Int, radix := i8(-1), allocator := context.allocator) -> (res: cstring, err: Error) {
int_itoa_cstring :: proc(a: ^Int, radix := i8(10), allocator := context.allocator) -> (res: cstring, err: Error) {
assert_if_nil(a)
context.allocator = allocator
a := a; radix := radix
clear_if_uninitialized(a) or_return
/*
Radix defaults to 10.
*/
radix = radix if radix > 0 else 10
s: string
s, err = int_itoa_string(a, radix, true)
@@ -378,6 +372,177 @@ radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false, allocator := con
return size, nil
}
/*
We might add functions to read and write byte-encoded Ints from/to files, using `int_to_bytes_*` functions.
LibTomMath allows exporting/importing to/from a file in ASCII, but it doesn't support a much more compact representation in binary, even though it has several pack functions int_to_bytes_* (which I expanded upon and wrote Python interoperable versions of as well), and (un)pack, which is GMP compatible.
Someone could implement their own read/write binary int procedures, of course.
Could be worthwhile to add a canonical binary file representation with an optional small header that says it's an Odin big.Int, big.Rat or Big.Float, byte count for each component that follows, flag for big/little endian and a flag that says a checksum exists at the end of the file.
For big.Rat and big.Float the header couldn't be optional, because we'd have no way to distinguish where the components end.
*/
/*
Read an Int from an ASCII file.
*/
internal_int_read_from_ascii_file :: proc(a: ^Int, filename: string, radix := i8(10), allocator := context.allocator) -> (err: Error) {
context.allocator = allocator
/*
We can either read the entire file at once, or read a bunch at a time and keep multiplying by the radix.
For now, we'll read the entire file. Eventually we'll replace this with a copy that duplicates the logic
of `atoi` so we don't need to read the entire file.
*/
res, ok := os.read_entire_file(filename, allocator)
defer delete(res, allocator)
if !ok {
return .Cannot_Read_File
}
as := string(res)
return atoi(a, as, radix)
}
/*
Write an Int to an ASCII file.
*/
internal_int_write_to_ascii_file :: proc(a: ^Int, filename: string, radix := i8(10), allocator := context.allocator) -> (err: Error) {
context.allocator = allocator
/*
For now we'll convert the Int using itoa and writing the result in one go.
If we want to preserve memory we could duplicate the itoa logic and write backwards.
*/
as := itoa(a, radix) or_return
defer delete(as)
l := len(as)
assert(l > 0)
data := transmute([]u8)mem.Raw_Slice{
data = raw_data(as),
len = l,
}
ok := os.write_entire_file(name=filename, data=data, truncate=true)
return nil if ok else .Cannot_Write_File
}
/*
Calculate the size needed for `internal_int_pack`.
See https://gmplib.org/manual/Integer-Import-and-Export.html
*/
internal_int_pack_count :: proc(a: ^Int, $T: typeid, nails := 0) -> (size_needed: int) {
assert(nails >= 0 && nails < (size_of(T) * 8))
bits := internal_count_bits(a)
size := size_of(T)
size_needed = bits / ((size * 8) - nails)
size_needed += 1 if (bits % ((size * 8) - nails)) != 0 else 0
return size_needed
}
/*
Based on gmp's mpz_export.
See https://gmplib.org/manual/Integer-Import-and-Export.html
`buf` is a pre-allocated slice of type `T` "words", which must be an unsigned integer of some description.
Use `internal_int_pack_count(a, T, nails)` to calculate the necessary size.
The library internally uses `DIGIT` as the type, which is u64 or u32 depending on the platform.
You are of course welcome to export to []u8, []u32be, and so forth.
After this you can use `mem.slice_data_cast` to interpret the buffer as bytes if you so choose.
`nails` are the number of top bits the output "word" reserves.
To mimic the internals of this library, this would be 4.
To use the minimum amount of output bytes, set `nails` to 0 and pass a `[]u8`.
IMPORTANT: `pack` serializes the magnitude of an Int, that is, the output is unsigned.
Assumes `a` not to be `nil` and to have been initialized.
*/
internal_int_pack :: proc(a: ^Int, buf: []$T, nails := 0, order := Order.LSB_First) -> (written: int, err: Error)
where intrinsics.type_is_integer(T) && intrinsics.type_is_unsigned(T) && size_of(T) <= 16 {
assert(nails >= 0 && nails < (size_of(T) * 8))
type_size := size_of(T)
type_bits := (type_size * 8) - nails
word_count := internal_int_pack_count(a, T, nails)
bit_count := internal_count_bits(a)
if len(buf) < word_count {
return 0, .Buffer_Overflow
}
bit_offset := 0
word_offset := 0
#no_bounds_check for i := 0; i < word_count; i += 1 {
bit_offset = i * type_bits
if order == .MSB_First {
word_offset = word_count - i - 1
} else {
word_offset = i
}
bits_to_get := min(type_bits, bit_count - bit_offset)
W := internal_int_bitfield_extract(a, bit_offset, bits_to_get) or_return
buf[word_offset] = T(W)
}
return word_count, nil
}
internal_int_unpack :: proc(a: ^Int, buf: []$T, nails := 0, order := Order.LSB_First, allocator := context.allocator) -> (err: Error)
where intrinsics.type_is_integer(T) && intrinsics.type_is_unsigned(T) && size_of(T) <= 16 {
assert(nails >= 0 && nails < (size_of(T) * 8))
context.allocator = allocator
type_size := size_of(T)
type_bits := (type_size * 8) - nails
type_mask := T(1 << uint(type_bits)) - 1
if len(buf) == 0 {
return .Invalid_Argument
}
bit_count := type_bits * len(buf)
digit_count := (bit_count / _DIGIT_BITS) + min(1, bit_count % _DIGIT_BITS)
/*
Pre-size output Int.
*/
internal_grow(a, digit_count) or_return
t := &Int{}
defer internal_destroy(t)
if order == .LSB_First {
for W, i in buf {
internal_set(t, W & type_mask) or_return
internal_shl(t, t, type_bits * i) or_return
internal_add(a, a, t) or_return
}
} else {
for W in buf {
internal_set(t, W & type_mask) or_return
internal_shl(a, a, type_bits) or_return
internal_add(a, a, t) or_return
}
}
return internal_clamp(a)
}
/*
Overestimate the size needed for the bigint to string conversion by a very small amount.
The error is about 10^-8; it will overestimate the result by at most 11 elements for
@@ -414,14 +579,14 @@ _log_bases :: [65]u32{
*/
RADIX_TABLE := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"
RADIX_TABLE_REVERSE := [RADIX_TABLE_REVERSE_SIZE]u8{
0x3e, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x01, 0x02, 0x03, 0x04, /* +,-./01234 */
0x05, 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, /* 56789:;<=> */
0xff, 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, /* ?@ABCDEFGH */
0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, /* IJKLMNOPQR */
0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0xff, 0xff, /* STUVWXYZ[\ */
0xff, 0xff, 0xff, 0xff, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, /* ]^_`abcdef */
0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, /* ghijklmnop */
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* qrstuvwxyz */
0x3e, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x01, 0x02, 0x03, 0x04, /* +,-./01234 */
0x05, 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, /* 56789:;<=> */
0xff, 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, /* ?@ABCDEFGH */
0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, /* IJKLMNOPQR */
0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0xff, 0xff, /* STUVWXYZ[\ */
0xff, 0xff, 0xff, 0xff, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, /* ]^_`abcdef */
0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, /* ghijklmnop */
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* qrstuvwxyz */
}
RADIX_TABLE_REVERSE_SIZE :: 80
+1 -2
View File
@@ -1,6 +1,4 @@
//+ignore
package math_big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
@@ -11,6 +9,7 @@ package math_big
This file exports procedures for use with the test.py test suite.
*/
package math_big
/*
TODO: Write tests for `internal_*` and test reusing parameters with the public implementations.
+9 -1
View File
@@ -1,3 +1,12 @@
#
# Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
# Made available under Odin's BSD-3 license.
#
# A BigInt 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.
#
from ctypes import *
from random import *
import math
@@ -404,7 +413,6 @@ def test_shr_signed(a = 0, bits = 0, expected_error = Error.Okay):
return test("test_shr_signed", res, [a, bits], expected_error, expected_result)
def test_factorial(number = 0, expected_error = Error.Okay):
print("Factorial:", number)
args = [number]
try:
res = int_factorial(*args)
+3 -2
View File
@@ -1,6 +1,4 @@
//+ignore
package math_big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
@@ -9,6 +7,7 @@ package math_big
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.
*/
package math_big
import "core:fmt"
import "core:time"
@@ -24,6 +23,8 @@ Category :: enum {
sqr,
bitfield_extract,
rm_trials,
is_prime,
random_prime,
}
Event :: struct {
+133 -39
View File
@@ -1276,81 +1276,175 @@ as_raw_data :: proc(a: any) -> (value: rawptr, valid: bool) {
return
}
/*
not_equal :: proc(a, b: any) -> bool {
return !equal(a, b);
eq :: equal
ne :: not_equal
DEFAULT_EQUAL_MAX_RECURSION_LEVEL :: 32
not_equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_level := 0) -> bool {
return !equal(a, b, including_indirect_array_recursion, recursion_level)
}
equal :: proc(a, b: any) -> bool {
equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_level := 0) -> bool {
if a == nil && b == nil {
return true;
return true
}
if a.id != b.id {
return false;
return false
}
if a.data == b.data {
return true;
return true
}
including_indirect_array_recursion := including_indirect_array_recursion
if recursion_level >= DEFAULT_EQUAL_MAX_RECURSION_LEVEL {
including_indirect_array_recursion = false
}
t := type_info_of(a.id);
if .Comparable not_in t.flags {
return false;
t := type_info_of(a.id)
if .Comparable not_in t.flags && !including_indirect_array_recursion {
return false
}
if t.size == 0 {
return true;
return true
}
if .Simple_Compare in t.flags {
return mem.compare_byte_ptrs((^byte)(a.data), (^byte)(b.data), t.size) == 0;
return mem.compare_byte_ptrs((^byte)(a.data), (^byte)(b.data), t.size) == 0
}
t = runtime.type_info_core(t)
t = runtime.type_info_core(t);
#partial switch v in t.variant {
switch v in t.variant {
case Type_Info_Named:
unreachable()
case Type_Info_Tuple:
unreachable()
case Type_Info_Any:
if !including_indirect_array_recursion {
return false
}
va := (^any)(a.data)
vb := (^any)(b.data)
return equal(va, vb, including_indirect_array_recursion, recursion_level+1)
case Type_Info_Map:
return false
case Type_Info_Relative_Slice:
return false
case
Type_Info_Boolean,
Type_Info_Integer,
Type_Info_Rune,
Type_Info_Float,
Type_Info_Complex,
Type_Info_Quaternion,
Type_Info_Type_Id,
Type_Info_Pointer,
Type_Info_Multi_Pointer,
Type_Info_Procedure,
Type_Info_Bit_Set,
Type_Info_Enum,
Type_Info_Simd_Vector,
Type_Info_Relative_Pointer:
return mem.compare_byte_ptrs((^byte)(a.data), (^byte)(b.data), t.size) == 0
case Type_Info_String:
if v.is_cstring {
x := string((^cstring)(a.data)^);
y := string((^cstring)(b.data)^);
return x == y;
x := string((^cstring)(a.data)^)
y := string((^cstring)(b.data)^)
return x == y
} else {
x := (^string)(a.data)^;
y := (^string)(b.data)^;
return x == y;
x := (^string)(a.data)^
y := (^string)(b.data)^
return x == y
}
return true
case Type_Info_Array:
for i in 0..<v.count {
x := rawptr(uintptr(a.data) + uintptr(v.elem_size*i));
y := rawptr(uintptr(b.data) + uintptr(v.elem_size*i));
if !equal(any{x, v.elem.id}, any{y, v.elem.id}) {
return false;
x := rawptr(uintptr(a.data) + uintptr(v.elem_size*i))
y := rawptr(uintptr(b.data) + uintptr(v.elem_size*i))
if !equal(any{x, v.elem.id}, any{y, v.elem.id}, including_indirect_array_recursion, recursion_level) {
return false
}
}
return true
case Type_Info_Enumerated_Array:
for i in 0..<v.count {
x := rawptr(uintptr(a.data) + uintptr(v.elem_size*i));
y := rawptr(uintptr(b.data) + uintptr(v.elem_size*i));
if !equal(any{x, v.elem.id}, any{y, v.elem.id}) {
return false;
x := rawptr(uintptr(a.data) + uintptr(v.elem_size*i))
y := rawptr(uintptr(b.data) + uintptr(v.elem_size*i))
if !equal(any{x, v.elem.id}, any{y, v.elem.id}, including_indirect_array_recursion, recursion_level) {
return false
}
}
return true
case Type_Info_Struct:
if v.equal != nil {
return v.equal(a.data, b.data);
return v.equal(a.data, b.data)
} else {
for offset, i in v.offsets {
x := rawptr(uintptr(a.data) + offset);
y := rawptr(uintptr(b.data) + offset);
id := v.types[i].id;
if !equal(any{x, id}, any{y, id}) {
return false;
x := rawptr(uintptr(a.data) + offset)
y := rawptr(uintptr(b.data) + offset)
id := v.types[i].id
if !equal(any{x, id}, any{y, id}, including_indirect_array_recursion, recursion_level) {
return false
}
}
return true
}
case Type_Info_Union:
if v.equal != nil {
return v.equal(a.data, b.data)
}
return false
case Type_Info_Slice:
if !including_indirect_array_recursion {
return false
}
array_a := (^mem.Raw_Slice)(a.data)
array_b := (^mem.Raw_Slice)(b.data)
if array_a.len != array_b.len {
return false
}
if array_a.data == array_b.data {
return true
}
for i in 0..<array_a.len {
x := rawptr(uintptr(array_a.data) + uintptr(v.elem_size*i))
y := rawptr(uintptr(array_b.data) + uintptr(v.elem_size*i))
if !equal(any{x, v.elem.id}, any{y, v.elem.id}, including_indirect_array_recursion, recursion_level+1) {
return false
}
}
return true
case Type_Info_Dynamic_Array:
if !including_indirect_array_recursion {
return false
}
array_a := (^mem.Raw_Dynamic_Array)(a.data)
array_b := (^mem.Raw_Dynamic_Array)(b.data)
if array_a.len != array_b.len {
return false
}
if array_a.data == array_b.data {
return true
}
if .Simple_Compare in v.elem.flags {
return mem.compare_byte_ptrs((^byte)(array_a.data), (^byte)(array_b.data), array_a.len * v.elem.size) == 0
}
for i in 0..<array_a.len {
x := rawptr(uintptr(array_a.data) + uintptr(v.elem_size*i))
y := rawptr(uintptr(array_b.data) + uintptr(v.elem_size*i))
if !equal(any{x, v.elem.id}, any{y, v.elem.id}, including_indirect_array_recursion, recursion_level+1) {
return false
}
}
return true
}
return true;
runtime.print_typeid(a.id)
runtime.print_string("\n")
return true
}
*/
+24 -1
View File
@@ -1871,6 +1871,29 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
operand->type = alloc_type_simd_vector(count, elem);
break;
}
case BuiltinProc_is_package_imported: {
bool value = false;
if (!is_type_string(operand->type) && (operand->mode != Addressing_Constant)) {
error(ce->args[0], "Expected a constant string for '%.*s'", LIT(builtin_name));
} else if (operand->value.kind == ExactValue_String) {
String pkg_name = operand->value.value_string;
// TODO(bill): probably should have this be a `StringMap` eventually
for_array(i, c->info->packages.entries) {
AstPackage *pkg = c->info->packages.entries[i].value;
if (pkg->name == pkg_name) {
value = true;
break;
}
}
}
operand->mode = Addressing_Constant;
operand->type = t_untyped_bool;
operand->value = exact_value_bool(value);
break;
}
case BuiltinProc_soa_struct: {
Operand x = {};
@@ -2032,7 +2055,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
error(al.expr, "Alignment parameter to '%.*s' must be constant", LIT(builtin_name));
}
operand->type = t_u8_ptr;
operand->type = alloc_type_multi_pointer(t_u8);
operand->mode = Addressing_Value;
break;
}
+5 -2
View File
@@ -36,6 +36,8 @@ enum BuiltinProcId {
BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures
// "Intrinsics"
BuiltinProc_is_package_imported,
BuiltinProc_simd_vector,
BuiltinProc_soa_struct,
@@ -230,7 +232,6 @@ BuiltinProc__type_simple_boolean_end,
BuiltinProc__type_end,
BuiltinProc_COUNT,
};
gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
@@ -270,6 +271,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
// "Intrinsics"
{STR_LIT("is_package_imported"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_vector"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, // Type
{STR_LIT("soa_struct"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, // Type
@@ -459,6 +462,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_equal_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_hasher_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
};