mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-13 01:21:38 -07:00
Merge branch 'master' into optional-semicolons
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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() ---
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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,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
File diff suppressed because it is too large
Load Diff
+1032
-30
File diff suppressed because it is too large
Load Diff
+299
-2
@@ -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
@@ -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,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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user