diff --git a/src/math/src/lib.cairo b/src/math/src/lib.cairo index 42255abe..5cc7c647 100644 --- a/src/math/src/lib.cairo +++ b/src/math/src/lib.cairo @@ -12,6 +12,7 @@ mod mod_arithmetics; mod perfect_number; mod sha256; mod sha512; +mod signed_u256; #[cfg(test)] mod tests; diff --git a/src/math/src/signed_u256.cairo b/src/math/src/signed_u256.cairo new file mode 100644 index 00000000..38a6745e --- /dev/null +++ b/src/math/src/signed_u256.cairo @@ -0,0 +1,300 @@ +use core::traits::Into; +// ====================== INT 257 ====================== + +// i257 represents a 129-bit integer. +// The inner field holds the absolute value of the integer. +// The sign field is true for negative integers, and false for non-negative integers. +#[derive(Copy, Drop)] +struct i257 { + sign: bool, + inner: u256, +} + + +// Checks if the given i257 integer is zero and has the correct sign. +// # Arguments +// * `x` - The i257 integer to check. +// # Panics +// Panics if `x` is zero and has a sign that is not false. +fn i257_check_sign_zero(x: i257) { + if x.inner == 0 { + assert(x.sign == false, 'sign of 0 must be false'); + } +} + +// Implements the Add trait for i257. +impl i257Add of Add { + fn add(lhs: i257, rhs: i257) -> i257 { + i257_check_sign_zero(lhs); + i257_check_sign_zero(rhs); + // If both integers have the same sign, + // the sum of their absolute values can be returned. + if lhs.sign == rhs.sign { + let sum = lhs.inner + rhs.inner; + i257 { inner: sum, sign: lhs.sign } + } else { + // If the integers have different signs, + // the larger absolute value is subtracted from the smaller one. + let (larger, smaller) = if lhs.inner >= rhs.inner { + (lhs, rhs) + } else { + (rhs, lhs) + }; + let difference = larger.inner - smaller.inner; + + i257 { inner: difference, sign: larger.sign } + } + } +} + +// Implements the AddEq trait for i257. +impl i257AddEq of AddEq { + #[inline(always)] + fn add_eq(ref self: i257, other: i257) { + self = Add::add(self, other); + } +} + +// Implements the Sub trait for i257. +impl i257Sub of Sub { + fn sub(lhs: i257, rhs: i257) -> i257 { + i257_check_sign_zero(lhs); + i257_check_sign_zero(rhs); + + if (rhs.inner == 0) { + return lhs; + } + + // The subtraction of `lhs` to `rhs` is achieved by negating `rhs` sign and adding it to `lhs`. + let neg_b = i257 { inner: rhs.inner, sign: !rhs.sign }; + lhs + neg_b + } +} + +// Implements the SubEq trait for i257. +impl i257SubEq of SubEq { + #[inline(always)] + fn sub_eq(ref self: i257, other: i257) { + self = Sub::sub(self, other); + } +} + +// Implements the Mul trait for i257. +impl i257Mul of Mul { + fn mul(lhs: i257, rhs: i257) -> i257 { + i257_check_sign_zero(lhs); + i257_check_sign_zero(rhs); + + // The sign of the product is the XOR of the signs of the operands. + let sign = lhs.sign ^ rhs.sign; + // The product is the product of the absolute values of the operands. + let inner = lhs.inner * rhs.inner; + i257 { inner, sign } + } +} + +// Implements the MulEq trait for i257. +impl i257MulEq of MulEq { + #[inline(always)] + fn mul_eq(ref self: i257, other: i257) { + self = Mul::mul(self, other); + } +} + +// Divides the first i257 by the second i257. +// # Arguments +// * `lhs` - The i257 dividend. +// * `rhs` - The i257 divisor. +// # Returns +// * `i257` - The quotient of `lhs` and `rhs`. +fn i257_div(lhs: i257, rhs: i257) -> i257 { + i257_check_sign_zero(lhs); + // Check that the divisor is not zero. + assert(rhs.inner != 0, 'b can not be 0'); + + // The sign of the quotient is the XOR of the signs of the operands. + let sign = lhs.sign ^ rhs.sign; + + if (sign == false) { + // If the operands are positive, the quotient is simply their absolute value quotient. + return i257 { inner: lhs.inner / rhs.inner, sign: sign }; + } + + // If the operands have different signs, rounding is necessary. + // First, check if the quotient is an integer. + if (lhs.inner % rhs.inner == 0) { + return i257 { inner: lhs.inner / rhs.inner, sign: sign }; + } + + // If the quotient is not an integer, multiply the dividend by 10 to move the decimal point over. + let quotient = (lhs.inner * 10) / rhs.inner; + let last_digit = quotient % 10; + + // Check the last digit to determine rounding direction. + if (last_digit <= 5) { + return i257 { inner: quotient / 10, sign: sign }; + } else { + return i257 { inner: (quotient / 10) + 1, sign: sign }; + } +} + +// Implements the Div trait for i257. +impl i257Div of Div { + fn div(lhs: i257, rhs: i257) -> i257 { + i257_div(lhs, rhs) + } +} + +// Implements the DivEq trait for i257. +impl i257DivEq of DivEq { + #[inline(always)] + fn div_eq(ref self: i257, other: i257) { + self = Div::div(self, other); + } +} + +// Calculates the remainder of the division of a first i257 by a second i257. +// # Arguments +// * `lhs` - The i257 dividend. +// * `rhs` - The i257 divisor. +// # Returns +// * `i257` - The remainder of dividing `lhs` by `rhs`. +fn i257_rem(lhs: i257, rhs: i257) -> i257 { + i257_check_sign_zero(lhs); + // Check that the divisor is not zero. + assert(rhs.inner != 0, 'b can not be 0'); + + return lhs - (rhs * (lhs / rhs)); +} + +// Implements the Rem trait for i257. +impl i257Rem of Rem { + fn rem(lhs: i257, rhs: i257) -> i257 { + i257_rem(lhs, rhs) + } +} + +// Implements the RemEq trait for i257. +impl i257RemEq of RemEq { + #[inline(always)] + fn rem_eq(ref self: i257, other: i257) { + self = Rem::rem(self, other); + } +} + +// Calculates both the quotient and the remainder of the division of a first i257 by a second i257. +// # Arguments +// * `lhs` - The i257 dividend. +// * `rhs` - The i257 divisor. +// # Returns +// * `(i257, i257)` - A tuple containing the quotient and the remainder of dividing `lhs` by `rhs`. +fn i257_div_rem(lhs: i257, rhs: i257) -> (i257, i257) { + let quotient = i257_div(lhs, rhs); + let remainder = i257_rem(lhs, rhs); + + return (quotient, remainder); +} + +// Implements the PartialEq trait for i257. +impl i257PartialEq of PartialEq { + fn eq(lhs: @i257, rhs: @i257) -> bool { + if lhs.sign == rhs.sign && lhs.inner == rhs.inner { + return true; + } + + return false; + } + + fn ne(lhs: @i257, rhs: @i257) -> bool { + !i257PartialEq::eq(lhs, rhs) + } +} + +// Implements the PartialOrd trait for i257. +impl i257PartialOrd of PartialOrd { + fn le(lhs: i257, rhs: i257) -> bool { + !i257PartialOrd::gt(lhs, rhs) + } + fn ge(lhs: i257, rhs: i257) -> bool { + i257PartialOrd::gt(lhs, rhs) || lhs == rhs + } + + fn lt(lhs: i257, rhs: i257) -> bool { + !i257PartialOrd::gt(lhs, rhs) && lhs != rhs + } + + fn gt(lhs: i257, rhs: i257) -> bool { + // Check if `lhs` is negative and `rhs` is positive. + if (lhs.sign & !rhs.sign) { + return false; + } + // Check if `lhs` is positive and `rhs` is negative. + if (!lhs.sign & rhs.sign) { + return true; + } + // If `lhs` and `rhs` have the same sign, compare their absolute values. + if (lhs.sign & rhs.sign) { + return lhs.inner < rhs.inner; + } else { + return lhs.inner > rhs.inner; + } + } +} + +// Implements the Neg trait for i257. +impl i257Neg of Neg { + fn neg(a: i257) -> i257 { + i257 { inner: a.inner, sign: !a.sign } + } +} + +// Computes the absolute value of the given i257 integer. +// # Arguments +// * `x` - The i257 integer to compute the absolute value of. +// # Returns +// * `i257` - The absolute value of `x`. +fn i257_abs(x: i257) -> i257 { + return i257 { inner: x.inner, sign: false }; +} + +// Computes the maximum between two i257 integers. +// # Arguments +// * `lhs` - The first i257 integer to compare. +// * `rhs` - The second i257 integer to compare. +// # Returns +// * `i257` - The maximum between `lhs` and `rhs`. +fn i257_max(lhs: i257, rhs: i257) -> i257 { + if (lhs > rhs) { + return lhs; + } else { + return rhs; + } +} + +// Computes the minimum between two i257 integers. +// # Arguments +// * `lhs` - The first i257 integer to compare. +// * `rhs` - The second i257 integer to compare. +// # Returns +// * `i257` - The minimum between `lhs` and `rhs`. +fn i257_min(lhs: i257, rhs: i257) -> i257 { + if (lhs < rhs) { + return lhs; + } else { + return rhs; + } +} + +// Convert u256 to i257 +impl U256IntoI257 of Into { + fn into(self: u256) -> i257 { + i257 { inner: self, sign: false, } + } +} + +// Convert felt252 to i257 +impl FeltIntoI257 of Into { + fn into(self: felt252) -> i257 { + i257 { inner: self.into(), sign: false, } + } +} diff --git a/src/math/src/tests.cairo b/src/math/src/tests.cairo index 0c2b2511..a256729f 100644 --- a/src/math/src/tests.cairo +++ b/src/math/src/tests.cairo @@ -12,6 +12,7 @@ mod mod_arithmetics_test; mod perfect_number_test; mod sha256_test; mod sha512_test; +mod signed_u256_test; mod test_keccak256; mod wad_ray_math_test; mod zellers_congruence_test; diff --git a/src/math/src/tests/signed_u256_test.cairo b/src/math/src/tests/signed_u256_test.cairo new file mode 100644 index 00000000..c880b5f1 --- /dev/null +++ b/src/math/src/tests/signed_u256_test.cairo @@ -0,0 +1,323 @@ +use alexandria_math::signed_u256::{i257, i257_div_rem}; + +#[test] +fn i257_test_add() { + // Test addition of two positive integers + let a = i257 { inner: 42_u256, sign: false }; + let b = i257 { inner: 13_u256, sign: false }; + let result = a + b; + assert(result.inner == 55_u256, '42 + 13 = 55'); + assert(result.sign == false, '42 + 13 -> positive'); + + // Test addition of two negative integers + let a = i257 { inner: 42_u256, sign: true }; + let b = i257 { inner: 13_u256, sign: true }; + let result = a + b; + assert(result.inner == 55_u256, '-42 - 13 = -55'); + assert(result.sign == true, '-42 - 13 -> negative'); + + // Test addition of a positive integer and a negative integer with the same magnitude + let a = i257 { inner: 42_u256, sign: false }; + let b = i257 { inner: 42_u256, sign: true }; + let result = a + b; + assert(result.inner == 0_u256, '42 - 42 = 0'); + assert(result.sign == false, '42 - 42 -> positive'); + + // Test addition of a positive integer and a negative integer with different magnitudes + let a = i257 { inner: 42_u256, sign: false }; + let b = i257 { inner: 13_u256, sign: true }; + let result = a + b; + assert(result.inner == 29_u256, '42 - 13 = 29'); + assert(result.sign == false, '42 - 13 -> positive'); + + // Test addition of a negative integer and a positive integer with different magnitudes + let a = i257 { inner: 42_u256, sign: true }; + let b = i257 { inner: 13_u256, sign: false }; + let result = a + b; + assert(result.inner == 29_u256, '-42 + 13 = -29'); + assert(result.sign == true, '-42 + 13 -> negative'); +} + +#[test] +fn i257_test_sub() { + // Test subtraction of two positive integers with larger first + let a = i257 { inner: 42_u256, sign: false }; + let b = i257 { inner: 13_u256, sign: false }; + let result = a - b; + assert(result.inner == 29_u256, '42 - 13 = 29'); + assert(result.sign == false, '42 - 13 -> positive'); + + // Test subtraction of two positive integers with larger second + let a = i257 { inner: 13_u256, sign: false }; + let b = i257 { inner: 42_u256, sign: false }; + let result = a - b; + assert(result.inner == 29_u256, '13 - 42 = -29'); + assert(result.sign == true, '13 - 42 -> negative'); + + // Test subtraction of two negative integers with larger first + let a = i257 { inner: 42_u256, sign: true }; + let b = i257 { inner: 13_u256, sign: true }; + let result = a - b; + assert(result.inner == 29_u256, '-42 - -13 = 29'); + assert(result.sign == true, '-42 - -13 -> negative'); + + // Test subtraction of two negative integers with larger second + let a = i257 { inner: 13_u256, sign: true }; + let b = i257 { inner: 42_u256, sign: true }; + let result = a - b; + assert(result.inner == 29_u256, '-13 - -42 = 29'); + assert(result.sign == false, '-13 - -42 -> positive'); + + // Test subtraction of a positive integer and a negative integer with the same magnitude + let a = i257 { inner: 42_u256, sign: false }; + let b = i257 { inner: 42_u256, sign: true }; + let result = a - b; + assert(result.inner == 84_u256, '42 - -42 = 84'); + assert(result.sign == false, '42 - -42 -> postive'); + + // Test subtraction of a negative integer and a positive integer with the same magnitude + let a = i257 { inner: 42_u256, sign: true }; + let b = i257 { inner: 42_u256, sign: false }; + let result = a - b; + assert(result.inner == 84_u256, '-42 - 42 = -84'); + assert(result.sign == true, '-42 - 42 -> negative'); + + // Test subtraction of a positive integer and a negative integer with different magnitudes + let a = i257 { inner: 100_u256, sign: false }; + let b = i257 { inner: 42_u256, sign: true }; + let result = a - b; + assert(result.inner == 142_u256, '100 - - 42 = 142'); + assert(result.sign == false, '100 - - 42 -> postive'); + + // Test subtraction of a negative integer and a positive integer with different magnitudes + let a = i257 { inner: 42_u256, sign: true }; + let b = i257 { inner: 100_u256, sign: false }; + let result = a - b; + assert(result.inner == 142_u256, '-42 - 100 = -142'); + assert(result.sign == true, '-42 - 100 -> negative'); + + // Test subtraction resulting in zero + let a = i257 { inner: 42_u256, sign: false }; + let b = i257 { inner: 42_u256, sign: false }; + let result = a - b; + assert(result.inner == 0_u256, '42 - 42 = 0'); + assert(result.sign == false, '42 - 42 -> positive'); +} + + +#[test] +fn i257_test_mul() { + // Test multiplication of positive integers + let a = i257 { inner: 10_u256, sign: false }; + let b = i257 { inner: 5_u256, sign: false }; + let result = a * b; + assert(result.inner == 50_u256, '10 * 5 = 50'); + assert(result.sign == false, '10 * 5 -> positive'); + + // Test multiplication of negative integers + let a = i257 { inner: 10_u256, sign: true }; + let b = i257 { inner: 5_u256, sign: true }; + let result = a * b; + assert(result.inner == 50_u256, '-10 * -5 = 50'); + assert(result.sign == false, '-10 * -5 -> positive'); + + // Test multiplication of positive and negative integers + let a = i257 { inner: 10_u256, sign: false }; + let b = i257 { inner: 5_u256, sign: true }; + let result = a * b; + assert(result.inner == 50_u256, '10 * -5 = -50'); + assert(result.sign == true, '10 * -5 -> negative'); + + // Test multiplication by zero + let a = i257 { inner: 10_u256, sign: false }; + let b = i257 { inner: 0_u256, sign: false }; + let expected = i257 { inner: 0_u256, sign: false }; + let result = a * b; + assert(result.inner == 0_u256, '10 * 0 = 0'); + assert(result.sign == false, '10 * 0 -> positive'); +} + +#[test] +fn i257_test_div_no_rem() { + // Test division of positive integers + let a = i257 { inner: 10_u256, sign: false }; + let b = i257 { inner: 5_u256, sign: false }; + let result = a / b; + assert(result.inner == 2_u256, '10 // 5 = 2'); + assert(result.sign == false, '10 // 5 -> positive'); + + // Test division of negative integers + let a = i257 { inner: 10_u256, sign: true }; + let b = i257 { inner: 5_u256, sign: true }; + let result = a / b; + assert(result.inner == 2_u256, '-10 // -5 = 2'); + assert(result.sign == false, '-10 // -5 -> positive'); + + // Test division of positive and negative integers + let a = i257 { inner: 10_u256, sign: false }; + let b = i257 { inner: 5_u256, sign: true }; + let result = a / b; + assert(result.inner == 2_u256, '10 // -5 = -2'); + assert(result.sign == true, '10 // -5 -> negative'); + + // Test division with a = zero + let a = i257 { inner: 0_u256, sign: false }; + let b = i257 { inner: 10_u256, sign: false }; + let result = a / b; + assert(result.inner == 0_u256, '0 // 10 = 0'); + assert(result.sign == false, '0 // 10 -> positive'); + + // Test division with a = zero + let a = i257 { inner: 0_u256, sign: false }; + let b = i257 { inner: 10_u256, sign: false }; + let result = a / b; + assert(result.inner == 0_u256, '0 // 10 = 0'); + assert(result.sign == false, '0 // 10 -> positive'); +} + +#[test] +fn i257_test_div_rem() { + // Test division and remainder of positive integers + let a = i257 { inner: 13_u256, sign: false }; + let b = i257 { inner: 5_u256, sign: false }; + let (q, r) = i257_div_rem(a, b); + assert(q.inner == 2_u256 && r.inner == 3_u256, '13 // 5 = 2 r 3'); + assert(q.sign == false && r.sign == false, '13 // 5 -> positive'); + + // Test division and remainder of negative integers + let a = i257 { inner: 13_u256, sign: true }; + let b = i257 { inner: 5_u256, sign: true }; + let (q, r) = i257_div_rem(a, b); + assert(q.inner == 2_u256 && r.inner == 3_u256, '-13 // -5 = 2 r -3'); + assert(q.sign == false && r.sign == true, '-13 // -5 -> positive'); + + // Test division and remainder of positive and negative integers + let a = i257 { inner: 13_u256, sign: false }; + let b = i257 { inner: 5_u256, sign: true }; + let (q, r) = i257_div_rem(a, b); + assert(q.inner == 3_u256 && r.inner == 2_u256, '13 // -5 = -3 r -2'); + assert(q.sign == true && r.sign == true, '13 // -5 -> negative'); + + // Test division with a = zero + let a = i257 { inner: 0_u256, sign: false }; + let b = i257 { inner: 10_u256, sign: false }; + let (q, r) = i257_div_rem(a, b); + assert(q.inner == 0_u256 && r.inner == 0_u256, '0 // 10 = 0 r 0'); + assert(q.sign == false && r.sign == false, '0 // 10 -> positive'); + + // Test division and remainder with a negative dividend and positive divisor + let a = i257 { inner: 13_u256, sign: true }; + let b = i257 { inner: 5_u256, sign: false }; + let (q, r) = i257_div_rem(a, b); + assert(q.inner == 3_u256 && r.inner == 2_u256, '-13 // 5 = -3 r 2'); + assert(q.sign == true && r.sign == false, '-13 // 5 -> negative'); +} + +#[test] +fn i257_test_partial_ord() { + // Test two postive integers + let a = i257 { inner: 13_u256, sign: false }; + let b = i257 { inner: 5_u256, sign: false }; + assert(a > b, '13 > 5'); + assert(a >= b, '13 >= 5'); + assert(b < a, '5 < 13'); + assert(b <= a, '5 <= 13'); + + // Test `a` postive and `b` negative + let a = i257 { inner: 13_u256, sign: false }; + let b = i257 { inner: 5_u256, sign: true }; + assert(a > b, '13 > -5'); + assert(a >= b, '13 >= -5'); + assert(b < a, '-5 < 13'); + assert(b <= a, '-5 <= 13'); + + // Test `a` negative and `b` postive + let a = i257 { inner: 13_u256, sign: true }; + let b = i257 { inner: 5_u256, sign: false }; + assert(b > a, '5 > -13'); + assert(b >= a, '5 >= -13'); + assert(a < b, '-13 < 5'); + assert(a <= b, '5 <= -13'); + + // Test `a` negative and `b` negative + let a = i257 { inner: 13_u256, sign: true }; + let b = i257 { inner: 5_u256, sign: true }; + assert(b > a, '-5 > -13'); + assert(b >= a, '-13 >= -5'); + assert(a < b, '-13 < -5'); + assert(a <= b, '-13 <= -5'); +} + +#[test] +fn i257_test_eq_not_eq() { + // Test two postive integers + let a = i257 { inner: 13_u256, sign: false }; + let b = i257 { inner: 5_u256, sign: false }; + assert(a != b, '13 != 5'); + + // Test `a` postive and `b` negative + let a = i257 { inner: 13_u256, sign: false }; + let b = i257 { inner: 5_u256, sign: true }; + assert(a != b, '13 != -5'); + + // Test `a` negative and `b` postive + let a = i257 { inner: 13_u256, sign: true }; + let b = i257 { inner: 5_u256, sign: false }; + assert(a != b, '-13 != 5'); + + // Test `a` negative and `b` negative + let a = i257 { inner: 13_u256, sign: true }; + let b = i257 { inner: 5_u256, sign: true }; + assert(a != b, '-13 != -5'); +} + +#[test] +fn i257_test_equality() { + // Test equal with two positive integers + let a = i257 { inner: 13_u256, sign: false }; + let b = i257 { inner: 13_u256, sign: false }; + assert(a == b, '13 == 13'); + + // Test equal with two negative integers + let a = i257 { inner: 13_u256, sign: true }; + let b = i257 { inner: 13_u256, sign: true }; + assert(a == b, '-13 == -13'); + + // Test not equal with two postive integers + let a = i257 { inner: 13_u256, sign: false }; + let b = i257 { inner: 5_u256, sign: false }; + assert(a != b, '13 != 5'); + + // Test not equal with `a` postive and `b` negative + let a = i257 { inner: 13_u256, sign: false }; + let b = i257 { inner: 5_u256, sign: true }; + assert(a != b, '13 != -5'); + + // Test not equal with `a` negative and `b` postive + let a = i257 { inner: 13_u256, sign: true }; + let b = i257 { inner: 5_u256, sign: false }; + assert(a != b, '-13 != 5'); + + // Test not equal with `a` negative and `b` negative + let a = i257 { inner: 13_u256, sign: true }; + let b = i257 { inner: 5_u256, sign: true }; + assert(a != b, '-13 != -5'); +} + +#[test] +#[should_panic] +fn i257_test_check_sign_zero() { + let x = i257 { inner: 0_u256, sign: true }; + alexandria_math::signed_u256::i257_check_sign_zero(x); +} + +#[test] +fn i257_test_into() { + let x: i257 = 35_u256.into(); + assert(x.inner == 35, 'incorrect into value'); + assert(x.sign == false, 'incorrect into sign'); + + let y: i257 = 258973.into(); + assert(y.inner == 258973, 'incorrect into value'); + assert(y.sign == false, 'incorrect into sign'); +}