diff --git a/docs/docs/noir/concepts/data_types/integers.md b/docs/docs/noir/concepts/data_types/integers.md index b8a5d498029..ff3fafa1f90 100644 --- a/docs/docs/noir/concepts/data_types/integers.md +++ b/docs/docs/noir/concepts/data_types/integers.md @@ -58,54 +58,6 @@ fn main(x: i16, y: i16) { Modulo operation is defined for negative integers thanks to integer division, so that the equality `x = (x/y)*y + (x%y)` holds. -## 128 bits Unsigned Integers - -The built-in structure `U128` allows you to use 128-bit unsigned integers almost like a native integer type. However, there are some differences to keep in mind: -- You cannot cast between a native integer and `U128` -- There is a higher performance cost when using `U128`, compared to a native type. - -Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. `from_integer` also accepts the `Field` type as input. - -```rust -fn main() { - let x = U128::from_integer(23); - let y = U128::from_hex("0x7"); - let z = x + y; - assert(z.to_integer() == 30); -} -``` - -`U128` is implemented with two 64 bits limbs, representing the low and high bits, which explains the performance cost. You should expect `U128` to be twice more costly for addition and four times more costly for multiplication. -You can construct a U128 from its limbs: -```rust -fn main(x: u64, y: u64) { - let z = U128::from_u64s_be(x,y); - assert(z.hi == x as Field); - assert(z.lo == y as Field); -} -``` - -Note that the limbs are stored as Field elements in order to avoid unnecessary conversions. -Apart from this, most operations will work as usual: - -```rust -fn main(x: U128, y: U128) { - // multiplication - let c = x * y; - // addition and subtraction - let c = c - x + y; - // division - let c = x / y; - // bit operation; - let c = x & y | y; - // bit shift - let c = x << y; - // comparisons; - let c = x < y; - let c = x == y; -} -``` - ## Overflows Computations that exceed the type boundaries will result in overflow errors. This happens with both signed and unsigned integers. For example, attempting to prove: diff --git a/docs/docs/noir/standard_library/traits.md b/docs/docs/noir/standard_library/traits.md index e6f6f80ff03..ed923c0707a 100644 --- a/docs/docs/noir/standard_library/traits.md +++ b/docs/docs/noir/standard_library/traits.md @@ -71,7 +71,7 @@ As a general rule of thumb, `From` may be implemented in the [situations where i - The conversion is *infallible*: Noir does not provide an equivalent to Rust's `TryFrom`, if the conversion can fail then provide a named method instead. - The conversion is *lossless*: semantically, it should not lose or discard information. For example, `u32: From` can losslessly convert any `u16` into a valid `u32` such that the original `u16` can be recovered. On the other hand, `u16: From` should not be implemented as `2**16` is a `u32` which cannot be losslessly converted into a `u16`. - The conversion is *value-preserving*: the conceptual kind and meaning of the resulting value is the same, even though the Noir type and technical representation might be different. While it's possible to infallibly and losslessly convert a `u8` into a `str<2>` hex representation, `4u8` and `"04"` are too different for `str<2>: From` to be implemented. -- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `U128: From<[u8; 16]>`, the methods `U128::from_le_bytes` and `U128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `U128` from the same byte array. +- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `u128: From<[u8; 16]>`, the methods `u128::from_le_bytes` and `u128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `u128` from the same byte array. One additional recommendation specific to Noir is: - The conversion is *efficient*: it's relatively cheap to convert between the two types. Due to being a ZK DSL, it's more important to avoid unnecessary computation compared to Rust. If the implementation of `From` would encourage users to perform unnecessary conversion, resulting in additional proving time, then it may be preferable to expose functionality such that this conversion may be avoided. diff --git a/noir_stdlib/src/hash/mod.nr b/noir_stdlib/src/hash/mod.nr index 1ded89ec80d..35a811c4570 100644 --- a/noir_stdlib/src/hash/mod.nr +++ b/noir_stdlib/src/hash/mod.nr @@ -9,7 +9,6 @@ use crate::embedded_curve_ops::{ EmbeddedCurvePoint, EmbeddedCurveScalar, multi_scalar_mul, multi_scalar_mul_array_return, }; use crate::meta::derive_via; -use crate::uint128::U128; // Kept for backwards compatibility pub use sha256::{digest, sha256, sha256_compression, sha256_var}; @@ -302,16 +301,6 @@ impl Hash for () { {} } -impl Hash for U128 { - fn hash(self, state: &mut H) - where - H: Hasher, - { - H::write(state, self.lo as Field); - H::write(state, self.hi as Field); - } -} - impl Hash for [T; N] where T: Hash, diff --git a/noir_stdlib/src/lib.nr b/noir_stdlib/src/lib.nr index d5c360792d9..8a8a4abd0ef 100644 --- a/noir_stdlib/src/lib.nr +++ b/noir_stdlib/src/lib.nr @@ -19,7 +19,6 @@ pub mod cmp; pub mod ops; pub mod default; pub mod prelude; -pub mod uint128; pub mod runtime; pub mod meta; pub mod append; @@ -121,8 +120,46 @@ where pub fn as_witness(x: Field) {} mod tests { + use super::wrapping_mul; + #[test(should_fail_with = "custom message")] fn test_static_assert_custom_message() { super::static_assert(1 == 2, "custom message"); } + + #[test(should_fail)] + fn test_wrapping_mul() { + // This currently fails. + // See: https://github.com/noir-lang/noir/issues/7528 + let zero: u128 = 0; + let one: u128 = 1; + let two_pow_64: u128 = 0x10000000000000000; + let u128_max: u128 = 0xffffffffffffffffffffffffffffffff; + + // 1*0==0 + assert_eq(zero, wrapping_mul(zero, one)); + + // 0*1==0 + assert_eq(zero, wrapping_mul(one, zero)); + + // 1*1==1 + assert_eq(one, wrapping_mul(one, one)); + + // 0 * ( 1 << 64 ) == 0 + assert_eq(zero, wrapping_mul(zero, two_pow_64)); + + // ( 1 << 64 ) * 0 == 0 + assert_eq(zero, wrapping_mul(two_pow_64, zero)); + + // 1 * ( 1 << 64 ) == 1 << 64 + assert_eq(two_pow_64, wrapping_mul(two_pow_64, one)); + + // ( 1 << 64 ) * 1 == 1 << 64 + assert_eq(two_pow_64, wrapping_mul(one, two_pow_64)); + + // ( 1 << 64 ) * ( 1 << 64 ) == 1 << 64 + assert_eq(zero, wrapping_mul(two_pow_64, two_pow_64)); + // -1 * -1 == 1 + assert_eq(one, wrapping_mul(u128_max, u128_max)); + } } diff --git a/noir_stdlib/src/prelude.nr b/noir_stdlib/src/prelude.nr index a4a6c35b615..7aa60456b6d 100644 --- a/noir_stdlib/src/prelude.nr +++ b/noir_stdlib/src/prelude.nr @@ -7,4 +7,3 @@ pub use crate::default::Default; pub use crate::meta::{derive, derive_via}; pub use crate::option::Option; pub use crate::panic::panic; -pub use crate::uint128::U128; diff --git a/noir_stdlib/src/uint128.nr b/noir_stdlib/src/uint128.nr deleted file mode 100644 index f41958e0e30..00000000000 --- a/noir_stdlib/src/uint128.nr +++ /dev/null @@ -1,572 +0,0 @@ -use crate::cmp::{Eq, Ord, Ordering}; -use crate::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Not, Rem, Shl, Shr, Sub}; -use crate::static_assert; -use super::{convert::AsPrimitive, default::Default}; - -global pow64: Field = 18446744073709551616; //2^64; -global pow63: Field = 9223372036854775808; // 2^63; -pub struct U128 { - pub(crate) lo: Field, - pub(crate) 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 zero() -> U128 { - U128 { lo: 0, hi: 0 } - } - - pub fn one() -> U128 { - U128 { lo: 1, hi: 0 } - } - 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_be_bytes(self: Self) -> [u8; 16] { - let lo: [u8; 8] = self.lo.to_be_bytes(); - let hi: [u8; 8] = self.hi.to_be_bytes(); - let mut bytes = [0; 16]; - for i in 0..8 { - bytes[i] = hi[i]; - bytes[i + 8] = lo[i]; - } - bytes - } - - pub fn to_le_bytes(self: Self) -> [u8; 16] { - let lo: [u8; 8] = self.lo.to_le_bytes(); - let hi: [u8; 8] = self.hi.to_le_bytes(); - let mut bytes = [0; 16]; - for i in 0..8 { - bytes[i] = lo[i]; - bytes[i + 8] = hi[i]; - } - bytes - } - - pub fn from_hex(hex: str) -> U128 { - let bytes = hex.as_bytes(); - // string must starts with "0x" - assert((bytes[0] == 48) & (bytes[1] == 120), "Invalid hexadecimal string"); - static_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 } - } - - unconstrained fn unconstrained_check_is_upper_ascii(ascii: u8) -> bool { - ((ascii >= 65) & (ascii <= 90)) // Between 'A' and 'Z' - } - - pub(crate) fn decode_ascii(ascii: u8) -> Field { - ( - if ascii < 58 { - ascii - 48 - } else { - // Safety: optionally adds 32 and then check (below) the result is in 'a..f' range - let ascii = - ascii + 32 * (unsafe { U128::unconstrained_check_is_upper_ascii(ascii) as u8 }); - assert(ascii >= 97); // enforce >= 'a' - assert(ascii <= 102); // enforce <= 'f' - ascii - 87 - } - ) as Field - } - - // TODO: Replace with a faster version. - // A circuit that uses this function can be slow to compute - // (we're doing up to 127 calls to compute the quotient) - unconstrained fn unconstrained_div(self: Self, b: U128) -> (U128, U128) { - if b == U128::zero() { - // Return 0,0 to avoid eternal loop - (U128::zero(), U128::zero()) - } else if self < b { - (U128::zero(), self) - } else if self == b { - (U128::one(), U128::zero()) - } else { - let (q, r) = if b.hi as u64 >= pow63 as u64 { - // The result of multiplication by 2 would overflow - (U128::zero(), self) - } else { - 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::one(), r - b) - } - } - } - - pub fn from_integer(i: T) -> U128 - where - T: AsPrimitive, - { - let f = i.as_(); - // Reject values which would overflow a u128 - f.assert_max_bit_size::<128>(); - let lo = f as u64 as Field; - let hi = (f - lo) / pow64; - U128 { lo, hi } - } - - pub fn to_integer(self) -> T - where - Field: AsPrimitive, - { - AsPrimitive::as_(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 = self.lo * b.hi + self.hi * b.lo + carry; - let hi = high as u64 as Field; - U128 { lo, hi } - } -} - -impl Add for U128 { - 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 { - 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 underflow"); - U128 { lo, hi } - } -} - -impl Mul for U128 { - 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 { - fn div(self: Self, b: U128) -> U128 { - // Safety: euclidian division is asserted to be correct: assert(a == b * q + r); and assert(r < b); - // Furthermore, U128 addition and multiplication ensures that b * q + r does not overflow - unsafe { - let (q, r) = self.unconstrained_div(b); - let a = b * q + r; - assert_eq(self, a); - assert(r < b); - q - } - } -} - -impl Rem for U128 { - fn rem(self: Self, b: U128) -> U128 { - // Safety: cf div() above - unsafe { - let (q, r) = self.unconstrained_div(b); - let a = b * q + r; - assert_eq(self, a); - assert(r < b); - - r - } - } -} - -impl Eq for U128 { - 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 Not for U128 { - fn not(self) -> U128 { - U128 { lo: (!(self.lo as u64)) as Field, hi: (!(self.hi as u64)) as Field } - } -} - -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: u8) -> U128 { - assert(other < 128, "attempt to shift left with overflow"); - let exp_bits: [u1; 7] = (other as Field).to_be_bits(); - - let mut r: Field = 2; - let mut y: Field = 1; - for i in 1..8 { - let bit = exp_bits[7 - i] as Field; - y = bit * (r * y) + (1 - bit) * y; - r *= r; - } - self.wrapping_mul(U128::from_integer(y)) - } -} - -impl Shr for U128 { - fn shr(self, other: u8) -> U128 { - assert(other < 128, "attempt to shift right with overflow"); - let exp_bits: [u1; 7] = (other as Field).to_be_bits(); - - let mut r: Field = 2; - let mut y: Field = 1; - for i in 1..8 { - let bit = exp_bits[7 - i] as Field; - y = bit * (r * y) + (1 - bit) * y; - r *= r; - } - self / U128::from_integer(y) - } -} - -impl Default for U128 { - fn default() -> Self { - U128::zero() - } -} - -mod tests { - use crate::default::Default; - use crate::ops::Not; - use crate::uint128::{pow63, pow64, U128}; - - #[test] - fn test_not(lo: u64, hi: u64) { - let num = U128::from_u64s_le(lo, hi); - let not_num = num.not(); - - assert_eq(not_num.hi, (hi.not() as Field)); - assert_eq(not_num.lo, (lo.not() as Field)); - - let not_not_num = not_num.not(); - assert_eq(num, not_not_num); - } - #[test] - fn test_construction() { - // Check little-endian u64 is inversed with big-endian u64 construction - let a = U128::from_u64s_le(2, 1); - let b = U128::from_u64s_be(1, 2); - assert_eq(a, b); - // Check byte construction is equivalent - let c = U128::from_le_bytes([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let d = U128::from_u64s_le(0x0706050403020100, 0x0f0e0d0c0b0a0908); - assert_eq(c, d); - } - #[test] - fn test_byte_decomposition() { - let a = U128::from_u64s_le(0x0706050403020100, 0x0f0e0d0c0b0a0908); - // Get big-endian and little-endian byte decompostions - let le_bytes_a = a.to_le_bytes(); - let be_bytes_a = a.to_be_bytes(); - - // Check equivalence - for i in 0..16 { - assert_eq(le_bytes_a[i], be_bytes_a[15 - i]); - } - // Reconstruct U128 from byte decomposition - let b = U128::from_le_bytes(le_bytes_a); - // Check that it's the same element - assert_eq(a, b); - } - #[test] - fn test_hex_constuction() { - let a = U128::from_u64s_le(0x1, 0x2); - let b = U128::from_hex("0x20000000000000001"); - assert_eq(a, b); - - let c = U128::from_hex("0xffffffffffffffffffffffffffffffff"); - let d = U128::from_u64s_le(0xffffffffffffffff, 0xffffffffffffffff); - assert_eq(c, d); - - let e = U128::from_hex("0x00000000000000000000000000000000"); - let f = U128::from_u64s_le(0, 0); - assert_eq(e, f); - } - - // Ascii decode tests - - #[test] - fn test_ascii_decode_correct_range() { - // '0'..'9' range - for i in 0..10 { - let decoded = U128::decode_ascii(48 + i); - assert_eq(decoded, i as Field); - } - // 'A'..'F' range - for i in 0..6 { - let decoded = U128::decode_ascii(65 + i); - assert_eq(decoded, (i + 10) as Field); - } - // 'a'..'f' range - for i in 0..6 { - let decoded = U128::decode_ascii(97 + i); - assert_eq(decoded, (i + 10) as Field); - } - } - - #[test(should_fail)] - fn test_ascii_decode_range_less_than_48_fails_0() { - crate::println(U128::decode_ascii(0)); - } - #[test(should_fail)] - fn test_ascii_decode_range_less_than_48_fails_1() { - crate::println(U128::decode_ascii(47)); - } - - #[test(should_fail)] - fn test_ascii_decode_range_58_64_fails_0() { - let _ = U128::decode_ascii(58); - } - #[test(should_fail)] - fn test_ascii_decode_range_58_64_fails_1() { - let _ = U128::decode_ascii(64); - } - #[test(should_fail)] - fn test_ascii_decode_range_71_96_fails_0() { - let _ = U128::decode_ascii(71); - } - #[test(should_fail)] - fn test_ascii_decode_range_71_96_fails_1() { - let _ = U128::decode_ascii(96); - } - #[test(should_fail)] - fn test_ascii_decode_range_greater_than_102_fails() { - let _ = U128::decode_ascii(103); - } - - #[test(should_fail)] - fn test_ascii_decode_regression() { - // This code will actually fail because of ascii_decode, - // but in the past it was possible to create a value > (1<<128) - let a = U128::from_hex("0x~fffffffffffffffffffffffffffffff"); - let b: Field = a.to_integer(); - let c: [u8; 17] = b.to_le_bytes(); - assert(c[16] != 0); - } - - #[test] - fn test_unconstrained_div() { - // Test the potential overflow case - let a = U128::from_u64s_le(0x0, 0xffffffffffffffff); - let b = U128::from_u64s_le(0x0, 0xfffffffffffffffe); - let c = U128::one(); - let d = U128::from_u64s_le(0x0, 0x1); - // Safety: testing context - unsafe { - let (q, r) = a.unconstrained_div(b); - assert_eq(q, c); - assert_eq(r, d); - } - - let a = U128::from_u64s_le(2, 0); - let b = U128::one(); - // Check the case where a is a multiple of b - // Safety: testing context - unsafe { - let (c, d) = a.unconstrained_div(b); - assert_eq((c, d), (a, U128::zero())); - } - - // Check where b is a multiple of a - // Safety: testing context - unsafe { - let (c, d) = b.unconstrained_div(a); - assert_eq((c, d), (U128::zero(), b)); - } - - // Dividing by zero returns 0,0 - let a = U128::from_u64s_le(0x1, 0x0); - let b = U128::zero(); - // Safety: testing context - unsafe { - let (c, d) = a.unconstrained_div(b); - assert_eq((c, d), (U128::zero(), U128::zero())); - } - // Dividing 1<<127 by 1<<127 (special case) - let a = U128::from_u64s_le(0x0, pow63 as u64); - let b = U128::from_u64s_le(0x0, pow63 as u64); - // Safety: testing context - unsafe { - let (c, d) = a.unconstrained_div(b); - assert_eq((c, d), (U128::one(), U128::zero())); - } - } - - #[test] - fn integer_conversions() { - // Maximum - let start: Field = 0xffffffffffffffffffffffffffffffff; - let a = U128::from_integer(start); - let end = a.to_integer(); - assert_eq(start, end); - - // Minimum - let start: Field = 0x0; - let a = U128::from_integer(start); - let end = a.to_integer(); - assert_eq(start, end); - - // Low limb - let start: Field = 0xffffffffffffffff; - let a = U128::from_integer(start); - let end = a.to_integer(); - assert_eq(start, end); - - // High limb - let start: Field = 0xffffffffffffffff0000000000000000; - let a = U128::from_integer(start); - let end = a.to_integer(); - assert_eq(start, end); - } - - #[test] - fn integer_conversions_fuzz(lo: u64, hi: u64) { - let start: Field = (lo as Field) + pow64 * (hi as Field); - let a = U128::from_integer(start); - let end = a.to_integer(); - assert_eq(start, end); - } - - #[test] - fn test_wrapping_mul() { - // 1*0==0 - assert_eq(U128::zero(), U128::zero().wrapping_mul(U128::one())); - - // 0*1==0 - assert_eq(U128::zero(), U128::one().wrapping_mul(U128::zero())); - - // 1*1==1 - assert_eq(U128::one(), U128::one().wrapping_mul(U128::one())); - - // 0 * ( 1 << 64 ) == 0 - assert_eq(U128::zero(), U128::zero().wrapping_mul(U128::from_u64s_le(0, 1))); - - // ( 1 << 64 ) * 0 == 0 - assert_eq(U128::zero(), U128::from_u64s_le(0, 1).wrapping_mul(U128::zero())); - - // 1 * ( 1 << 64 ) == 1 << 64 - assert_eq(U128::from_u64s_le(0, 1), U128::from_u64s_le(0, 1).wrapping_mul(U128::one())); - - // ( 1 << 64 ) * 1 == 1 << 64 - assert_eq(U128::from_u64s_le(0, 1), U128::one().wrapping_mul(U128::from_u64s_le(0, 1))); - - // ( 1 << 64 ) * ( 1 << 64 ) == 1 << 64 - assert_eq(U128::zero(), U128::from_u64s_le(0, 1).wrapping_mul(U128::from_u64s_le(0, 1))); - // -1 * -1 == 1 - assert_eq( - U128::one(), - U128::from_u64s_le(0xffffffffffffffff, 0xffffffffffffffff).wrapping_mul( - U128::from_u64s_le(0xffffffffffffffff, 0xffffffffffffffff), - ), - ); - } - - #[test] - fn test_default() { - assert_eq(U128::default(), U128::zero()); - } -} diff --git a/test_programs/compile_success_empty/comptime_as_field/src/main.nr b/test_programs/compile_success_empty/comptime_as_field/src/main.nr deleted file mode 100644 index f5871bbed81..00000000000 --- a/test_programs/compile_success_empty/comptime_as_field/src/main.nr +++ /dev/null @@ -1,5 +0,0 @@ -fn main() { - comptime { - let _: U128 = U128::from_integer(1); - } -} diff --git a/test_programs/compile_success_empty/comptime_as_field/Nargo.toml b/test_programs/compile_success_empty/comptime_as_primitive/Nargo.toml similarity index 100% rename from test_programs/compile_success_empty/comptime_as_field/Nargo.toml rename to test_programs/compile_success_empty/comptime_as_primitive/Nargo.toml diff --git a/test_programs/compile_success_empty/comptime_as_primitive/src/main.nr b/test_programs/compile_success_empty/comptime_as_primitive/src/main.nr new file mode 100644 index 00000000000..392ccf2ebfc --- /dev/null +++ b/test_programs/compile_success_empty/comptime_as_primitive/src/main.nr @@ -0,0 +1,7 @@ +fn main() { + comptime { + let x: u64 = 1; + let y = x as Field; + let _ = y as u128; + } +} diff --git a/test_programs/compile_success_empty/comptime_from_field/Nargo.toml b/test_programs/compile_success_empty/comptime_from_field/Nargo.toml deleted file mode 100644 index 38a46ba0dbe..00000000000 --- a/test_programs/compile_success_empty/comptime_from_field/Nargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "comptime_from_field" -type = "bin" -authors = [""] - -[dependencies] diff --git a/test_programs/compile_success_empty/comptime_from_field/src/main.nr b/test_programs/compile_success_empty/comptime_from_field/src/main.nr deleted file mode 100644 index 028722b94b2..00000000000 --- a/test_programs/compile_success_empty/comptime_from_field/src/main.nr +++ /dev/null @@ -1,5 +0,0 @@ -fn main() { - comptime { - let _: Field = U128::from_hex("0x0").to_integer(); - } -} diff --git a/test_programs/execution_success/u128/Nargo.toml b/test_programs/execution_success/u128/Nargo.toml deleted file mode 100644 index c1dcd84db04..00000000000 --- a/test_programs/execution_success/u128/Nargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "u128" -type = "bin" -authors = [""] - -[dependencies] diff --git a/test_programs/execution_success/u128/Prover.toml b/test_programs/execution_success/u128/Prover.toml deleted file mode 100644 index 961db9825a7..00000000000 --- a/test_programs/execution_success/u128/Prover.toml +++ /dev/null @@ -1,7 +0,0 @@ -x = "3" -y = "4" -z = "7" -hexa ="0x1f03a" -[big_int] -lo = 1 -hi = 2 \ No newline at end of file diff --git a/test_programs/execution_success/u128/src/main.nr b/test_programs/execution_success/u128/src/main.nr deleted file mode 100644 index 56ac5b995d9..00000000000 --- a/test_programs/execution_success/u128/src/main.nr +++ /dev/null @@ -1,41 +0,0 @@ -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 << (x as u8); - assert(shift == U128::from_integer(x << (x as u8))); - assert(shift >> (x as u8) == small_int); - assert(shift >> 127 == U128::from_integer(0)); - assert(shift << 127 == U128::from_integer(0)); - assert(U128::from_integer(3).to_integer() == 3); -} diff --git a/test_programs/noir_test_success/global_eval/src/main.nr b/test_programs/noir_test_success/global_eval/src/main.nr index 87a2d50a916..da962bf5203 100644 --- a/test_programs/noir_test_success/global_eval/src/main.nr +++ b/test_programs/noir_test_success/global_eval/src/main.nr @@ -1,20 +1,9 @@ -use std::uint128::U128; - // These definitions require `to_be_bits` and `to_le_bits` to be supported at comptime. global BITS_BE_13: [u1; 4] = (13 as Field).to_be_bits(); global BITS_LE_13: [u1; 4] = (13 as Field).to_le_bits(); -// Examples from #6691 which use the above behind the scenes. -global POW64_A: Field = 2.pow_32(64); -global POW64_B: Field = (U128::one() << 64).to_integer(); - #[test] fn test_be_and_le_bits() { assert_eq(BITS_BE_13, [1, 1, 0, 1]); assert_eq(BITS_LE_13, [1, 0, 1, 1]); } - -#[test] -fn test_pow64() { - assert_eq(POW64_A, POW64_B); -}