Merge pull request #1130 from Kelimion/bigint

big: Remove `core:fmt` usage + Add a little demo to examples/demo.
This commit is contained in:
Jeroen van Rijn
2021-09-06 23:35:57 +02:00
committed by GitHub
6 changed files with 179 additions and 180 deletions

View File

@@ -5,7 +5,7 @@
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%

View File

@@ -174,6 +174,7 @@ Error :: enum int {
}
Error_String :: #partial [Error]string{
.Okay = "Okay",
.Out_Of_Memory = "Out of memory",
.Invalid_Pointer = "Invalid pointer",
.Invalid_Argument = "Invalid argument",

View File

@@ -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)
}
}
}

View File

@@ -11,8 +11,6 @@ package math_big
import "core:intrinsics"
import rnd "core:math/rand"
// import "core:fmt"
/*
TODO: Int.flags and Constants like ONE, NAN, etc, are not yet properly handled everywhere.
*/

View File

@@ -9,8 +9,84 @@
*/
package math_big
import "core:fmt"
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 {
itoa,
@@ -35,32 +111,32 @@ Event :: struct {
Timings := [Category]Event{}
print_timings :: proc() {
duration :: proc(d: time.Duration) -> (res: string) {
switch {
case d < time.Microsecond:
return fmt.tprintf("%v ns", time.duration_nanoseconds(d))
case d < time.Millisecond:
return fmt.tprintf("%v µs", time.duration_microseconds(d))
case:
return fmt.tprintf("%v ms", time.duration_milliseconds(d))
}
}
// duration :: proc(d: time.Duration) -> (res: string) {
// switch {
// case d < time.Microsecond:
// return fmt.tprintf("%v ns", time.duration_nanoseconds(d))
// case d < time.Millisecond:
// return fmt.tprintf("%v µs", time.duration_microseconds(d))
// case:
// return fmt.tprintf("%v ms", time.duration_milliseconds(d))
// }
// }
for v in Timings {
if v.count > 0 {
fmt.println("\nTimings:")
break
}
}
// for v in Timings {
// if v.count > 0 {
// fmt.println("\nTimings:")
// break
// }
// }
for v, i in Timings {
if v.count > 0 {
avg_ticks := time.Duration(f64(v.ticks) / f64(v.count))
avg_cycles := f64(v.cycles) / f64(v.count)
// for v, i in Timings {
// if v.count > 0 {
// avg_ticks := time.Duration(f64(v.ticks) / 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)

View File

@@ -8,7 +8,7 @@ import "core:time"
import "core:reflect"
import "core:runtime"
import "core:intrinsics"
import "core:math/big"
/*
The Odin programming language is fast, concise, readable, pragmatic and open sourced.
@@ -2127,6 +2127,81 @@ or_return_operator :: proc() {
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() {
when true {
@@ -2162,5 +2237,6 @@ main :: proc() {
relative_data_types()
or_else_operator()
or_return_operator()
arbitrary_precision_maths()
}
}