mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-04 14:48:47 +00:00
Merge pull request #1130 from Kelimion/bigint
big: Remove `core:fmt` usage + Add a little demo to examples/demo.
This commit is contained in:
@@ -5,7 +5,7 @@
|
|||||||
set TEST_ARGS=-fast-tests
|
set TEST_ARGS=-fast-tests
|
||||||
:set TEST_ARGS=
|
: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: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: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 -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%
|
:odin build . -build-mode:shared -show-timings -o:speed -define:MATH_BIG_EXE=false && python test.py -fast-tests %TEST_ARGS%
|
||||||
@@ -174,6 +174,7 @@ Error :: enum int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Error_String :: #partial [Error]string{
|
Error_String :: #partial [Error]string{
|
||||||
|
.Okay = "Okay",
|
||||||
.Out_Of_Memory = "Out of memory",
|
.Out_Of_Memory = "Out of memory",
|
||||||
.Invalid_Pointer = "Invalid pointer",
|
.Invalid_Pointer = "Invalid pointer",
|
||||||
.Invalid_Argument = "Invalid argument",
|
.Invalid_Argument = "Invalid argument",
|
||||||
|
|||||||
@@ -1,152 +0,0 @@
|
|||||||
//+ignore
|
|
||||||
/*
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
package math_big
|
|
||||||
|
|
||||||
|
|
||||||
import "core:fmt"
|
|
||||||
import "core:mem"
|
|
||||||
|
|
||||||
print_configation :: proc() {
|
|
||||||
fmt.printf(
|
|
||||||
`
|
|
||||||
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
|
|
||||||
MUL_TOOM_CUTOFF %v
|
|
||||||
SQR_TOOM_CUTOFF %v
|
|
||||||
MAX_ITERATIONS_ROOT_N %v
|
|
||||||
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,
|
|
||||||
SQR_TOOM_CUTOFF,
|
|
||||||
MAX_ITERATIONS_ROOT_N,
|
|
||||||
FACTORIAL_MAX_N,
|
|
||||||
FACTORIAL_BINARY_SPLIT_CUTOFF,
|
|
||||||
FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS,
|
|
||||||
USE_MILLER_RABIN_ONLY,
|
|
||||||
MAX_ITERATIONS_RANDOM_PRIME,
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
print :: proc(name: string, a: ^Int, base := i8(10), print_name := true, newline := true, print_extra_info := false) {
|
|
||||||
assert_if_nil(a)
|
|
||||||
|
|
||||||
as, err := itoa(a, base)
|
|
||||||
defer delete(as)
|
|
||||||
|
|
||||||
cb := internal_count_bits(a)
|
|
||||||
if print_name {
|
|
||||||
fmt.printf("%v", name)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
fmt.printf("%v (error: %v | %v)", name, err, a)
|
|
||||||
}
|
|
||||||
fmt.printf("%v", as)
|
|
||||||
if print_extra_info {
|
|
||||||
fmt.printf(" (base: %v, bits: %v (digits: %v), flags: %v)", base, cb, a.used, a.flags)
|
|
||||||
}
|
|
||||||
if newline {
|
|
||||||
fmt.println()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// printf :: fmt.printf;
|
|
||||||
|
|
||||||
demo :: proc() {
|
|
||||||
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() {
|
|
||||||
ta := mem.Tracking_Allocator{}
|
|
||||||
mem.tracking_allocator_init(&ta, context.allocator)
|
|
||||||
context.allocator = mem.tracking_allocator(&ta)
|
|
||||||
|
|
||||||
demo()
|
|
||||||
|
|
||||||
print_configation()
|
|
||||||
|
|
||||||
print_timings()
|
|
||||||
|
|
||||||
if len(ta.allocation_map) > 0 {
|
|
||||||
for _, v in ta.allocation_map {
|
|
||||||
fmt.printf("Leaked %v bytes @ %v\n", v.size, v.location)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(ta.bad_free_array) > 0 {
|
|
||||||
fmt.println("Bad frees:")
|
|
||||||
for v in ta.bad_free_array {
|
|
||||||
fmt.println(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,8 +11,6 @@ package math_big
|
|||||||
import "core:intrinsics"
|
import "core:intrinsics"
|
||||||
import rnd "core:math/rand"
|
import rnd "core:math/rand"
|
||||||
|
|
||||||
// import "core:fmt"
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO: Int.flags and Constants like ONE, NAN, etc, are not yet properly handled everywhere.
|
TODO: Int.flags and Constants like ONE, NAN, etc, are not yet properly handled everywhere.
|
||||||
*/
|
*/
|
||||||
|
|||||||
+100
-24
@@ -9,8 +9,84 @@
|
|||||||
*/
|
*/
|
||||||
package math_big
|
package math_big
|
||||||
|
|
||||||
import "core:fmt"
|
|
||||||
import "core:time"
|
import "core:time"
|
||||||
|
import "core:runtime"
|
||||||
|
|
||||||
|
print_value :: proc(name: string, value: i64) {
|
||||||
|
runtime.print_string("\t")
|
||||||
|
runtime.print_string(name)
|
||||||
|
runtime.print_string(": ")
|
||||||
|
runtime.print_i64(value)
|
||||||
|
runtime.print_string("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
print_bool :: proc(name: string, value: bool) {
|
||||||
|
runtime.print_string("\t")
|
||||||
|
runtime.print_string(name)
|
||||||
|
if value {
|
||||||
|
runtime.print_string(": true\n")
|
||||||
|
} else {
|
||||||
|
runtime.print_string(": false\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print_configation :: proc() {
|
||||||
|
runtime.print_string("Configuration:\n")
|
||||||
|
print_value("_DIGIT_BITS ", _DIGIT_BITS)
|
||||||
|
print_bool ("MATH_BIG_SMALL_MEMORY ", _LOW_MEMORY)
|
||||||
|
print_value("_MIN_DIGIT_COUNT ", _MIN_DIGIT_COUNT)
|
||||||
|
print_value("_MAX_DIGIT_COUNT ", i64(_MAX_DIGIT_COUNT))
|
||||||
|
print_value("_DEFAULT_DIGIT_COUNT ", _DEFAULT_DIGIT_COUNT)
|
||||||
|
print_value("_MAX_COMBA ", _MAX_COMBA)
|
||||||
|
print_value("_WARRAY ", _WARRAY)
|
||||||
|
print_value("_TAB_SIZE ", _TAB_SIZE)
|
||||||
|
print_value("_MAX_WIN_SIZE ", _MAX_WIN_SIZE)
|
||||||
|
print_bool ("MATH_BIG_USE_LUCAS_SELFRIDGE_TEST ", MATH_BIG_USE_LUCAS_SELFRIDGE_TEST)
|
||||||
|
|
||||||
|
runtime.print_string("\nRuntime tunable:\n")
|
||||||
|
print_value("MUL_KARATSUBA_CUTOFF ", i64(MUL_KARATSUBA_CUTOFF))
|
||||||
|
print_value("SQR_KARATSUBA_CUTOFF ", i64(SQR_KARATSUBA_CUTOFF))
|
||||||
|
print_value("MUL_TOOM_CUTOFF ", i64(MUL_TOOM_CUTOFF))
|
||||||
|
print_value("SQR_TOOM_CUTOFF ", i64(SQR_TOOM_CUTOFF))
|
||||||
|
print_value("MAX_ITERATIONS_ROOT_N ", i64(MAX_ITERATIONS_ROOT_N))
|
||||||
|
print_value("FACTORIAL_MAX_N ", i64(FACTORIAL_MAX_N))
|
||||||
|
print_value("FACTORIAL_BINARY_SPLIT_CUTOFF ", i64(FACTORIAL_BINARY_SPLIT_CUTOFF))
|
||||||
|
print_value("FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS", i64(FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS))
|
||||||
|
print_value("FACTORIAL_BINARY_SPLIT_CUTOFF ", i64(FACTORIAL_BINARY_SPLIT_CUTOFF))
|
||||||
|
print_bool ("USE_MILLER_RABIN_ONLY ", USE_MILLER_RABIN_ONLY)
|
||||||
|
print_value("MAX_ITERATIONS_RANDOM_PRIME ", i64(MAX_ITERATIONS_RANDOM_PRIME))
|
||||||
|
}
|
||||||
|
|
||||||
|
print :: proc(name: string, a: ^Int, base := i8(10), print_name := true, newline := true, print_extra_info := false) {
|
||||||
|
assert_if_nil(a)
|
||||||
|
|
||||||
|
as, err := itoa(a, base)
|
||||||
|
defer delete(as)
|
||||||
|
|
||||||
|
cb := internal_count_bits(a)
|
||||||
|
if print_name {
|
||||||
|
runtime.print_string(name)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
runtime.print_string("(Error: ")
|
||||||
|
es := Error_String
|
||||||
|
runtime.print_string(es[err])
|
||||||
|
runtime.print_string(")")
|
||||||
|
}
|
||||||
|
runtime.print_string(as)
|
||||||
|
if print_extra_info {
|
||||||
|
runtime.print_string(" (base: ")
|
||||||
|
runtime.print_i64(i64(base))
|
||||||
|
runtime.print_string(", bits: ")
|
||||||
|
runtime.print_i64(i64(cb))
|
||||||
|
runtime.print_string(", digits: ")
|
||||||
|
runtime.print_i64(i64(a.used))
|
||||||
|
runtime.print_string(")")
|
||||||
|
}
|
||||||
|
if newline {
|
||||||
|
runtime.print_string("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Category :: enum {
|
Category :: enum {
|
||||||
itoa,
|
itoa,
|
||||||
@@ -35,32 +111,32 @@ Event :: struct {
|
|||||||
Timings := [Category]Event{}
|
Timings := [Category]Event{}
|
||||||
|
|
||||||
print_timings :: proc() {
|
print_timings :: proc() {
|
||||||
duration :: proc(d: time.Duration) -> (res: string) {
|
// duration :: proc(d: time.Duration) -> (res: string) {
|
||||||
switch {
|
// switch {
|
||||||
case d < time.Microsecond:
|
// case d < time.Microsecond:
|
||||||
return fmt.tprintf("%v ns", time.duration_nanoseconds(d))
|
// return fmt.tprintf("%v ns", time.duration_nanoseconds(d))
|
||||||
case d < time.Millisecond:
|
// case d < time.Millisecond:
|
||||||
return fmt.tprintf("%v µs", time.duration_microseconds(d))
|
// return fmt.tprintf("%v µs", time.duration_microseconds(d))
|
||||||
case:
|
// case:
|
||||||
return fmt.tprintf("%v ms", time.duration_milliseconds(d))
|
// return fmt.tprintf("%v ms", time.duration_milliseconds(d))
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
for v in Timings {
|
// for v in Timings {
|
||||||
if v.count > 0 {
|
// if v.count > 0 {
|
||||||
fmt.println("\nTimings:")
|
// fmt.println("\nTimings:")
|
||||||
break
|
// break
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
for v, i in Timings {
|
// for v, i in Timings {
|
||||||
if v.count > 0 {
|
// if v.count > 0 {
|
||||||
avg_ticks := time.Duration(f64(v.ticks) / f64(v.count))
|
// avg_ticks := time.Duration(f64(v.ticks) / f64(v.count))
|
||||||
avg_cycles := f64(v.cycles) / f64(v.count)
|
// avg_cycles := f64(v.cycles) / f64(v.count)
|
||||||
|
|
||||||
fmt.printf("\t%v: %s / %v cycles (avg), %s / %v cycles (total, %v calls)\n", i, duration(avg_ticks), avg_cycles, duration(v.ticks), v.cycles, v.count)
|
// fmt.printf("\t%v: %s / %v cycles (avg), %s / %v cycles (total, %v calls)\n", i, duration(avg_ticks), avg_cycles, duration(v.ticks), v.cycles, v.count)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
@(deferred_in_out=_SCOPE_END)
|
@(deferred_in_out=_SCOPE_END)
|
||||||
|
|||||||
+77
-1
@@ -8,7 +8,7 @@ import "core:time"
|
|||||||
import "core:reflect"
|
import "core:reflect"
|
||||||
import "core:runtime"
|
import "core:runtime"
|
||||||
import "core:intrinsics"
|
import "core:intrinsics"
|
||||||
|
import "core:math/big"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The Odin programming language is fast, concise, readable, pragmatic and open sourced.
|
The Odin programming language is fast, concise, readable, pragmatic and open sourced.
|
||||||
@@ -2127,6 +2127,81 @@ or_return_operator :: proc() {
|
|||||||
foo_2()
|
foo_2()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
arbitrary_precision_maths :: proc() {
|
||||||
|
fmt.println("\n# core:math/big")
|
||||||
|
|
||||||
|
print_bigint :: proc(name: string, a: ^big.Int, base := i8(10), print_name := true, newline := true, print_extra_info := true) {
|
||||||
|
big.assert_if_nil(a)
|
||||||
|
|
||||||
|
as, err := big.itoa(a, base)
|
||||||
|
defer delete(as)
|
||||||
|
|
||||||
|
cb := big.internal_count_bits(a)
|
||||||
|
if print_name {
|
||||||
|
fmt.printf(name)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
fmt.printf(" (Error: %v) ", err)
|
||||||
|
}
|
||||||
|
fmt.printf(as)
|
||||||
|
if print_extra_info {
|
||||||
|
fmt.printf(" (base: %v, bits: %v, digits: %v)", base, cb, a.used)
|
||||||
|
}
|
||||||
|
if newline {
|
||||||
|
fmt.println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a, b, c, d, e, f, res := &big.Int{}, &big.Int{}, &big.Int{}, &big.Int{}, &big.Int{}, &big.Int{}, &big.Int{}
|
||||||
|
defer big.destroy(a, b, c, d, e, f, res)
|
||||||
|
|
||||||
|
// How many bits should the random prime be?
|
||||||
|
bits := 64
|
||||||
|
// Number of Rabin-Miller trials, -1 for automatic.
|
||||||
|
trials := -1
|
||||||
|
|
||||||
|
// Default prime generation flags
|
||||||
|
flags := big.Primality_Flags{}
|
||||||
|
|
||||||
|
err := big.internal_random_prime(a, bits, trials, flags)
|
||||||
|
if err != nil {
|
||||||
|
fmt.printf("Error %v while generating random prime.\n", err)
|
||||||
|
} else {
|
||||||
|
print_bigint("Random Prime A: ", a, 10)
|
||||||
|
fmt.printf("Random number iterations until prime found: %v\n", big.RANDOM_PRIME_ITERATIONS_USED)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we want to pack this Int into a buffer of u32, how many do we need?
|
||||||
|
count := big.internal_int_pack_count(a, u32)
|
||||||
|
buf := make([]u32, count)
|
||||||
|
defer delete(buf)
|
||||||
|
|
||||||
|
written: int
|
||||||
|
written, err = big.internal_int_pack(a, buf)
|
||||||
|
fmt.printf("\nPacked into u32 buf: %v | err: %v | written: %v\n", buf, err, written)
|
||||||
|
|
||||||
|
// If we want to pack this Int into a buffer of bytes of which only the bottom 6 bits are used, how many do we need?
|
||||||
|
nails := 2
|
||||||
|
|
||||||
|
count = big.internal_int_pack_count(a, u8, nails)
|
||||||
|
byte_buf := make([]u8, count)
|
||||||
|
defer delete(byte_buf)
|
||||||
|
|
||||||
|
written, err = big.internal_int_pack(a, byte_buf, nails)
|
||||||
|
fmt.printf("\nPacked into buf of 6-bit bytes: %v | err: %v | written: %v\n", byte_buf, err, written)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Pick another random big Int, not necesssarily prime.
|
||||||
|
err = big.random(b, 2048)
|
||||||
|
print_bigint("\n2048 bit random number: ", b)
|
||||||
|
|
||||||
|
// Calculate GCD + LCM in one fell swoop
|
||||||
|
big.gcd_lcm(c, d, a, b)
|
||||||
|
|
||||||
|
print_bigint("\nGCD of random prime A and random number B: ", c)
|
||||||
|
print_bigint("\nLCM of random prime A and random number B (in base 36): ", d, 36)
|
||||||
|
}
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
when true {
|
when true {
|
||||||
@@ -2162,5 +2237,6 @@ main :: proc() {
|
|||||||
relative_data_types()
|
relative_data_types()
|
||||||
or_else_operator()
|
or_else_operator()
|
||||||
or_return_operator()
|
or_return_operator()
|
||||||
|
arbitrary_precision_maths()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user