-
Notifications
You must be signed in to change notification settings - Fork 374
feat: support for u128 #3913
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
feat: support for u128 #3913
Changes from all commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
5a68607
u128 noir struct
guipublic 3740f2d
add U128::new()
guipublic f5bd81b
u128 subtraction
guipublic 95bf0c4
handle multiplication
guipublic f9f3cec
add a safeguard for multiplication
guipublic fbbf4c6
Merge branch 'master' into gd/u128_add
guipublic ce68683
simple division
guipublic 915c078
Merge branch 'master' into gd/u128_add
guipublic 9198e65
Merge branch 'master' into gd/u128_add
guipublic 9f71ce1
fix the test
guipublic 7dc3218
add U128 to prelude
guipublic edeb94a
Merge branch 'master' into gd/u128_add
guipublic d2bdb60
use traits from ops
guipublic 4686c46
convert to integer
guipublic ed97153
chore: use standard trait definitions for `U128`
TomAFrench 4cd200b
Merge branch 'master' into gd/u128_add
TomAFrench b6fa04e
feat: implement `Ordering`
TomAFrench 14d588f
chore: make use of operator overloading in test
TomAFrench 70fb569
chore: more overloading
TomAFrench 0412ef0
chore: implement `Rem` on `U128`
TomAFrench 3daa3f6
chore: implement bitwise operations
TomAFrench b8ba80e
bit shifts for 128
guipublic a598d99
Merge branch 'master' into gd/u128_add
guipublic d2e0949
Merge branch 'master' into gd/u128_add
guipublic e6202e1
Revert u128 usage to U128
guipublic File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| use crate::collections::vec::Vec; | ||
| use crate::option::Option; | ||
| use crate::{print, println, assert_constant}; | ||
| use crate::uint128::U128; | ||
| use crate::cmp::{Eq, Ord}; | ||
| use crate::default::Default; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,292 @@ | ||
| use crate::ops::{Add, Sub, Mul, Div, Rem, BitOr, BitAnd, BitXor, Shl, Shr}; | ||
| use crate::cmp::{Eq, Ord, Ordering}; | ||
|
|
||
| global pow64 : Field = 18446744073709551616; //2^64; | ||
|
|
||
| struct U128 { | ||
| lo: Field, | ||
| hi: Field, | ||
| } | ||
|
|
||
| impl U128 { | ||
|
|
||
| pub fn from_u64s_le(lo: u64, hi: u64) -> U128 { | ||
| // in order to handle multiplication, we need to represent the product of two u64 without overflow | ||
| assert(crate::field::modulus_num_bits() as u32 > 128); | ||
| U128 { | ||
| lo: lo as Field, | ||
| hi: hi as Field, | ||
| } | ||
| } | ||
|
|
||
| pub fn from_u64s_be(hi: u64, lo: u64) -> U128 { | ||
| U128::from_u64s_le(lo,hi) | ||
| } | ||
|
|
||
| pub fn from_le_bytes(bytes: [u8; 16]) -> U128 { | ||
| let mut lo = 0; | ||
| let mut base = 1; | ||
| for i in 0..8 { | ||
| lo += (bytes[i] as Field)*base; | ||
| base *= 256; | ||
| } | ||
| let mut hi = 0; | ||
| base = 1; | ||
| for i in 8..16 { | ||
| hi += (bytes[i] as Field)*base; | ||
| base *= 256; | ||
| } | ||
| U128 { | ||
| lo, | ||
| hi, | ||
| } | ||
| } | ||
|
|
||
| pub fn to_le_bytes(self: Self) -> [u8; 16] { | ||
| let lo = self.lo.to_le_bytes(8); | ||
| let hi = self.hi.to_le_bytes(8); | ||
| let mut bytes = [0;16]; | ||
| for i in 0..8 { | ||
| bytes[i] = lo[i]; | ||
| bytes[i+8] = hi[i]; | ||
| } | ||
| bytes | ||
| } | ||
|
|
||
| pub fn from_hex<N>(hex: str<N>) -> U128 { | ||
| let N = N as u32; | ||
| let bytes = hex.as_bytes(); | ||
| // string must starts with "0x" | ||
| assert((bytes[0] == 48) & (bytes[1] == 120), "Invalid hexadecimal string"); | ||
| assert(N < 35, "Input does not fit into a U128"); | ||
|
|
||
| let mut lo = 0; | ||
| let mut hi = 0; | ||
| let mut base = 1; | ||
| if N <= 18 { | ||
| for i in 0..N-2 { | ||
| lo += U128::decode_ascii(bytes[N-i-1])*base; | ||
| base = base*16; | ||
| } | ||
| } else { | ||
| for i in 0..16 { | ||
| lo += U128::decode_ascii(bytes[N-i-1])*base; | ||
| base = base*16; | ||
| } | ||
| base = 1; | ||
| for i in 17..N-1 { | ||
| hi += U128::decode_ascii(bytes[N-i])*base; | ||
| base = base*16; | ||
| } | ||
| } | ||
| U128 { | ||
| lo: lo as Field, | ||
| hi: hi as Field, | ||
| } | ||
| } | ||
|
|
||
| fn decode_ascii(ascii: u8) -> Field { | ||
| if ascii < 58 { | ||
| ascii - 48 | ||
| } else { | ||
| if ascii < 71 { | ||
| ascii - 55 | ||
| } else { | ||
| ascii - 87 | ||
| } | ||
|
|
||
| } as Field | ||
| } | ||
|
|
||
| unconstrained fn unconstrained_div(self: Self, b: U128) -> (U128, U128) { | ||
| if self < b { | ||
| (U128::from_u64s_le(0, 0), self) | ||
| } else { | ||
| //TODO check if this can overflow? | ||
| let (q,r) = self.unconstrained_div(b * U128::from_u64s_le(2,0)); | ||
| let q_mul_2 = q * U128::from_u64s_le(2,0); | ||
| if r < b { | ||
| (q_mul_2, r) | ||
| } else { | ||
| (q_mul_2 + U128::from_u64s_le(1,0), r - b) | ||
| } | ||
|
|
||
| } | ||
| } | ||
|
|
||
| pub fn from_integer<T>(i: T) -> U128 { | ||
| let f = crate::as_field(i); | ||
| let lo = f as u64 as Field; | ||
| let hi = (f-lo) / pow64; | ||
| U128 { | ||
| lo, | ||
| hi, | ||
| } | ||
| } | ||
|
|
||
| pub fn to_integer<T>(self) -> T { | ||
| crate::from_field(self.lo+self.hi*pow64) | ||
| } | ||
|
|
||
| fn wrapping_mul(self: Self, b: U128) -> U128 { | ||
| let low = self.lo*b.lo; | ||
| let lo = low as u64 as Field; | ||
| let carry = (low - lo) / pow64; | ||
| let high = if crate::field::modulus_num_bits() as u32 > 196 { | ||
| (self.lo+self.hi)*(b.lo+b.hi) - low + carry | ||
| } else { | ||
| self.lo*b.hi + self.hi*b.lo + carry | ||
| }; | ||
| let hi = high as u64 as Field; | ||
| U128 { | ||
| lo, | ||
| hi, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl Add for U128 { | ||
| pub fn add(self: Self, b: U128) -> U128 { | ||
| let low = self.lo + b.lo; | ||
| let lo = low as u64 as Field; | ||
| let carry = (low - lo) / pow64; | ||
| let high = self.hi + b.hi + carry; | ||
| let hi = high as u64 as Field; | ||
| assert(hi == high, "attempt to add with overflow"); | ||
| U128 { | ||
| lo, | ||
| hi, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl Sub for U128 { | ||
| pub fn sub(self: Self, b: U128) -> U128 { | ||
| let low = pow64 + self.lo - b.lo; | ||
| let lo = low as u64 as Field; | ||
| let borrow = (low == lo) as Field; | ||
| let high = self.hi - b.hi - borrow; | ||
| let hi = high as u64 as Field; | ||
| assert(hi == high, "attempt to subtract with overflow"); | ||
| U128 { | ||
| lo, | ||
| hi, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl Mul for U128 { | ||
| pub fn mul(self: Self, b: U128) -> U128 { | ||
| assert(self.hi*b.hi == 0, "attempt to multiply with overflow"); | ||
| let low = self.lo*b.lo; | ||
| let lo = low as u64 as Field; | ||
| let carry = (low - lo) / pow64; | ||
| let high = if crate::field::modulus_num_bits() as u32 > 196 { | ||
| (self.lo+self.hi)*(b.lo+b.hi) - low + carry | ||
| } else { | ||
| self.lo*b.hi + self.hi*b.lo + carry | ||
| }; | ||
| let hi = high as u64 as Field; | ||
| assert(hi == high, "attempt to multiply with overflow"); | ||
| U128 { | ||
| lo, | ||
| hi, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl Div for U128 { | ||
| pub fn div(self: Self, b: U128) -> U128 { | ||
| let (q,r) = self.unconstrained_div(b); | ||
| let a = b * q + r; | ||
| assert_eq(self, a); | ||
| assert(r < b); | ||
| q | ||
| } | ||
| } | ||
|
|
||
| impl Rem for U128 { | ||
| pub fn rem(self: Self, b: U128) -> U128 { | ||
| let (q,r) = self.unconstrained_div(b); | ||
| let a = b * q + r; | ||
| assert_eq(self, a); | ||
| assert(r < b); | ||
| r | ||
| } | ||
| } | ||
|
|
||
| impl Eq for U128 { | ||
| pub fn eq(self: Self, b: U128) -> bool { | ||
| (self.lo == b.lo) & (self.hi == b.hi) | ||
| } | ||
| } | ||
|
|
||
| impl Ord for U128 { | ||
| fn cmp(self, other: Self) -> Ordering { | ||
| let hi_ordering = (self.hi as u64).cmp((other.hi as u64)); | ||
| let lo_ordering = (self.lo as u64).cmp((other.lo as u64)); | ||
|
|
||
| if hi_ordering == Ordering::equal() { | ||
| lo_ordering | ||
| } else { | ||
| hi_ordering | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl BitOr for U128 { | ||
| fn bitor(self, other: U128) -> U128 { | ||
| U128 { | ||
| lo: ((self.lo as u64) | (other.lo as u64)) as Field, | ||
| hi: ((self.hi as u64) | (other.hi as u64))as Field | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl BitAnd for U128 { | ||
| fn bitand(self, other: U128) -> U128 { | ||
| U128 { | ||
| lo: ((self.lo as u64) & (other.lo as u64)) as Field, | ||
| hi: ((self.hi as u64) & (other.hi as u64)) as Field | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl BitXor for U128 { | ||
| fn bitxor(self, other: U128) -> U128 { | ||
| U128 { | ||
| lo: ((self.lo as u64) ^ (other.lo as u64)) as Field, | ||
| hi: ((self.hi as u64) ^ (other.hi as u64)) as Field | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl Shl for U128 { | ||
| fn shl(self, other: U128) -> U128 { | ||
| assert(other < U128::from_u64s_le(128,0), "attempt to shift left with overflow"); | ||
| let exp_bits = other.lo.to_be_bits(7); | ||
|
|
||
| let mut r: Field = 2; | ||
| let mut y: Field = 1; | ||
| for i in 1..8 { | ||
| y = (exp_bits[7-i] as Field) * (r * y) + (1 - exp_bits[7-i] as Field) * y; | ||
| r *= r; | ||
| } | ||
| self.wrapping_mul(U128::from_integer(y)) | ||
| } | ||
| } | ||
|
|
||
| impl Shr for U128 { | ||
| fn shr(self, other: U128) -> U128 { | ||
| assert(other < U128::from_u64s_le(128,0), "attempt to shift right with overflow"); | ||
| let exp_bits = other.lo.to_be_bits(7); | ||
|
|
||
| let mut r: Field = 2; | ||
| let mut y: Field = 1; | ||
| for i in 1..8 { | ||
| y = (exp_bits[7-i] as Field) * (r * y) + (1 - exp_bits[7-i] as Field) * y; | ||
| r *= r; | ||
| } | ||
| self / U128::from_integer(y) | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| [package] | ||
| name = "u128" | ||
| type = "bin" | ||
| authors = [""] | ||
|
|
||
| [dependencies] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| x = "3" | ||
| y = "4" | ||
| z = "7" | ||
| hexa ="0x1f03a" | ||
| [big_int] | ||
| lo = 1 | ||
| hi = 2 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| use dep::std; | ||
|
|
||
| fn main(mut x: u32, y: u32, z: u32, big_int: U128, hexa: str<7>) { | ||
| let a = U128::from_u64s_le(x as u64, x as u64); | ||
| let b = U128::from_u64s_le(y as u64, x as u64); | ||
| let c = a + b; | ||
| assert(c.lo == z as Field); | ||
| assert(c.hi == 2 * x as Field); | ||
| assert(U128::from_hex(hexa).lo == 0x1f03a); | ||
| let t1 = U128::from_hex("0x9d9c7a87771f03a23783f9d9c7a8777"); | ||
| let t2 = U128::from_hex("0x45a26c708BFCF39041"); | ||
| let t = t1 + t2; | ||
| assert(t.lo == 0xc5e4b029996e17b8); | ||
| assert(t.hi == 0x09d9c7a87771f07f); | ||
| let t3 = U128::from_le_bytes(t.to_le_bytes()); | ||
| assert(t == t3); | ||
|
|
||
| let t4 = t - t2; | ||
| assert(t4 == t1); | ||
|
|
||
| let t5 = U128::from_u64s_le(0, 1); | ||
| let t6 = U128::from_u64s_le(1, 0); | ||
| assert((t5 - t6).hi == 0); | ||
|
|
||
| assert( | ||
| (U128::from_hex("0x71f03a23783f9d9c7a8777") * U128::from_hex("0x8BFCF39041")).hi | ||
| == U128::from_hex("0x3e4e0471b873470e247c824e61445537").hi | ||
| ); | ||
| let q = U128::from_hex("0x3e4e0471b873470e247c824e61445537") / U128::from_hex("0x8BFCF39041"); | ||
| assert(q == U128::from_hex("0x71f03a23783f9d9c7a8777")); | ||
|
|
||
| assert(big_int.hi == 2); | ||
|
|
||
| let mut small_int = U128::from_integer(x); | ||
| assert(small_int.lo == x as Field); | ||
| assert(x == small_int.to_integer()); | ||
| let shift = small_int << small_int; | ||
| assert(shift == U128::from_integer(x << x)); | ||
| assert(shift >> small_int == small_int); | ||
| assert(shift >> U128::from_integer(127) == U128::from_integer(0)); | ||
| assert(shift << U128::from_integer(127) == U128::from_integer(0)); | ||
|
|
||
| } | ||
|
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.