diff --git a/core/math/big/common.odin b/core/math/big/common.odin index cad6524e5..7943c0e4b 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -26,10 +26,15 @@ when _LOW_MEMORY { _DEFAULT_DIGIT_COUNT :: 32; } -_MUL_KARATSUBA_CUTOFF :: #config(MUL_KARATSUBA_CUTOFF, _DEFAULT_MUL_KARATSUBA_CUTOFF); -_SQR_KARATSUBA_CUTOFF :: #config(SQR_KARATSUBA_CUTOFF, _DEFAULT_SQR_KARATSUBA_CUTOFF); -_MUL_TOOM_CUTOFF :: #config(MUL_TOOM_CUTOFF, _DEFAULT_MUL_TOOM_CUTOFF); -_SQR_TOOM_CUTOFF :: #config(SQR_TOOM_CUTOFF, _DEFAULT_SQR_TOOM_CUTOFF); +/* + `initialize_constants` returns `#config(MUL_KARATSUBA_CUTOFF, _DEFAULT_MUL_KARATSUBA_CUTOFF)` + and we initialize this cutoff that way so that the procedure is used and called, + because it handles initializing the constants ONE, ZERO, MINUS_ONE, NAN and INF. +*/ +_MUL_KARATSUBA_CUTOFF := initialize_constants(); +_SQR_KARATSUBA_CUTOFF := #config(SQR_KARATSUBA_CUTOFF, _DEFAULT_SQR_KARATSUBA_CUTOFF); +_MUL_TOOM_CUTOFF := #config(MUL_TOOM_CUTOFF, _DEFAULT_MUL_TOOM_CUTOFF); +_SQR_TOOM_CUTOFF := #config(SQR_TOOM_CUTOFF, _DEFAULT_SQR_TOOM_CUTOFF); /* These defaults were tuned on an AMD A8-6600K (64-bit) using libTomMath's `make tune`. @@ -59,9 +64,10 @@ Sign :: enum u8 { }; Int :: struct { - used: int, - digit: [dynamic]DIGIT, - sign: Sign, + used: int, + digit: [dynamic]DIGIT, + sign: Sign, + flags: Flags, }; Flag :: enum u8 { @@ -76,35 +82,35 @@ Flags :: bit_set[Flag; u8]; Errors are a strict superset of runtime.Allocation_Error. */ Error :: enum int { - Out_Of_Memory = 1, - Invalid_Pointer = 2, - Invalid_Argument = 3, + Out_Of_Memory = 1, + Invalid_Pointer = 2, + Invalid_Argument = 3, - Unknown_Error = 4, - Max_Iterations_Reached = 5, - Buffer_Overflow = 6, - Integer_Overflow = 7, + Assignment_To_Immutable = 4, + Max_Iterations_Reached = 5, + Buffer_Overflow = 6, + Integer_Overflow = 7, - Division_by_Zero = 8, - Math_Domain_Error = 9, + Division_by_Zero = 8, + Math_Domain_Error = 9, - Unimplemented = 127, + Unimplemented = 127, }; Error_String :: #partial [Error]string{ - .Out_Of_Memory = "Out of memory", - .Invalid_Pointer = "Invalid pointer", - .Invalid_Argument = "Invalid argument", + .Out_Of_Memory = "Out of memory", + .Invalid_Pointer = "Invalid pointer", + .Invalid_Argument = "Invalid argument", - .Unknown_Error = "Unknown error", - .Max_Iterations_Reached = "Max iterations reached", - .Buffer_Overflow = "Buffer overflow", - .Integer_Overflow = "Integer overflow", + .Assignment_To_Immutable = "Assignment to immutable", + .Max_Iterations_Reached = "Max iterations reached", + .Buffer_Overflow = "Buffer overflow", + .Integer_Overflow = "Integer overflow", - .Division_by_Zero = "Division by zero", - .Math_Domain_Error = "Math domain error", + .Division_by_Zero = "Division by zero", + .Math_Domain_Error = "Math domain error", - .Unimplemented = "Unimplemented", + .Unimplemented = "Unimplemented", }; Primality_Flag :: enum u8 { diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 7dc6329c6..96d036570 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -39,6 +39,7 @@ _SQR_KARATSUBA_CUTOFF, _MUL_TOOM_CUTOFF, _SQR_TOOM_CUTOFF, ); + } print_timings :: proc() { @@ -95,16 +96,15 @@ print :: proc(name: string, a: ^Int, base := i8(10), print_name := false, newlin defer delete(as); cb, _ := count_bits(a); if print_name { - fmt.printf("%v ", name); - } - if print_extra_info { - fmt.printf("(base: %v, bits used: %v): %v", base, cb, as); - } else { - fmt.printf("%v", as); + 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 used: %v, flags: %v)", base, cb, a.flags); + } if newline { fmt.println(); } @@ -118,6 +118,31 @@ demo :: proc() { a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(a, b, c, d, e, f); + fmt.println(); + print(" ONE: ", ONE, 10, true, true, true); + fmt.println(); + + one(a); + print(" one: ", a, 10, true, true, true); + fmt.println(); + + minus_one(a); + print("-one: ", a, 10, true, true, true); + fmt.println(); + + nan(a); + print(" nan: ", a, 10, true, true, true); + fmt.println(); + + inf(a); + print(" inf: ", a, 10, true, true, true); + fmt.println(); + + minus_inf(a); + print("-inf: ", a, 10, true, true, true); + fmt.println(); + + factorial(a, 128); // Untimed warmup. N :: 128; @@ -145,8 +170,8 @@ main :: proc() { mem.tracking_allocator_init(&ta, context.allocator); context.allocator = mem.tracking_allocator(&ta); - // print_configation(); demo(); + print_timings(); if len(ta.allocation_map) > 0 { diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index c14690279..d4b5d0220 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -56,7 +56,8 @@ set :: proc { int_set_from_integer, int_copy }; /* Copy one `Int` to another. */ -int_copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { +int_copy :: proc(dest, src: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { + if err = error_if_immutable(dest); err != nil { return err; } if err = clear_if_uninitialized(src); err != nil { return err; } /* If dest == src, do nothing @@ -68,7 +69,7 @@ int_copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error Grow `dest` to fit `src`. If `dest` is not yet initialized, it will be using `allocator`. */ - if err = grow(dest, src.used, false, allocator); err != nil { + if err = grow(dest, src.used, minimize, allocator); err != nil { return err; } @@ -78,8 +79,10 @@ int_copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error for v, i in src.digit[:src.used] { dest.digit[i] = v; } - dest.used = src.used; - dest.sign = src.sign; + dest.used = src.used; + dest.sign = src.sign; + dest.flags = src.flags &~ {.Immutable}; + _zero_unused(dest); return nil; } @@ -120,7 +123,7 @@ int_abs :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) /* Copy `src` to `dest` */ - if err = copy(dest, src, allocator); err != nil { + if err = copy(dest, src, false, allocator); err != nil { return err; } @@ -163,7 +166,7 @@ neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { /* Copy `src` to `dest` */ - if err = copy(dest, src, allocator); err != nil { + if err = copy(dest, src, false, allocator); err != nil { return err; } @@ -337,12 +340,7 @@ zero :: clear; Set the `Int` to 1 and optionally shrink it to the minimum backing size. */ int_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - if err = clear(a, minimize, allocator); err != nil { return err; } - - a.used = 1; - a.digit[0] = 1; - a.sign = .Zero_or_Positive; - return nil; + return copy(a, ONE, minimize, allocator); } one :: proc { int_one, }; @@ -350,17 +348,34 @@ one :: proc { int_one, }; Set the `Int` to -1 and optionally shrink it to the minimum backing size. */ int_minus_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - if err = clear(a, minimize, allocator); err != nil { - return err; - } - - a.used = 1; - a.digit[0] = 1; - a.sign = .Negative; - return nil; + return copy(a, MINUS_ONE, minimize, allocator); } minus_one :: proc { int_minus_one, }; +/* + Set the `Int` to Inf and optionally shrink it to the minimum backing size. +*/ +int_inf :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { + return copy(a, INF, minimize, allocator); +} +inf :: proc { int_inf, }; + +/* + Set the `Int` to -Inf and optionally shrink it to the minimum backing size. +*/ +int_minus_inf :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { + return copy(a, MINUS_INF, minimize, allocator); +} +minus_inf :: proc { int_inf, }; + +/* + Set the `Int` to NaN and optionally shrink it to the minimum backing size. +*/ +int_nan :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { + return copy(a, NAN, minimize, allocator); +} +nan :: proc { int_nan, }; + power_of_two :: proc(a: ^Int, power: int) -> (err: Error) { /* Check that `a` is usable. @@ -602,9 +617,21 @@ clear_if_uninitialized_multi :: proc(args: ..^Int) -> (err: Error) { } return err; } - clear_if_uninitialized :: proc {clear_if_uninitialized_single, clear_if_uninitialized_multi, }; +error_if_immutable_single :: proc(arg: ^Int) -> (err: Error) { + if arg != nil && .Immutable in arg.flags { return .Assignment_To_Immutable; } + return nil; +} + +error_if_immutable_multi :: proc(args: ..^Int) -> (err: Error) { + for i in args { + if i != nil && .Immutable in i.flags { return .Assignment_To_Immutable; } + } + return nil; +} +error_if_immutable :: proc {error_if_immutable_single, error_if_immutable_multi, }; + /* Allocates several `Int`s at once. */ @@ -655,7 +682,23 @@ clamp :: proc(a: ^Int) -> (err: Error) { } -_STATIC_ZERO := &Int{ - used = 0, - sign = .Zero_or_Positive, -}; +/* + Initialize constants. +*/ +ONE, ZERO, MINUS_ONE, INF, MINUS_INF, NAN := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; + +initialize_constants :: proc() -> (res: int) { + set( ZERO, 0); ZERO.flags = {.Immutable}; + set( ONE, 1); ONE.flags = {.Immutable}; + set(MINUS_ONE, -1); MINUS_ONE.flags = {.Immutable}; + set( INF, 0); INF.flags = {.Immutable, .Inf}; + set( INF, 0); MINUS_INF.flags = {.Immutable, .Inf}; MINUS_INF.sign = .Negative; + set( NAN, 0); NAN.flags = {.Immutable, .NaN}; + + return #config(MUL_KARATSUBA_CUTOFF, _DEFAULT_MUL_KARATSUBA_CUTOFF); +} + +destroy_constants :: proc() { + destroy(ONE, ZERO, MINUS_ONE, INF, NAN); +} +