diff options
| author | Jeroen van Rijn <Kelimion@users.noreply.github.com> | 2021-08-11 15:48:20 +0200 |
|---|---|---|
| committer | Jeroen van Rijn <Kelimion@users.noreply.github.com> | 2021-08-11 20:59:54 +0200 |
| commit | 12f9b6db63fbbc0fe57ce93145d8c55b8f1f0db8 (patch) | |
| tree | 59143b21569ba52138fabf9ec3d91cdc2b770fba | |
| parent | 851780b8f449847953c49e59d7ce401f2d53c779 (diff) | |
big: Add `int_to_bytes_{big, little}` + Python compatible variants.
| -rw-r--r-- | core/math/big/build.bat | 12 | ||||
| -rw-r--r-- | core/math/big/common.odin | 4 | ||||
| -rw-r--r-- | core/math/big/example.odin | 63 | ||||
| -rw-r--r-- | core/math/big/helpers.odin | 144 | ||||
| -rw-r--r-- | core/math/big/internal.odin | 4 | ||||
| -rw-r--r-- | core/math/big/test.py | 1 |
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",
)
|