aboutsummaryrefslogtreecommitdiff
path: root/core/math
diff options
context:
space:
mode:
authorJeroen van Rijn <Kelimion@users.noreply.github.com>2021-08-11 15:48:20 +0200
committerJeroen van Rijn <Kelimion@users.noreply.github.com>2021-08-11 20:59:54 +0200
commit12f9b6db63fbbc0fe57ce93145d8c55b8f1f0db8 (patch)
tree59143b21569ba52138fabf9ec3d91cdc2b770fba /core/math
parent851780b8f449847953c49e59d7ce401f2d53c779 (diff)
big: Add `int_to_bytes_{big, little}` + Python compatible variants.
Diffstat (limited to 'core/math')
-rw-r--r--core/math/big/build.bat12
-rw-r--r--core/math/big/common.odin4
-rw-r--r--core/math/big/example.odin63
-rw-r--r--core/math/big/helpers.odin144
-rw-r--r--core/math/big/internal.odin4
-rw-r--r--core/math/big/test.py1
6 files changed, 202 insertions, 26 deletions
diff --git a/core/math/big/build.bat b/core/math/big/build.bat
index e98ac7015..b1ff7303a 100644
--- a/core/math/big/build.bat
+++ b/core/math/big/build.bat
@@ -1,8 +1,8 @@
@echo off
-:odin run . -vet
+odin run . -vet
: -o:size
-:odin build . -build-mode:shared -show-timings -o:minimal -no-bounds-check && python test.py
-:odin build . -build-mode:shared -show-timings -o:size -no-bounds-check && python test.py
-:odin build . -build-mode:shared -show-timings -o:size && python test.py
-odin build . -build-mode:shared -show-timings -o:speed -no-bounds-check && python test.py
-:odin build . -build-mode:shared -show-timings -o:speed && python test.py \ No newline at end of file
+:odin build . -build-mode:shared -show-timings -o:minimal -no-bounds-check && python test.py -fast-tests
+:odin build . -build-mode:shared -show-timings -o:size -no-bounds-check && python test.py -fast-tests
+:odin build . -build-mode:shared -show-timings -o:size && python test.py -fast-tests
+:odin build . -build-mode:shared -show-timings -o:speed -no-bounds-check && python test.py -fast-tests
+:odin build . -build-mode:shared -show-timings -o:speed && python test.py -fast-tests \ No newline at end of file
diff --git a/core/math/big/common.odin b/core/math/big/common.odin
index bc9f189cf..5e2fe4d8c 100644
--- a/core/math/big/common.odin
+++ b/core/math/big/common.odin
@@ -114,7 +114,7 @@ Flags :: bit_set[Flag; u8];
Error :: enum int {
Okay = 0,
Out_Of_Memory = 1,
- // Invalid_Pointer = 2,
+ Invalid_Pointer = 2,
Invalid_Argument = 3,
Assignment_To_Immutable = 4,
@@ -130,7 +130,7 @@ Error :: enum int {
Error_String :: #partial [Error]string{
.Out_Of_Memory = "Out of memory",
- // .Invalid_Pointer = "Invalid pointer",
+ .Invalid_Pointer = "Invalid pointer",
.Invalid_Argument = "Invalid argument",
.Assignment_To_Immutable = "Assignment to immutable",
diff --git a/core/math/big/example.odin b/core/math/big/example.odin
index 5b9fbb481..552ba69f6 100644
--- a/core/math/big/example.odin
+++ b/core/math/big/example.odin
@@ -78,26 +78,61 @@ demo :: proc() {
defer destroy(a, b, c, d, e, f);
err: Error;
- bs: string;
- // if err = factorial(a, 850); err != nil { fmt.printf("factorial err: %v\n", err); return; }
+ foo := "2291194942392555914538479778530519876003906024854260006581638127590953";
+ if err = atoi(a, foo, 10); err != nil { return; }
+ // print("a: ", a, 10, true, true, true);
- foo := "54fc32611b510b653a608aaf41ca6d8e6a927520c87124d6bc9df29e29dcafbb0096428ca4a48905a3b6c9f02c56983d6d14711e7f5ce433feca8fcf382cdbd76cd627c45bc55423b8aea7f1bf638e81a3182ccd8b937467ca3916b37e67d0d4a1f3a0400360e8b02211a61071549525c4a1d4b32bfc83381e00d7d977bcf8f76e74d7a5a9532b75adfe67b6511cb377fa2828f3d9f989b3a532e2ded695796052ec3073267c11270fd393087a0ddf02f480f31149ee0c889811a8e43c25b906c9be5627bab8ba8eeba80ebdbfa0c6fe988542398d17f9df13887ddf5b109fc70033b325ec79340bd3e8d0e9217d0095fa1d5ed8750b479e2f85dd15bba5ce8ab9376d7fba183435d6d7b67e244358464efea9f5f3311efca81e36a5875e484cba3bce9536c87a038a16b85817812afde4ac8592af4f0a34ecb2e35ec160755ae17c9ad5ebee8f8a3687a06ce6a8385c161c275d1f1c1e10b64eeab5a56d76d5574c7a19a177c4018ec61f85f636697f177160452535de3a751c61f2156cd33a61e4b290b79429286397f6e58ee4936d4f1fd911b6511215fc9690e21b6f0f0c315341d5a192c40d6543c330a92c2161639f915ae50751ecdfcf6b5a489b4a874867a559e962c5464f69e50916c7621d8c7941357883e0ac5ba44dcf5cfccaa6a83ae035d66d9f00de1f115ceae4bb87133ab03d960b1c29ec801c2ac880b65d6ffd676b2c0a0f32282c47cfa977fa03ba94939cc6ec8666be46e8ce6d8a473089fd313dad75bdf99b024ad2b2bec434a2bde6102adee4c10fc53836fcade7e5eeef09a1dbeec913e535ba39b05035316e81723b93d465bd952bf1faada1564111836f022c482be52568f0c061bf0666db8c33e7a4f05c5792d0914b4ef7b654735bc1d363ca15af78ad32c0db6247b5721517393d47ce267f3a7e888642c7f595d7c8e84f680d766551bde0f9002f040a688973d3498c0ab4443b33652639018d33e0e9fe97831ba1bf7531b4d69f84e1a99cd58105756dd78ba9524ed4d236387ce9211ecab98048a728f5526c44372e44ed7a6853eeacda09d659119b0082cec1c1a6295fd9c6439f95dc60a9467e4296c0babb0138dae22ef64716fbaf0cbcd4c3047b976ae923f75fc6f4303e2ffe2792ff367d48b82b162182700bd08622a0b3304a6b0e1154ae66dc3a1607fc03026a1b81a7ec904aa98c201551baf5da48d78916ab5acb88df7bc57d7b4f1e96716dbfe56366c20cd28c2ae9d007c7c65c7fb4b6ccf108d6f23215f27913bb57ed13f9a75fb017ca5242a46ef2248da0b60ca1cb5cd80219367ed61082b3ca6c07ae581430c334de073e241993cc7458be8017c4d8c1a2b9d9d2e4e72fff048c2a70b2f57e8259a0dac6cdab9dd4d7bcf69b401d52a67a656d62730672fa3b05aa83fbc97a2362bb6c218d9c659dbfd64e20cc7f0977ba2c2ad695202fee68aca07698d3e4a9677d8ea69099d1ccb113ddb695017d6ce0da36fa3c8f3fd22ad400f53335e1e965d3f3323575927273987cb9e798b222e125c4290a4bf751b8e5329a6690b32bedf6f90786511d55da1dabe963cff83686f454b1b1fa28c46abc20c4b500d9ecad4b3f3c446fb74eebb596aea1c6f079b43f167f228cab3ceab8965c34d5b0c760221ca441b6d1d75b5c39d7443fc58933bc2cadb1c5d1b92ac70179677feef4ee63a8f8fb76eeb20e705b58f138867db80bfaf4e2edba0a7f68e56e4650b2d6e8fdf634d74f7ba8df527fd3c3a03a987e9b73d2e50aea3020251c06d5629dd32790bc36f02c638b757441a813c101f3d81eb6a4d1edd147ba9dbbc2d3c419fa11bda7f3602dda5dc2f9a19fc7306660d20ecb1da3bb87b8415e340f2a3d1e843bc19059734e2417f1783c7859f00c6801dc51acf068d57777de7d100ae77bca7614a69efe557d574abb2b79d5d90ba621aa2b6460cdce64012cad33bf374989044bba0d280c26b71a1a2d7abf7bd75009a71db9f488c4b3b58db217689bc355d68817f6153736f597e3586780175960edf4fded6513aa8c6154cee94e278e9967d0f256a4b14a2cd188e4800f146118a35633476c665edc7460658dc7877f107bcd3108d";
- if err = atoi(a, foo, 16); err != nil { return; }
- //print("a: ", a, 16, true, true, true);
- //fmt.println();
+ byte_length, _ := int_to_bytes_size(a);
- {
- SCOPED_TIMING(.sqr);
- if err = _private_int_sqr_toom(b, a); err != nil { fmt.printf("sqr err: %v\n", err); return; }
+ fmt.printf("byte_length(a): %v\n", byte_length);
+
+ buf := make([]u8, byte_length);
+ defer delete(buf);
+
+ err = int_to_bytes_big(a, buf);
+
+ python_big := []u8{
+ 84, 252, 50, 97, 27, 81, 11, 101, 58, 96, 138, 175, 65, 202, 109,
+ 142, 106, 146, 117, 32, 200, 113, 36, 214, 188, 157, 242, 158, 41,
+ };
+
+ if mem.compare(python_big, buf) == 0 {
+ fmt.printf("int_to_bytes_big: pass\n");
+ } else {
+ fmt.printf("int_to_bytes_big: fail | %v\n", buf);
+ }
+ python_little := []u8{
+ 41, 158, 242, 157, 188, 214, 36, 113, 200, 32, 117, 146, 106, 142, 109,
+ 202, 65, 175, 138, 96, 58, 101, 11, 81, 27, 97, 50, 252, 84,
+ };
+
+ err = int_to_bytes_little(a, buf);
+ if mem.compare(python_little, buf) == 0 {
+ fmt.printf("int_to_bytes_little: pass\n");
+ } else {
+ fmt.printf("int_to_bytes_little: fail | %v\n", buf);
}
- fmt.println();
- bs, err = itoa(b, 16);
- defer delete(bs);
+ _ = neg(b, a);
+
+ python_little_neg := []u8{
+ 215, 97, 13, 98, 67, 41, 219, 142, 55, 223, 138, 109, 149, 113, 146,
+ 53, 190, 80, 117, 159, 197, 154, 244, 174, 228, 158, 205, 3, 171,
+ };
+
+ byte_length, _ = int_to_bytes_size_python(b, true);
+
+ fmt.printf("byte_length(a): %v\n", byte_length);
+
+ buf2 := make([]u8, byte_length);
+ defer delete(buf2);
- if bs[:50] != "1C367982F3050A8A3C62A8A7906D165438B54B287AF3F15D36" {
- fmt.println("sqr failed");
+ err = int_to_bytes_little_python(b, buf, true);
+ if mem.compare(python_little_neg, buf) == 0 {
+ fmt.printf("int_to_bytes_little: pass\n");
+ } else {
+ fmt.printf("int_to_bytes_little: %v | %v\n", err, buf);
}
}
diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin
index 14c88be5f..058836df9 100644
--- a/core/math/big/helpers.odin
+++ b/core/math/big/helpers.odin
@@ -45,7 +45,7 @@ int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator :
return #force_inline internal_int_set_from_integer(dest, src, minimize);
}
-set :: proc { int_set_from_integer, int_copy };
+set :: proc { int_set_from_integer, int_copy, int_atoi, };
/*
Copy one `Int` to another.
@@ -464,6 +464,148 @@ clamp :: proc(a: ^Int, allocator := context.allocator) -> (err: Error) {
return nil;
}
+
+/*
+ Size binary representation
+*/
+int_to_bytes_size :: proc(a: ^Int, signed := false, allocator := context.allocator) -> (size_in_bytes: int, err: Error) {
+ assert_if_nil(a);
+ if err = #force_inline internal_clear_if_uninitialized(a, allocator); err != nil { return {}, err; }
+
+ size_in_bits := internal_count_bits(a);
+
+ size_in_bytes = (size_in_bits / 8);
+ size_in_bytes += 0 if size_in_bits % 8 == 0 else 1;
+ size_in_bytes += 1 if signed else 0;
+ return;
+}
+
+/*
+ Size binary representation, Python `._to_bytes(num_bytes, "endianness", signed=Bool)` compatible.
+*/
+int_to_bytes_size_python :: proc(a: ^Int, signed := false, allocator := context.allocator) -> (size_in_bytes: int, err: Error) {
+ assert_if_nil(a);
+ size_in_bytes, err = int_to_bytes_size(a, signed, allocator);
+
+ /*
+ Python uses a complement representation of negative numbers and doesn't add a prefix byte.
+ */
+ if signed {
+ size_in_bytes -= 1;
+ }
+ return;
+}
+
+/*
+ Return Little Endian binary representation of `a`, either signed or unsigned.
+ If `a` is negative and we ask for the default unsigned representation, we return abs(a).
+*/
+int_to_bytes_little :: proc(a: ^Int, buf: []u8, signed := false, allocator := context.allocator) -> (err: Error) {
+ assert_if_nil(a);
+ size_in_bytes: int;
+
+ if size_in_bytes, err = int_to_bytes_size(a, signed, allocator); err != nil { return err; }
+ if size_in_bytes > len(buf) { return .Buffer_Overflow; }
+
+ size_in_bits := internal_count_bits(a);
+ i := 0;
+ if signed {
+ i += 1;
+ buf[0] = 1 if a.sign == .Negative else 0;
+ }
+ for offset := 0; offset < size_in_bits; offset += 8 {
+ bits, _ := internal_int_bitfield_extract(a, offset, 8);
+ buf[i] = u8(bits & 255); i += 1;
+ }
+ return;
+}
+
+/*
+ Return Big Endian binary representation of `a`, either signed or unsigned.
+ If `a` is negative and we ask for the default unsigned representation, we return abs(a).
+*/
+int_to_bytes_big :: proc(a: ^Int, buf: []u8, signed := false, allocator := context.allocator) -> (err: Error) {
+ assert_if_nil(a);
+ size_in_bytes: int;
+
+ if size_in_bytes, err = int_to_bytes_size(a, signed, allocator); err != nil { return err; }
+ l := len(buf);
+ if size_in_bytes > l { return .Buffer_Overflow; }
+
+ size_in_bits := internal_count_bits(a);
+ i := l - 1;
+
+ if signed {
+ buf[0] = 1 if a.sign == .Negative else 0;
+ }
+ for offset := 0; offset < size_in_bits; offset += 8 {
+ bits, _ := internal_int_bitfield_extract(a, offset, 8);
+ buf[i] = u8(bits & 255); i -= 1;
+ }
+ return;
+}
+
+/*
+ Return Python 3.x compatible Little Endian binary representation of `a`, either signed or unsigned.
+ If `a` is negative when asking for an unsigned number, we return an error like Python does.
+*/
+int_to_bytes_little_python :: proc(a: ^Int, buf: []u8, signed := false, allocator := context.allocator) -> (err: Error) {
+ assert_if_nil(a);
+ size_in_bytes: int;
+
+ if a.sign == .Zero_or_Positive {
+ return int_to_bytes_little(a, buf, signed, allocator);
+ }
+ if a.sign == .Negative && !signed { return .Invalid_Argument; }
+
+ l := len(buf);
+ if size_in_bytes, err = int_to_bytes_size_python(a, signed, allocator); err != nil { return err; }
+ if size_in_bytes > l { return .Buffer_Overflow; }
+
+ t := &Int{};
+ defer destroy(t);
+ if err = complement(t, a, allocator); err != nil { return err; }
+
+ size_in_bits := internal_count_bits(t);
+ i := 0;
+ for offset := 0; offset < size_in_bits; offset += 8 {
+ bits, _ := internal_int_bitfield_extract(t, offset, 8);
+ buf[i] = 255 - u8(bits & 255); i += 1;
+ }
+ return;
+}
+
+/*
+ Return Python 3.x compatible Big Endian binary representation of `a`, either signed or unsigned.
+ If `a` is negative when asking for an unsigned number, we return an error like Python does.
+*/
+int_to_bytes_big_python :: proc(a: ^Int, buf: []u8, signed := false, allocator := context.allocator) -> (err: Error) {
+ assert_if_nil(a);
+ size_in_bytes: int;
+
+ if a.sign == .Zero_or_Positive {
+ return int_to_bytes_big(a, buf, signed, allocator);
+ }
+ if a.sign == .Negative && !signed { return .Invalid_Argument; }
+
+ l := len(buf);
+ if size_in_bytes, err = int_to_bytes_size_python(a, signed, allocator); err != nil { return err; }
+ if size_in_bytes > l { return .Buffer_Overflow; }
+
+ t := &Int{};
+ defer destroy(t);
+ if err = complement(t, a, allocator); err != nil { return err; }
+
+ size_in_bits := internal_count_bits(t);
+ i := l - 1;
+
+ for offset := 0; offset < size_in_bits; offset += 8 {
+ bits, _ := internal_int_bitfield_extract(a, offset, 8);
+ buf[i] = 255 - u8(bits & 255); i -= 1;
+ }
+ return;
+}
+
/*
Initialize constants.
*/
diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin
index fb4cd6572..695dca04d 100644
--- a/core/math/big/internal.odin
+++ b/core/math/big/internal.odin
@@ -1719,9 +1719,9 @@ internal_int_neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (er
/*
If `dest == src`, just fix `dest`'s sign.
*/
- sign := Sign.Zero_or_Positive;
+ sign := Sign.Negative;
if #force_inline internal_is_zero(src) || #force_inline internal_is_negative(src) {
- sign = .Negative;
+ sign = .Zero_or_Positive;
}
if dest == src {
dest.sign = sign;
diff --git a/core/math/big/test.py b/core/math/big/test.py
index b60698881..f9398389c 100644
--- a/core/math/big/test.py
+++ b/core/math/big/test.py
@@ -61,7 +61,6 @@ parser.add_argument(
timed_or_fast.add_argument(
"-fast-tests",
help = "Cut down on the number of iterations of each test",
- default = True,
action = "store_true",
)