Browse Source

bigint: `itoa` support for arbitrary precision if `is_power_of_two(radix)`

Jeroen van Rijn 4 years ago
parent
commit
5af85aed3d

+ 1 - 0
core/math/bigint/bigint.odin

@@ -111,6 +111,7 @@ _DIGIT_TYPE_BITS :: 8 * size_of(DIGIT);
 _WORD_TYPE_BITS  :: 8 * size_of(_WORD);
 _WORD_TYPE_BITS  :: 8 * size_of(_WORD);
 
 
 _DIGIT_BITS      :: _DIGIT_TYPE_BITS - 4;
 _DIGIT_BITS      :: _DIGIT_TYPE_BITS - 4;
+_WORD_BITS       :: 2 * _DIGIT_BITS;
 
 
 _MASK            :: (DIGIT(1) << DIGIT(_DIGIT_BITS)) - DIGIT(1);
 _MASK            :: (DIGIT(1) << DIGIT(_DIGIT_BITS)) - DIGIT(1);
 _DIGIT_MAX       :: _MASK;
 _DIGIT_MAX       :: _MASK;

+ 13 - 11
core/math/bigint/example.odin

@@ -46,37 +46,39 @@ demo :: proc() {
 	as, bs, cs: string;
 	as, bs, cs: string;
 	err:  Error;
 	err:  Error;
 
 
-	a, err = init(-512);
+	a, err = init();
+	a.digit[2] = 512;
+	a.used = 3;
 	defer destroy(a);
 	defer destroy(a);
-	as, err = itoa(a);
+	as, err = itoa(a, 16);
 	fmt.printf("a: %v, err: %v\n\n", as, err);
 	fmt.printf("a: %v, err: %v\n\n", as, err);
 	delete(as);
 	delete(as);
 
 
 	b, err = init(42);
 	b, err = init(42);
 	defer destroy(b);
 	defer destroy(b);
-	bs, err = itoa(b);
+	bs, err = itoa(b, 16);
 	fmt.printf("b: %s, err: %v\n\n", bs, err);
 	fmt.printf("b: %s, err: %v\n\n", bs, err);
 	delete(bs);
 	delete(bs);
 
 
 	c, err = init(-4);
 	c, err = init(-4);
 	defer destroy(c);
 	defer destroy(c);
-	cs, err = itoa(c);
+	cs, err = itoa(c, 16);
 	fmt.printf("c: %s, err: %v\n\n", cs, err);
 	fmt.printf("c: %s, err: %v\n\n", cs, err);
 	delete(cs);
 	delete(cs);
 
 
-	cstr: cstring;
-	defer delete(cstr);
+	// cstr: cstring;
+	// defer delete(cstr);
 
 
-	cstr, err = itoa_cstring(a);
-	fmt.printf("cstring: %v, err: %v\n\n", cstr, err);
+	// cstr, err = itoa_cstring(a);
+	// fmt.printf("cstring: %v, err: %v\n\n", cstr, err);
 
 
 	fmt.println("=== Add ===");
 	fmt.println("=== Add ===");
 	err = sub(c, a, b);
 	err = sub(c, a, b);
 
 
 	fmt.printf("Error: %v\n", err);
 	fmt.printf("Error: %v\n", err);
-	as, err = itoa(a, 8);
-	bs, err = itoa(b);
-	cs, err = itoa(c);
+	as, err = itoa(a, 16);
+	bs, err = itoa(b, 16);
+	cs, err = itoa(c, 16);
 	fmt.printf("a: %v, bits: %v\n", as, count_bits(a));
 	fmt.printf("a: %v, bits: %v\n", as, count_bits(a));
 	fmt.printf("b: %v, bits: %v\n", bs, count_bits(b));
 	fmt.printf("b: %v, bits: %v\n", bs, count_bits(b));
 	fmt.printf("c: %v, bits: %v\n", cs, count_bits(c));
 	fmt.printf("c: %v, bits: %v\n", cs, count_bits(c));

+ 33 - 0
core/math/bigint/helpers.odin

@@ -102,6 +102,39 @@ set_integer :: proc(a: ^Int, n: $T, minimize := false, loc := #caller_location)
 
 
 set :: proc{set_integer};
 set :: proc{set_integer};
 
 
+/*
+	Helpers to extract values from the `Int`.
+*/
+extract_bit :: proc(a: ^Int, bit_offset: int) -> (bit: DIGIT, err: Error) {
+	limb := bit_offset / _DIGIT_BITS;
+	if limb < 0 || limb >= a.used {
+		return 0, .Invalid_Input;
+	}
+
+	i := DIGIT(1 << DIGIT((bit_offset % _DIGIT_BITS)));
+
+	return 1 if ((a.digit[limb] & i) != 0) else 0, .OK;
+}
+
+extract_bits :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) {
+	if count > _WORD_BITS || count < 1 {
+		return 0, .Invalid_Input;
+	}
+
+	v: DIGIT;
+	e: Error;
+	for shift := 0; shift < count; shift += 1 {
+		o   := offset + shift;
+		v, e = extract_bit(a, o);
+		if e != .OK {
+			break;
+		}
+		res = res + _WORD(v) << uint(shift);
+	}
+
+	return res, e;
+}
+
 /*
 /*
 	Resize backing store.
 	Resize backing store.
 */
 */

+ 35 - 5
core/math/bigint/radix.odin

@@ -37,15 +37,16 @@ itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator
 	*/
 	*/
 	size: int;
 	size: int;
 	size, err = radix_size(a, radix, zero_terminate);
 	size, err = radix_size(a, radix, zero_terminate);
-
 	/*
 	/*
 		Exit if calculating the size returned an error.
 		Exit if calculating the size returned an error.
 	*/
 	*/
 	if err != .OK {
 	if err != .OK {
+		f := strings.clone(fallback(a), allocator);
 		if zero_terminate {
 		if zero_terminate {
-			return string(cstring("")), err;
+			c := strings.clone_to_cstring(f);
+			return string(c), err;
 		}
 		}
-		return "", err;
+		return f, err;
 	}
 	}
 
 
 	/*
 	/*
@@ -149,7 +150,6 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina
 			available -= 1;
 			available -= 1;
 			buffer[available] = 0;
 			buffer[available] = 0;
 		}
 		}
-
 		available -= 1;
 		available -= 1;
 		buffer[available] = RADIX_TABLE[a.digit[0]];
 		buffer[available] = RADIX_TABLE[a.digit[0]];
 
 
@@ -184,13 +184,40 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina
 		}
 		}
 		return len(buffer) - available, .OK;
 		return len(buffer) - available, .OK;
 	}
 	}
+	/*
+		At least 3 DIGITs are in use if we made it this far.
+	*/
 
 
 	/*
 	/*
 		Fast path for radixes that are a power of two.
 		Fast path for radixes that are a power of two.
 	*/
 	*/
 	if is_power_of_two(int(radix)) {
 	if is_power_of_two(int(radix)) {
+		if zero_terminate {
+			available -= 1;
+			buffer[available] = 0;
+		}
 
 
-		// return len(buffer), .OK;
+		mask  := _WORD(radix - 1);
+		shift := int(log_n(DIGIT(radix), 2));
+		count := int(count_bits(a));
+		digit: _WORD;
+
+		for offset := 0; offset < count; offset += 4 {
+			bits_to_get := int(min(count - offset, shift));
+			digit, err  := extract_bits(a, offset, bits_to_get);
+			if err != .OK {
+				return len(buffer) - available, .Invalid_Input;
+			}
+			available -= 1;
+			buffer[available] = RADIX_TABLE[digit];
+		}
+
+		if is_neg(a) {
+			available -= 1;
+			buffer[available] = '-';
+		}
+
+		return len(buffer) - available, .OK;
 	}
 	}
 
 
 	return -1, .Unimplemented;
 	return -1, .Unimplemented;
@@ -209,6 +236,9 @@ radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false) -> (size: int, e
 	}
 	}
 
 
  	if is_zero(a) {
  	if is_zero(a) {
+ 		if zero_terminate {
+ 			return 2, .OK;
+ 		}
  		return 1, .OK;
  		return 1, .OK;
  	}
  	}