use crate::ParseFeltError;
use crate::bigint_felt::{FeltBigInt, FIELD_HIGH, FIELD_LOW};
use num_bigint::{BigInt, BigUint, U64Digits};
use num_integer::Integer;
use num_traits::{Bounded, FromPrimitive, Num, One, Pow, Signed, ToPrimitive, Zero};
use serde::{Deserialize, Serialize};
use core::{
convert::Into,
fmt,
iter::Sum,
ops::{
Add, AddAssign, BitAnd, BitOr, BitXor, Div, Mul, MulAssign, Neg, Rem, Shl, Shr, ShrAssign,
Sub, SubAssign,
},
};
#[cfg(all(not(feature = "std"), feature = "alloc"))]
use alloc::{string::String, vec::Vec};
#[cfg(all(feature = "arbitrary", feature = "std"))]
use arbitrary::Arbitrary;
pub(crate) trait FeltOps {
fn new<T: Into<FeltBigInt<FIELD_HIGH, FIELD_LOW>>>(value: T) -> Self;
fn modpow(
&self,
exponent: &FeltBigInt<FIELD_HIGH, FIELD_LOW>,
modulus: &FeltBigInt<FIELD_HIGH, FIELD_LOW>,
) -> Self;
fn iter_u64_digits(&self) -> U64Digits;
#[cfg(any(feature = "std", feature = "alloc"))]
fn to_signed_bytes_le(&self) -> Vec<u8>;
#[cfg(any(feature = "std", feature = "alloc"))]
fn to_bytes_be(&self) -> Vec<u8>;
fn parse_bytes(buf: &[u8], radix: u32) -> Option<FeltBigInt<FIELD_HIGH, FIELD_LOW>>;
fn from_bytes_be(bytes: &[u8]) -> Self;
fn from_bytes_le(bytes: &[u8]) -> Self;
#[cfg(any(feature = "std", feature = "alloc"))]
fn to_str_radix(&self, radix: u32) -> String;
fn to_signed_felt(&self) -> BigInt;
fn to_bigint(&self) -> BigInt;
fn to_biguint(&self) -> BigUint;
fn bits(&self) -> u64;
fn prime() -> BigUint;
}
#[macro_export]
macro_rules! felt_str {
($val: expr) => {
$crate::Felt252::parse_bytes($val.as_bytes(), 10_u32).expect("Couldn't parse bytes")
};
($val: expr, $opt: expr) => {
$crate::Felt252::parse_bytes($val.as_bytes(), $opt as u32).expect("Couldn't parse bytes")
};
}
#[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(Arbitrary))]
#[derive(Eq, Hash, PartialEq, PartialOrd, Ord, Clone, Deserialize, Default, Serialize)]
pub struct Felt252 {
pub(crate) value: FeltBigInt<FIELD_HIGH, FIELD_LOW>,
}
macro_rules! from_num {
($type:ty) => {
impl From<$type> for Felt252 {
fn from(value: $type) -> Self {
Self {
value: value.into(),
}
}
}
};
}
from_num!(i8);
from_num!(i16);
from_num!(i32);
from_num!(i64);
from_num!(i128);
from_num!(isize);
from_num!(u8);
from_num!(u16);
from_num!(u32);
from_num!(u64);
from_num!(u128);
from_num!(usize);
from_num!(BigInt);
from_num!(&BigInt);
from_num!(BigUint);
from_num!(&BigUint);
impl From<bool> for Felt252 {
fn from(flag: bool) -> Self {
if flag {
Self::one()
} else {
Self::zero()
}
}
}
impl Felt252 {
pub fn new<T: Into<Felt252>>(value: T) -> Self {
value.into()
}
#[deprecated]
pub fn modpow(&self, exponent: &Felt252, modulus: &Felt252) -> Self {
Self {
value: self.value.modpow(&exponent.value, &modulus.value),
}
}
pub fn iter_u64_digits(&self) -> U64Digits {
self.value.iter_u64_digits()
}
pub fn to_le_bytes(&self) -> [u8; 32] {
let mut res = [0u8; 32];
let mut iter = self.iter_u64_digits();
let (d0, d1, d2, d3) = (
iter.next().unwrap_or_default().to_le_bytes(),
iter.next().unwrap_or_default().to_le_bytes(),
iter.next().unwrap_or_default().to_le_bytes(),
iter.next().unwrap_or_default().to_le_bytes(),
);
res[..8].copy_from_slice(&d0);
res[8..16].copy_from_slice(&d1);
res[16..24].copy_from_slice(&d2);
res[24..].copy_from_slice(&d3);
res
}
pub fn to_be_bytes(&self) -> [u8; 32] {
let mut bytes = self.to_le_bytes();
bytes.reverse();
bytes
}
pub fn to_le_digits(&self) -> [u64; 4] {
let mut iter = self.iter_u64_digits();
[
iter.next().unwrap_or_default(),
iter.next().unwrap_or_default(),
iter.next().unwrap_or_default(),
iter.next().unwrap_or_default(),
]
}
#[cfg(any(feature = "std", feature = "alloc"))]
#[deprecated]
pub fn to_signed_bytes_le(&self) -> Vec<u8> {
self.value.to_signed_bytes_le()
}
#[cfg(any(feature = "std", feature = "alloc"))]
pub fn to_bytes_be(&self) -> Vec<u8> {
self.value.to_bytes_be()
}
pub fn parse_bytes(buf: &[u8], radix: u32) -> Option<Self> {
Some(Self {
value: FeltBigInt::parse_bytes(buf, radix)?,
})
}
pub fn from_bytes_be(bytes: &[u8]) -> Self {
Self {
value: FeltBigInt::from_bytes_be(bytes),
}
}
pub fn from_bytes_le(bytes: &[u8]) -> Self {
Self {
value: FeltBigInt::from_bytes_le(bytes),
}
}
pub fn from_bytes_ne(bytes: &[u8]) -> Self {
#[cfg(target_endian = "little")]
let res = Self::from_bytes_le(bytes);
#[cfg(target_endian = "big")]
let res = Self::from_bytes_be(bytes);
res
}
#[cfg(any(feature = "std", feature = "alloc"))]
pub fn to_str_radix(&self, radix: u32) -> String {
self.value.to_str_radix(radix)
}
pub fn to_signed_felt(&self) -> BigInt {
#[allow(deprecated)]
self.value.to_signed_felt()
}
pub fn to_bigint(&self) -> BigInt {
#[allow(deprecated)]
self.value.to_bigint()
}
pub fn to_biguint(&self) -> BigUint {
#[allow(deprecated)]
self.value.to_biguint()
}
pub fn sqrt(&self) -> Self {
if self.is_zero() || self.is_one() {
return self.clone();
}
let max_felt = Felt252::max_value();
let trailing_prime = Felt252::max_value() >> 192; let a = self.pow(&trailing_prime);
let d = (&Felt252::new(3_i32)).pow(&trailing_prime);
let mut m = Felt252::zero();
let mut exponent = Felt252::one() << 191_u32;
let mut adm;
for i in 0..192_u32 {
adm = &a * &(&d).pow(&m);
adm = (&adm).pow(&exponent);
exponent >>= 1;
if adm == max_felt {
m += Felt252::one() << i;
}
}
let root_1 = self.pow(&((trailing_prime + 1_u32) >> 1)) * (&d).pow(&(m >> 1));
let root_2 = &max_felt - &root_1 + 1_usize;
if root_1 < root_2 {
root_1
} else {
root_2
}
}
pub fn bits(&self) -> u64 {
self.value.bits()
}
pub fn prime() -> BigUint {
FeltBigInt::prime()
}
}
impl Add for Felt252 {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Self {
value: self.value + rhs.value,
}
}
}
impl<'a> Add for &'a Felt252 {
type Output = Felt252;
fn add(self, rhs: Self) -> Self::Output {
Self::Output {
value: &self.value + &rhs.value,
}
}
}
impl<'a> Add<&'a Felt252> for Felt252 {
type Output = Self;
fn add(self, rhs: &Self) -> Self::Output {
Self::Output {
value: self.value + &rhs.value,
}
}
}
impl Add<u32> for Felt252 {
type Output = Self;
fn add(self, rhs: u32) -> Self {
Self {
value: self.value + rhs,
}
}
}
impl Add<usize> for Felt252 {
type Output = Self;
fn add(self, rhs: usize) -> Self {
Self {
value: self.value + rhs,
}
}
}
impl<'a> Add<usize> for &'a Felt252 {
type Output = Felt252;
fn add(self, rhs: usize) -> Self::Output {
Self::Output {
value: &self.value + rhs,
}
}
}
impl Add<u64> for &Felt252 {
type Output = Felt252;
fn add(self, rhs: u64) -> Self::Output {
Self::Output {
value: &self.value + rhs,
}
}
}
impl Add<&Felt252> for u64 {
type Output = Option<u64>;
fn add(self, rhs: &Felt252) -> Option<u64> {
const PRIME_DIGITS_LE_HI: (u64, u64, u64) =
(0x0000000000000000, 0x0000000000000000, 0x0800000000000011);
const PRIME_MINUS_U64_MAX_DIGITS_LE_HI: (u64, u64, u64) =
(0xffffffffffffffff, 0xffffffffffffffff, 0x0800000000000010);
let mut rhs_digits = rhs.iter_u64_digits();
let Some(low) = rhs_digits.next() else {
return Some(self);
};
let Some(h0) = rhs_digits.next() else {
return self.checked_add(low)
};
let (h1, h2) = (rhs_digits.next()?, rhs_digits.next()?);
match (h0, h1, h2) {
#[allow(clippy::suspicious_arithmetic_impl)]
PRIME_DIGITS_LE_HI => self.checked_sub(1),
#[allow(clippy::suspicious_arithmetic_impl)]
PRIME_MINUS_U64_MAX_DIGITS_LE_HI if low >= 2 => {
(self).checked_sub(u64::MAX - (low - 2))
}
_ => None,
}
}
}
impl AddAssign for Felt252 {
fn add_assign(&mut self, rhs: Self) {
self.value += rhs.value;
}
}
impl<'a> AddAssign<&'a Felt252> for Felt252 {
fn add_assign(&mut self, rhs: &Self) {
self.value += &rhs.value;
}
}
impl Sum for Felt252 {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Felt252::zero(), |mut acc, x| {
acc += x;
acc
})
}
}
impl Neg for Felt252 {
type Output = Self;
fn neg(self) -> Self {
Self {
value: self.value.neg(),
}
}
}
impl<'a> Neg for &'a Felt252 {
type Output = Felt252;
fn neg(self) -> Self::Output {
Self::Output {
value: (&self.value).neg(),
}
}
}
impl Sub for Felt252 {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
Self {
value: self.value - rhs.value,
}
}
}
impl<'a> Sub for &'a Felt252 {
type Output = Felt252;
fn sub(self, rhs: Self) -> Self::Output {
Self::Output {
value: &self.value - &rhs.value,
}
}
}
impl<'a> Sub<&'a Felt252> for Felt252 {
type Output = Self;
fn sub(self, rhs: &Self) -> Self {
Self {
value: self.value - &rhs.value,
}
}
}
impl Sub<&Felt252> for usize {
type Output = Felt252;
fn sub(self, rhs: &Self::Output) -> Self::Output {
Self::Output {
value: self - &rhs.value,
}
}
}
impl SubAssign for Felt252 {
fn sub_assign(&mut self, rhs: Self) {
self.value -= rhs.value
}
}
impl<'a> SubAssign<&'a Felt252> for Felt252 {
fn sub_assign(&mut self, rhs: &Self) {
self.value -= &rhs.value;
}
}
impl Sub<u32> for Felt252 {
type Output = Self;
fn sub(self, rhs: u32) -> Self {
Self {
value: self.value - rhs,
}
}
}
impl<'a> Sub<u32> for &'a Felt252 {
type Output = Felt252;
fn sub(self, rhs: u32) -> Self::Output {
Self::Output {
value: &self.value - rhs,
}
}
}
impl Sub<usize> for Felt252 {
type Output = Self;
fn sub(self, rhs: usize) -> Self {
Self {
value: self.value - rhs,
}
}
}
impl Mul for Felt252 {
type Output = Self;
fn mul(self, rhs: Self) -> Self {
Self {
value: self.value * rhs.value,
}
}
}
impl<'a> Mul for &'a Felt252 {
type Output = Felt252;
fn mul(self, rhs: Self) -> Self::Output {
Self::Output {
value: &self.value * &rhs.value,
}
}
}
impl<'a> Mul<&'a Felt252> for Felt252 {
type Output = Self;
fn mul(self, rhs: &Self) -> Self {
Self {
value: self.value * &rhs.value,
}
}
}
impl<'a> MulAssign<&'a Felt252> for Felt252 {
fn mul_assign(&mut self, rhs: &Self) {
self.value *= &rhs.value;
}
}
impl Pow<u32> for Felt252 {
type Output = Self;
fn pow(self, rhs: u32) -> Self {
Self {
value: self.value.pow(rhs),
}
}
}
impl<'a> Pow<u32> for &'a Felt252 {
type Output = Felt252;
fn pow(self, rhs: u32) -> Self::Output {
Self::Output {
value: (&self.value).pow(rhs),
}
}
}
impl<'a> Pow<&'a Felt252> for &'a Felt252 {
type Output = Felt252;
fn pow(self, rhs: &'a Felt252) -> Self::Output {
Self::Output {
value: (&self.value).pow(&rhs.value),
}
}
}
impl Div for Felt252 {
type Output = Self;
fn div(self, rhs: Self) -> Self {
Self {
value: self.value / rhs.value,
}
}
}
impl<'a> Div for &'a Felt252 {
type Output = Felt252;
fn div(self, rhs: Self) -> Self::Output {
Self::Output {
value: &self.value / &rhs.value,
}
}
}
impl<'a> Div<Felt252> for &'a Felt252 {
type Output = Felt252;
fn div(self, rhs: Self::Output) -> Self::Output {
Self::Output {
value: &self.value / rhs.value,
}
}
}
impl Rem for Felt252 {
type Output = Self;
fn rem(self, rhs: Self) -> Self {
Self {
value: self.value % rhs.value,
}
}
}
impl<'a> Rem<&'a Felt252> for Felt252 {
type Output = Self;
fn rem(self, rhs: &Self) -> Self {
Self {
value: self.value % &rhs.value,
}
}
}
impl Zero for Felt252 {
fn zero() -> Self {
Self {
value: FeltBigInt::zero(),
}
}
fn is_zero(&self) -> bool {
self.value.is_zero()
}
}
impl One for Felt252 {
fn one() -> Self {
Self {
value: FeltBigInt::one(),
}
}
fn is_one(&self) -> bool {
self.value.is_one()
}
}
impl Bounded for Felt252 {
fn min_value() -> Self {
Self {
value: FeltBigInt::min_value(),
}
}
fn max_value() -> Self {
Self {
value: FeltBigInt::max_value(),
}
}
}
impl Num for Felt252 {
type FromStrRadixErr = ParseFeltError;
fn from_str_radix(string: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
Ok(Self {
value: FeltBigInt::from_str_radix(string, radix)?,
})
}
}
impl Integer for Felt252 {
fn div_floor(&self, rhs: &Self) -> Self {
Self {
value: self.value.div_floor(&rhs.value),
}
}
fn div_rem(&self, other: &Self) -> (Self, Self) {
let (div, rem) = self.value.div_rem(&other.value);
(Self { value: div }, Self { value: rem })
}
fn divides(&self, other: &Self) -> bool {
self.value.divides(&other.value)
}
fn gcd(&self, other: &Self) -> Self {
Self {
value: self.value.gcd(&other.value),
}
}
fn is_even(&self) -> bool {
self.value.is_even()
}
fn is_multiple_of(&self, other: &Self) -> bool {
self.value.is_multiple_of(&other.value)
}
fn is_odd(&self) -> bool {
self.value.is_odd()
}
fn lcm(&self, other: &Self) -> Self {
Self {
value: self.value.lcm(&other.value),
}
}
fn mod_floor(&self, rhs: &Self) -> Self {
Self {
value: self.value.mod_floor(&rhs.value),
}
}
}
impl Signed for Felt252 {
fn abs(&self) -> Self {
Self {
value: self.value.abs(),
}
}
fn abs_sub(&self, other: &Self) -> Self {
Self {
value: self.value.abs_sub(&other.value),
}
}
fn signum(&self) -> Self {
Self {
value: self.value.signum(),
}
}
fn is_positive(&self) -> bool {
self.value.is_positive()
}
fn is_negative(&self) -> bool {
self.value.is_negative()
}
}
impl Shl<u32> for Felt252 {
type Output = Self;
fn shl(self, rhs: u32) -> Self {
Self {
value: self.value << rhs,
}
}
}
impl<'a> Shl<u32> for &'a Felt252 {
type Output = Felt252;
fn shl(self, rhs: u32) -> Self::Output {
Self::Output {
value: &self.value << rhs,
}
}
}
impl Shl<usize> for Felt252 {
type Output = Self;
fn shl(self, rhs: usize) -> Self {
Self {
value: self.value << rhs,
}
}
}
impl<'a> Shl<usize> for &'a Felt252 {
type Output = Felt252;
fn shl(self, rhs: usize) -> Self::Output {
Self::Output {
value: &self.value << rhs,
}
}
}
impl Shr<u32> for Felt252 {
type Output = Self;
fn shr(self, rhs: u32) -> Self {
Self {
value: self.value >> rhs,
}
}
}
impl<'a> Shr<u32> for &'a Felt252 {
type Output = Felt252;
fn shr(self, rhs: u32) -> Self::Output {
Self::Output {
value: &self.value >> rhs,
}
}
}
impl ShrAssign<usize> for Felt252 {
fn shr_assign(&mut self, rhs: usize) {
self.value >>= rhs
}
}
impl<'a> BitAnd for &'a Felt252 {
type Output = Felt252;
fn bitand(self, rhs: Self) -> Self::Output {
Self::Output {
value: &self.value & &rhs.value,
}
}
}
impl<'a> BitAnd<&'a Felt252> for Felt252 {
type Output = Self;
fn bitand(self, rhs: &Self) -> Self {
Self {
value: self.value & &rhs.value,
}
}
}
impl<'a> BitAnd<Felt252> for &'a Felt252 {
type Output = Felt252;
fn bitand(self, rhs: Self::Output) -> Self::Output {
Self::Output {
value: &self.value & rhs.value,
}
}
}
impl<'a> BitOr for &'a Felt252 {
type Output = Felt252;
fn bitor(self, rhs: Self) -> Self::Output {
Self::Output {
value: &self.value | &rhs.value,
}
}
}
impl<'a> BitXor for &'a Felt252 {
type Output = Felt252;
fn bitxor(self, rhs: Self) -> Self::Output {
Self::Output {
value: &self.value ^ &rhs.value,
}
}
}
impl ToPrimitive for Felt252 {
fn to_u128(&self) -> Option<u128> {
self.value.to_u128()
}
fn to_u64(&self) -> Option<u64> {
self.value.to_u64()
}
fn to_i64(&self) -> Option<i64> {
self.value.to_i64()
}
}
impl FromPrimitive for Felt252 {
fn from_u64(n: u64) -> Option<Self> {
FeltBigInt::from_u64(n).map(|n| Self { value: n })
}
fn from_i64(n: i64) -> Option<Self> {
FeltBigInt::from_i64(n).map(|n| Self { value: n })
}
}
impl fmt::Display for Felt252 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl fmt::Debug for Felt252 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.value)
}
}
macro_rules! assert_felt_methods {
($type:ty) => {
const _: () = {
fn assert_felt_ops<T: FeltOps>() {}
fn assertion() {
assert_felt_ops::<$type>();
}
};
};
}
macro_rules! assert_felt_impl {
($type:ty) => {
const _: () = {
fn assert_add<T: Add>() {}
fn assert_add_ref<'a, T: Add<&'a $type>>() {}
fn assert_add_u32<T: Add<u32>>() {}
fn assert_add_usize<T: Add<usize>>() {}
fn assert_add_assign<T: AddAssign>() {}
fn assert_add_assign_ref<'a, T: AddAssign<&'a $type>>() {}
fn assert_sum<T: Sum<$type>>() {}
fn assert_neg<T: Neg>() {}
fn assert_sub<T: Sub>() {}
fn assert_sub_ref<'a, T: Sub<&'a $type>>() {}
fn assert_sub_assign<T: SubAssign>() {}
fn assert_sub_assign_ref<'a, T: SubAssign<&'a $type>>() {}
fn assert_sub_u32<T: Sub<u32>>() {}
fn assert_sub_usize<T: Sub<usize>>() {}
fn assert_mul<T: Mul>() {}
fn assert_mul_ref<'a, T: Mul<&'a $type>>() {}
fn assert_mul_assign_ref<'a, T: MulAssign<&'a $type>>() {}
fn assert_pow_u32<T: Pow<u32>>() {}
fn assert_pow_felt<'a, T: Pow<&'a $type>>() {}
fn assert_div<T: Div>() {}
fn assert_ref_div<T: Div<$type>>() {}
fn assert_rem<T: Rem>() {}
fn assert_rem_ref<'a, T: Rem<&'a $type>>() {}
fn assert_zero<T: Zero>() {}
fn assert_one<T: One>() {}
fn assert_bounded<T: Bounded>() {}
fn assert_num<T: Num>() {}
fn assert_integer<T: Integer>() {}
fn assert_signed<T: Signed>() {}
fn assert_shl_u32<T: Shl<u32>>() {}
fn assert_shl_usize<T: Shl<usize>>() {}
fn assert_shr_u32<T: Shr<u32>>() {}
fn assert_shr_assign_usize<T: ShrAssign<usize>>() {}
fn assert_bitand<T: BitAnd>() {}
fn assert_bitand_ref<'a, T: BitAnd<&'a $type>>() {}
fn assert_ref_bitand<T: BitAnd<$type>>() {}
fn assert_bitor<T: BitOr>() {}
fn assert_bitxor<T: BitXor>() {}
fn assert_from_primitive<T: FromPrimitive>() {}
fn assert_to_primitive<T: ToPrimitive>() {}
fn assert_display<T: fmt::Display>() {}
fn assert_debug<T: fmt::Debug>() {}
#[allow(dead_code)]
fn assert_all() {
assert_add::<$type>();
assert_add::<&$type>();
assert_add_ref::<$type>();
assert_add_u32::<$type>();
assert_add_usize::<$type>();
assert_add_usize::<&$type>();
assert_add_assign::<$type>();
assert_add_assign_ref::<$type>();
assert_sum::<$type>();
assert_neg::<$type>();
assert_neg::<&$type>();
assert_sub::<$type>();
assert_sub::<&$type>();
assert_sub_ref::<$type>();
assert_sub_assign::<$type>();
assert_sub_assign_ref::<$type>();
assert_sub_u32::<$type>();
assert_sub_u32::<&$type>();
assert_sub_usize::<$type>();
assert_mul::<$type>();
assert_mul::<&$type>();
assert_mul_ref::<$type>();
assert_mul_assign_ref::<$type>();
assert_pow_u32::<$type>();
assert_pow_felt::<&$type>();
assert_div::<$type>();
assert_div::<&$type>();
assert_ref_div::<&$type>();
assert_rem::<$type>();
assert_rem_ref::<$type>();
assert_zero::<$type>();
assert_one::<$type>();
assert_bounded::<$type>();
assert_num::<$type>();
assert_integer::<$type>();
assert_signed::<$type>();
assert_shl_u32::<$type>();
assert_shl_u32::<&$type>();
assert_shl_usize::<$type>();
assert_shl_usize::<&$type>();
assert_shr_u32::<$type>();
assert_shr_u32::<&$type>();
assert_shr_assign_usize::<$type>();
assert_bitand::<&$type>();
assert_bitand_ref::<$type>();
assert_ref_bitand::<&$type>();
assert_bitor::<&$type>();
assert_bitxor::<&$type>();
assert_from_primitive::<$type>();
assert_to_primitive::<$type>();
assert_display::<$type>();
assert_debug::<$type>();
}
};
};
}
assert_felt_methods!(FeltBigInt<FIELD_HIGH, FIELD_LOW>);
assert_felt_impl!(FeltBigInt<FIELD_HIGH, FIELD_LOW>);
assert_felt_impl!(Felt252);
#[cfg(test)]
mod test {
use super::*;
use crate::{arbitrary_bigint_felt::nonzero_felt252, PRIME_STR};
use core::cmp;
use rstest::rstest;
use proptest::prelude::*;
proptest! {
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn new_in_range(ref x in any::<[u8; 40]>()) {
let x = Felt252::from_bytes_be(x);
let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap();
prop_assert!(&x.to_biguint() < p);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn from_bytes_be(high: u128, low: u128) {
let expected = (Felt252::from(high) << 128_usize) + Felt252::from(low);
let mut bytes = [0; 32];
bytes[..16].copy_from_slice(&high.to_be_bytes());
bytes[16..].copy_from_slice(&low.to_be_bytes());
let got = Felt252::from_bytes_be(&bytes);
prop_assert_eq!(got, expected);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn from_bytes_le(high: u128, low: u128) {
let expected = (Felt252::from(high) << 128_usize) + Felt252::from(low);
let mut bytes = [0; 32];
bytes[..16].copy_from_slice(&low.to_le_bytes());
bytes[16..].copy_from_slice(&high.to_le_bytes());
let got = Felt252::from_bytes_le(&bytes);
prop_assert_eq!(got, expected);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn to_be_bytes(ref x in any::<Felt252>()) {
let bytes = x.to_be_bytes();
let y = &Felt252::from_bytes_be(&bytes);
prop_assert_eq!(x, y);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn to_le_bytes(ref x in any::<Felt252>()) {
let mut bytes = x.to_le_bytes();
bytes.reverse();
let y = &Felt252::from_bytes_be(&bytes);
prop_assert_eq!(x, y);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn to_le_digits(ref x in any::<Felt252>()) {
let digits: [u64; 4] = x.to_le_digits();
let mut bytes: Vec<_> = digits
.into_iter()
.flat_map(|x| x.to_le_bytes())
.collect();
bytes.reverse();
let y = &Felt252::from_bytes_be(&bytes);
prop_assert_eq!(x, y);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn to_u128_ok(x in any::<u128>()) {
let y = Felt252::from(x);
let y = y.to_u128();
prop_assert_eq!(Some(x), y);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn to_u128_out_of_range(x in nonzero_felt252()) {
let y = x + Felt252::from(u128::MAX);
let y = y.to_u128();
prop_assert_eq!(None, y);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn from_bytes_be_in_range(ref x in any::<[u8; 40]>()) {
let x = Felt252::from_bytes_be(x);
let max_felt = Felt252::max_value();
prop_assert!(x <= max_felt);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn neg_in_range(x in any::<Felt252>()) {
let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap();
let neg = -x.clone();
let as_uint = &neg.to_biguint();
prop_assert!(as_uint < p);
let neg = -&x;
let as_uint = &neg.to_biguint();
prop_assert!(as_uint < p);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn sub(ref x in any::<Felt252>(), ref y in any::<Felt252>()) {
let (x_int, y_int) = (&x.to_biguint(), &y.to_biguint());
let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap();
let sub_xy = x - y;
prop_assert!(&sub_xy.to_biguint() < p);
prop_assert_eq!(Felt252::from(p + x_int - y_int), sub_xy);
let sub_yx = y - x;
prop_assert!(&sub_yx.to_biguint() < p);
prop_assert_eq!(Felt252::from(p + y_int - x_int), sub_yx);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn sub_assign_in_range(mut x in any::<Felt252>(), y in any::<Felt252>()) {
let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap();
x -= y.clone();
let as_uint = &x.to_biguint();
prop_assert!(as_uint < p, "{}", as_uint);
x -= &y;
let as_uint = &x.to_biguint();
prop_assert!(as_uint < p, "{}", as_uint);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn mul(ref x in any::<Felt252>(), ref y in any::<Felt252>()) {
let xy_int = x.to_biguint() * y.to_biguint();
let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap();
let (xy, yx) = (x * y, y * x);
prop_assert_eq!(&xy, &yx);
prop_assert_eq!(xy.to_biguint(), xy_int.mod_floor(p));
prop_assert!(&xy.to_biguint() < p);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn mul_assign_in_range(mut x in any::<Felt252>(), y in any::<Felt252>()) {
let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap();
x *= &y;
let as_uint = &x.to_biguint();
prop_assert!(as_uint < p, "{}", as_uint);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn div_is_mul_inv(ref x in any::<Felt252>(), ref y in nonzero_felt252()) {
let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap();
prop_assume!(!y.is_zero());
let q = x / y;
let as_uint = &q.to_biguint();
prop_assert!(as_uint < p, "{}", as_uint);
prop_assert_eq!(&(q * y), x);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn shift_left_in_range(value in any::<Felt252>(), shift_amount in 0..1000_u32){
let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap();
let result = (value.clone() << shift_amount).to_biguint();
prop_assert!(&result < p);
let result = (&value << shift_amount).to_biguint();
prop_assert!(&result < p);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn shift_right_in_range(value in any::<Felt252>(), shift_amount in 0..1000_u32){
let result = (value >> shift_amount).to_biguint();
let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap();
prop_assert!(&result < p);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn shift_right_assign_in_range(mut value in any::<Felt252>(), shift_amount in 0..1000_usize){
let p = BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap();
value >>= shift_amount;
prop_assert!(value.to_biguint() < p);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn bitand_in_range(x in any::<Felt252>(), y in any::<Felt252>()){
let p = BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap();
let result = x & &y;
result.to_biguint();
prop_assert!(result.to_biguint() < p);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn bitor_in_range(x in any::<Felt252>(), y in any::<Felt252>()){
let p = BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap();
let result = &x | &y;
prop_assert!(result.to_biguint() < p);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn bitxor_in_range(x in any::<Felt252>(), y in any::<Felt252>()){
let p = BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap();
let result = &x ^ &y;
prop_assert!(result.to_biguint() < p);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn pow_in_range(base in any::<Felt252>(), exp in 0..100_u32){
let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap();
let result = Pow::pow(base.clone(), exp);
let as_uint = &result.to_biguint();
prop_assert!(as_uint < p, "{}", as_uint);
let result = Pow::pow(&base, exp);
let as_uint = &result.to_biguint();
prop_assert!(as_uint < p, "{}", as_uint);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn pow_felt_in_range(base in any::<Felt252>(), exponent in any::<Felt252>()){
let p = BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap();
let result = Pow::pow(&base, &exponent);
let as_uint = result.to_biguint();
prop_assert!(as_uint < p, "{}", as_uint);
let result: Felt252 = Pow::pow(&base, &exponent);
let as_uint = result.to_biguint();
prop_assert!(as_uint < p, "{}", as_uint);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn sum_in_range(x in any::<Felt252>(), y in any::<Felt252>()){
let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap();
let result = x + y;
let as_uint = &result.to_biguint();
prop_assert!(as_uint < p, "{}", as_uint);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn rem_in_range(x in any::<Felt252>(), y in nonzero_felt252()) {
let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap();
let result = x.clone() % y.clone();
let as_uint = &result.to_biguint();
prop_assert!(as_uint < p, "{}", as_uint);
let result = x % &y;
let as_uint = &result.to_biguint();
prop_assert!(as_uint < p, "{}", as_uint);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn from_u64_and_to_u64_primitive(x in any::<u64>()) {
let x_felt:Felt252 = Felt252::from_u64(x).unwrap();
let x_u64:u64 = Felt252::to_u64(&x_felt).unwrap();
prop_assert_eq!(x, x_u64);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn from_i64_and_to_i64_primitive(x in any::<u32>()) {
let x: i64 = x as i64;
let x_felt:Felt252 = Felt252::from_i64(x).unwrap();
let x_i64:i64 = Felt252::to_i64(&x_felt).unwrap();
prop_assert_eq!(x, x_i64);
}
#[test]
fn lcm_doesnt_panic(x in any::<Felt252>(), y in any::<Felt252>()) {
let lcm = x.lcm(&y);
prop_assert!(lcm == cmp::max(x, y));
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn is_multiple_of_doesnt_panic(x in any::<Felt252>(), y in any::<Felt252>()) {
prop_assert!(x.is_multiple_of(&y));
}
#[test]
fn divides_doesnt_panic(x in any::<Felt252>(), y in any::<Felt252>()) {
prop_assert!(x.divides(&y));
}
#[test]
fn gcd_doesnt_panic(x in any::<Felt252>(), y in any::<Felt252>()) {
let gcd1 = x.gcd(&y);
let gcd2 = y.gcd(&x);
prop_assert_eq!(gcd1, gcd2);
}
#[test]
fn is_even(x in any::<Felt252>()) {
prop_assert_eq!(x.is_even(), x.to_biguint().is_even());
}
#[test]
fn is_odd(x in any::<Felt252>()) {
prop_assert_eq!(x.is_odd(), x.to_biguint().is_odd());
}
#[test]
fn zero_additive_identity(ref x in any::<Felt252>()) {
let zero = Felt252::zero();
prop_assert_eq!(x, &(x + &zero));
prop_assert_eq!(x, &(&zero + x));
}
#[test]
fn one_multiplicative_identity(ref x in any::<Felt252>()) {
let one = Felt252::one();
prop_assert_eq!(x, &(x * &one));
prop_assert_eq!(x, &(&one * x));
}
#[test]
fn felt_is_always_positive(x in any::<Felt252>()) {
prop_assert!(x.is_positive())
}
#[test]
fn felt_is_never_negative(x in any::<Felt252>()) {
prop_assert!(!x.is_negative())
}
#[test]
fn non_zero_felt_signum_is_always_one(ref x in nonzero_felt252()) {
let one = Felt252::one();
prop_assert_eq!(x.signum(), one)
}
#[test]
fn sub_abs(x in any::<Felt252>(), y in any::<Felt252>()) {
let expected_abs_sub = if x > y {&x - &y} else {&y - &x};
prop_assert_eq!(x.abs_sub(&y), expected_abs_sub)
}
#[test]
fn abs(x in any::<Felt252>()) {
prop_assert_eq!(&x, &x.abs())
}
#[test]
fn modpow_in_range(x in any::<Felt252>(), y in any::<Felt252>()) {
let p = BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap();
let p_felt = Felt252::max_value();
#[allow(deprecated)]
let modpow = x.modpow(&y, &p_felt).to_biguint();
prop_assert!(modpow < p, "{}", modpow);
}
#[test]
fn sqrt_in_range(x in any::<Felt252>()) {
let p = BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap();
let sqrt = x.sqrt().to_biguint();
prop_assert!(sqrt < p, "{}", sqrt);
}
#[test]
fn sqrt_is_inv_square(x in any::<Felt252>()) {
let x_sq = &x * &x;
let sqrt = x_sq.sqrt();
if sqrt != x {
prop_assert_eq!(Felt252::max_value() - sqrt + 1_usize, x);
} else {
prop_assert_eq!(sqrt, x);
}
}
#[test]
fn add_to_u64(x in any::<u64>(), ref felt in any::<Felt252>()) {
let sum = (felt + x).to_u64();
prop_assert_eq!(x + felt, sum);
}
#[test]
fn add_to_u64_extremes(x in any::<u64>()) {
let big_zero = &Felt252::zero();
let big_max = &Felt252::max_value();
let big_min = &(big_zero + (i64::MIN as usize));
let sum_max = (big_max + x).to_u64();
prop_assert_eq!(x + big_max, sum_max);
let sum_min = (big_min + x).to_u64();
prop_assert_eq!(x + big_min, sum_min);
let sum_zero = (big_zero + x).to_u64();
prop_assert_eq!(x + big_zero, sum_zero);
}
#[test]
fn add_u32_in_range(x in any::<Felt252>(), y in any::<u32>()) {
let p = BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap();
let x_add_y = (x + y).to_biguint();
prop_assert!(x_add_y < p, "{}", x_add_y);
}
#[test]
fn add_u32_is_inv_sub(x in any::<Felt252>(), y in any::<u32>()) {
let expected_y = (x.clone() + y - x).to_u32().unwrap();
prop_assert_eq!(expected_y, y, "{}", expected_y);
}
#[test]
fn sub_u32_in_range(x in any::<Felt252>(), y in any::<u32>()) {
let p = BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap();
let x_sub_y = (x - y).to_biguint();
prop_assert!(x_sub_y < p, "{}", x_sub_y);
}
#[test]
fn sub_u32_is_inv_add(x in any::<Felt252>(), y in any::<u32>()) {
prop_assert_eq!(x.clone() - y + y, x)
}
#[test]
fn sub_usize_in_range(x in any::<Felt252>(), y in any::<usize>()) {
let p = BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap();
let x_sub_y = (x - y).to_biguint();
prop_assert!(x_sub_y < p, "{}", x_sub_y);
}
#[test]
fn sub_usize_is_inv_add(x in any::<Felt252>(), y in any::<usize>()) {
prop_assert_eq!(x.clone() - y + y, x)
}
#[test]
fn add_in_range(x in any::<Felt252>(), y in any::<Felt252>()) {
let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap();
let sub = x + y;
let as_uint = &sub.to_biguint();
prop_assert!(as_uint < p, "{}", as_uint);
}
#[test]
fn add_is_inv_sub(ref x in any::<Felt252>(), ref y in any::<Felt252>()) {
let expected_y = x + y - x;
prop_assert_eq!(&expected_y, y, "{}", y);
}
#[test]
fn add_assign_in_range(mut x in any::<Felt252>(), y in any::<Felt252>()) {
let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap();
x += y.clone();
let as_uint = &x.to_biguint();
prop_assert!(as_uint < p, "{}", as_uint);
x += &y;
let as_uint = &x.to_biguint();
prop_assert!(as_uint < p, "{}", as_uint);
}
}
#[rstest]
fn add_to_u64_edge_cases(
#[values(0, 1, u64::MAX)] x: u64,
#[values(-2, -1, 0, 1, 1i128.neg(), i64::MIN as i128, u64::MAX as i128, u64::MAX as i128 + 1, (u64::MAX as i128).neg())]
y: i128,
) {
let y = Felt252::from(y);
assert_eq!(x + &y, (&y + x).to_u64());
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn sum_zeros_in_range() {
let x = Felt252::new(0);
let y = Felt252::new(0);
let z = Felt252::new(0);
assert_eq!(x + y, z)
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn mul_zeros_in_range() {
let x = Felt252::new(0);
let y = Felt252::new(0);
let z = Felt252::new(0);
assert_eq!(x * y, z)
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn bit_and_zeros_in_range() {
let x = Felt252::new(0);
let y = Felt252::new(0);
let z = Felt252::new(0);
assert_eq!(&x & &y, z)
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn bit_or_zeros_in_range() {
let x = Felt252::new(0);
let y = Felt252::new(0);
let z = Felt252::new(0);
assert_eq!(&x | &y, z)
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn bit_xor_zeros_in_range() {
let x = Felt252::new(0);
let y = Felt252::new(0);
let z = Felt252::new(0);
assert_eq!(&x ^ &y, z)
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn upper_bound() {
let prime = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap();
let unit = BigUint::one();
let felt_max_value = Felt252::max_value().to_biguint();
assert_eq!(prime - unit, felt_max_value)
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn lower_bound() {
let zero = BigUint::zero();
let felt_min_value = Felt252::min_value().to_biguint();
assert_eq!(zero, felt_min_value)
}
#[test]
fn zero_value() {
let zero = BigUint::zero();
let felt_zero = Felt252::zero().to_biguint();
assert_eq!(zero, felt_zero)
}
#[test]
fn is_zero() {
let felt_zero = Felt252::zero();
let felt_non_zero = Felt252::new(3);
assert!(felt_zero.is_zero());
assert!(!felt_non_zero.is_zero())
}
#[test]
fn one_value() {
let one = BigUint::one();
let felt_one = Felt252::one().to_biguint();
assert_eq!(one, felt_one)
}
#[test]
fn is_one() {
let felt_one = Felt252::one();
let felt_non_one = Felt252::new(8);
assert!(felt_one.is_one());
assert!(!felt_non_one.is_one())
}
#[test]
fn signum_of_zero_is_zero() {
let zero = Felt252::zero();
assert_eq!(&zero.signum(), &zero)
}
#[test]
fn from_bytes_ne() {
let expected = Felt252::zero();
let bytes = [0; 32];
let got = Felt252::from_bytes_ne(&bytes);
assert_eq!(got, expected);
}
}