Browse Source

big: Add constants.

Jeroen van Rijn 4 years ago
parent
commit
fc0a92f8ac
3 changed files with 133 additions and 59 deletions
  1. 33 27
      core/math/big/common.odin
  2. 32 7
      core/math/big/example.odin
  3. 68 25
      core/math/big/helpers.odin

+ 33 - 27
core/math/big/common.odin

@@ -26,10 +26,15 @@ when _LOW_MEMORY {
 	_DEFAULT_DIGIT_COUNT :: 32;
 	_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`.
 	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 {
 Int :: struct {
-	used:      int,
-	digit:     [dynamic]DIGIT,
-	sign:      Sign,
+	used:  int,
+	digit: [dynamic]DIGIT,
+	sign:  Sign,
+	flags: Flags,
 };
 };
 
 
 Flag :: enum u8 {
 Flag :: enum u8 {
@@ -76,35 +82,35 @@ Flags :: bit_set[Flag; u8];
 	Errors are a strict superset of runtime.Allocation_Error.
 	Errors are a strict superset of runtime.Allocation_Error.
 */
 */
 Error :: enum int {
 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{
 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 {
 Primality_Flag :: enum u8 {

+ 32 - 7
core/math/big/example.odin

@@ -39,6 +39,7 @@ _SQR_KARATSUBA_CUTOFF,
 _MUL_TOOM_CUTOFF,
 _MUL_TOOM_CUTOFF,
 _SQR_TOOM_CUTOFF,
 _SQR_TOOM_CUTOFF,
 );
 );
+
 }
 }
 
 
 print_timings :: proc() {
 print_timings :: proc() {
@@ -95,16 +96,15 @@ print :: proc(name: string, a: ^Int, base := i8(10), print_name := false, newlin
 	defer delete(as);
 	defer delete(as);
 	cb, _ := count_bits(a);
 	cb, _ := count_bits(a);
 	if print_name {
 	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 {
 	if err != nil {
 		fmt.printf("%v (error: %v | %v)", name, err, a);
 		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 {
 	if newline {
 		fmt.println();
 		fmt.println();
 	}
 	}
@@ -118,6 +118,31 @@ demo :: proc() {
 	a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{};
 	a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{};
 	defer destroy(a, b, c, d, e, f);
 	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.
 	factorial(a, 128); // Untimed warmup.
 
 
 	N :: 128;
 	N :: 128;
@@ -145,8 +170,8 @@ main :: proc() {
 	mem.tracking_allocator_init(&ta, context.allocator);
 	mem.tracking_allocator_init(&ta, context.allocator);
 	context.allocator = mem.tracking_allocator(&ta);
 	context.allocator = mem.tracking_allocator(&ta);
 
 
-	// print_configation();
 	demo();
 	demo();
+
 	print_timings();
 	print_timings();
 
 
 	if len(ta.allocation_map) > 0 {
 	if len(ta.allocation_map) > 0 {

+ 68 - 25
core/math/big/helpers.odin

@@ -56,7 +56,8 @@ set :: proc { int_set_from_integer, int_copy };
 /*
 /*
 	Copy one `Int` to another.
 	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 err = clear_if_uninitialized(src); err != nil { return err; }
 	/*
 	/*
 		If dest == src, do nothing
 		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`.
 		Grow `dest` to fit `src`.
 		If `dest` is not yet initialized, it will be using `allocator`.
 		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;
 		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] {
 	for v, i in src.digit[:src.used] {
 		dest.digit[i] = v;
 		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);
 	_zero_unused(dest);
 	return nil;
 	return nil;
 }
 }
@@ -120,7 +123,7 @@ int_abs :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error)
 	/*
 	/*
 		Copy `src` to `dest`
 		Copy `src` to `dest`
 	*/
 	*/
-	if err = copy(dest, src, allocator); err != nil {
+	if err = copy(dest, src, false, allocator); err != nil {
 		return err;
 		return err;
 	}
 	}
 
 
@@ -163,7 +166,7 @@ neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) {
 	/*
 	/*
 		Copy `src` to `dest`
 		Copy `src` to `dest`
 	*/
 	*/
-	if err = copy(dest, src, allocator); err != nil {
+	if err = copy(dest, src, false, allocator); err != nil {
 		return err;
 		return err;
 	}
 	}
 
 
@@ -337,12 +340,7 @@ zero  :: clear;
 	Set the `Int` to 1 and optionally shrink it to the minimum backing size.
 	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) {
 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, };
 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.
 	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) {
 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, };
 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) {
 power_of_two :: proc(a: ^Int, power: int) -> (err: Error) {
 	/*
 	/*
 		Check that `a` is usable.
 		Check that `a` is usable.
@@ -602,9 +617,21 @@ clear_if_uninitialized_multi :: proc(args: ..^Int) -> (err: Error) {
 	}
 	}
 	return err;
 	return err;
 }
 }
-
 clear_if_uninitialized :: proc {clear_if_uninitialized_single, clear_if_uninitialized_multi, };
 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.
 	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);
+}
+