diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 14beedcd4a..3fe9ae3a79 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -14,11 +14,9 @@ jobs: build: runs-on: ubuntu-20.04 steps: - - name: Install Rust 1.66.1 - uses: actions-rs/toolchain@v1 + - name: Install Rust + uses: dtolnay/rust-toolchain@1.69.0 with: - toolchain: 1.66.1 - override: true components: rustfmt, clippy - uses: actions/checkout@v3 - name: Python3 Build diff --git a/.github/workflows/hint_accountant.yml b/.github/workflows/hint_accountant.yml index 87ea94c036..a73d2bb272 100644 --- a/.github/workflows/hint_accountant.yml +++ b/.github/workflows/hint_accountant.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@1.66.1 + uses: dtolnay/rust-toolchain@1.69.0 - name: Set up Cargo cache uses: Swatinem/rust-cache@v2 - name: Checkout diff --git a/.github/workflows/hyperfine.yml b/.github/workflows/hyperfine.yml index f80fc333ff..bfeb794c0d 100644 --- a/.github/workflows/hyperfine.yml +++ b/.github/workflows/hyperfine.yml @@ -74,9 +74,7 @@ jobs: - name: Install Rust if: ${{ steps.cache.outputs.cache-hit != 'true' }} - uses: dtolnay/rust-toolchain@stable - with: - toolchain: 1.66.1 + uses: dtolnay/rust-toolchain@1.69.0 - name: Checkout if: ${{ steps.cache.outputs.cache-hit != 'true' }} diff --git a/.github/workflows/iai_main.yml b/.github/workflows/iai_main.yml index ac19394879..6c382e650e 100644 --- a/.github/workflows/iai_main.yml +++ b/.github/workflows/iai_main.yml @@ -9,11 +9,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.66.1 - override: true - profile: minimal + uses: dtolnay/rust-toolchain@1.69.0 - name: Python3 Build uses: actions/setup-python@v4 with: diff --git a/.github/workflows/iai_pr.yml b/.github/workflows/iai_pr.yml index 25b7bb063f..83ee415a1b 100644 --- a/.github/workflows/iai_pr.yml +++ b/.github/workflows/iai_pr.yml @@ -9,11 +9,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.66.1 - override: true - profile: minimal + uses: dtolnay/rust-toolchain@1.69.0 - name: Python3 Build uses: actions/setup-python@v4 with: @@ -46,11 +42,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.66.1 - override: true - profile: minimal + uses: dtolnay/rust-toolchain@1.69.0 - name: Python3 Build uses: actions/setup-python@v4 with: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 7fb6b34c56..943713cbd8 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -13,11 +13,7 @@ jobs: - name: Checkout sources uses: actions/checkout@v2 - name: Install stable toolchain - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true + uses: dtolnay/rust-toolchain@1.69.0 - name: Publish crate cairo-take_until_unbalanced env: CRATES_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} @@ -34,4 +30,3 @@ jobs: env: CRATES_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} run: cargo publish --token ${CRATES_TOKEN} --all-features -p cairo-vm - diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index da68ee1bb0..c7b9870c02 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -170,6 +170,7 @@ jobs: strategy: fail-fast: false matrix: + special-features: ["", "lambdaworks-felt"] target: [ test, test-no_std, test-wasm ] # TODO: features name: Run tests @@ -221,14 +222,14 @@ jobs: # FIXME: we need to update the Makefile to do this correctly case ${{ matrix.target }} in 'test') - cargo llvm-cov nextest --lcov --output-path lcov-${{ matrix.target }}.info --workspace --features "cairo-1-hints, test_utils" + cargo llvm-cov nextest --lcov --output-path lcov-${{ matrix.target }}.info --workspace --features "cairo-1-hints, test_utils, ${{ matrix.special_features }}" ;; 'test-no_std') - cargo llvm-cov nextest --lcov --output-path lcov-${{ matrix.target }}.info --workspace --no-default-features + cargo llvm-cov nextest --lcov --output-path lcov-${{ matrix.target }}.info --workspace --no-default-features --features "${{ matrix.special_features }}" ;; 'test-wasm') # NOTE: release mode is needed to avoid "too many locals" error - wasm-pack test --release --node vm --no-default-features + wasm-pack test --release --node vm --no-default-features --features "${{ matrix.special_features }}" ;; esac diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f6917a1ad..1573323634 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ #### Upcoming Changes +* feat: Add feature `lambdaworks-felt` to `felt` & `cairo-vm` crates [#1218](https://github.com/lambdaclass/cairo-rs/pull/1281) + + Changes under this feature: + * `Felt252` now uses _lambdaworks_' `FieldElement` internally + * BREAKING: some methods of `Felt252` were removed, namely: `modpow` and `to_bytes_be` + #### [0.7.0] - 2023-6-26 * BREAKING: Integrate `RunResources` logic into `HintProcessor` trait [#1274](https://github.com/lambdaclass/cairo-rs/pull/1274) diff --git a/Cargo.lock b/Cargo.lock index f6ddc863e8..db142159b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -177,6 +177,15 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" +[[package]] +name = "atomic-polyfill" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ff7eb3f316534d83a8a2c3d1674ace8a5a71198eba31e2e2b597833f699b28" +dependencies = [ + "critical-section", +] + [[package]] name = "atty" version = "0.2.14" @@ -282,6 +291,7 @@ dependencies = [ name = "cairo-felt" version = "0.7.0" dependencies = [ + "lambdaworks-math", "lazy_static", "num-bigint", "num-integer", @@ -961,6 +971,12 @@ dependencies = [ "itertools", ] +[[package]] +name = "critical-section" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6548a0ad5d2549e111e1f6a11a6c2e2d00ce6a3dafe22948d67c2b443f775e52" + [[package]] name = "crossbeam-channel" version = "0.5.8" @@ -1358,6 +1374,15 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1377,6 +1402,19 @@ dependencies = [ "serde", ] +[[package]] +name = "heapless" +version = "0.7.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db04bc24a18b9ea980628ecf00e6c0264f3c1426dac36c00cb49b6fbad8b0743" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "spin 0.9.8", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.3.3" @@ -1571,13 +1609,35 @@ dependencies = [ "regex", ] +[[package]] +name = "lambdaworks-gpu" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b233fcb8213965c77f67c07c3e8294539701a2b5fd55a4e05b48ab2210098063" +dependencies = [ + "rand", + "thiserror", +] + +[[package]] +name = "lambdaworks-math" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "000838fc32d61770baebf9e3068606722258cee1490c43f72b1d751bc3c8994d" +dependencies = [ + "heapless", + "lambdaworks-gpu", + "rand", + "thiserror", +] + [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" dependencies = [ - "spin", + "spin 0.5.2", ] [[package]] @@ -2427,6 +2487,15 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + [[package]] name = "sprs" version = "0.7.1" @@ -2438,6 +2507,12 @@ dependencies = [ "num-traits 0.1.43", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "starknet-crypto" version = "0.5.1" diff --git a/README.md b/README.md index b708c2ccb4..88f03ffa45 100644 --- a/README.md +++ b/README.md @@ -81,14 +81,26 @@ These dependencies are only necessary in order to run the original VM, compile C ## 🚀 Usage -### Running cairo-rs +### Adding cairo-rs as a dependency + +You can add the following to your rust project's `Cargo.toml`: + +```toml +cairo-vm = { version = '0.7.0', features = ["lambdaworks-felt"] } +``` + +The `features = ["lambdaworks-felt"]` part adds usage of [`lambdaworks-math`](https://github.com/lambdaclass/lambdaworks) as the backend for `Felt252`. This improves performance by more than 20%, and will be the default in the future. + +### Running cairo-rs from CLI To run programs from the command line, first compile the repository from the cairo-vm-cli folder: ```bash -cd cairo-vm-cli; cargo build --release; cd .. +cd cairo-vm-cli; cargo build --release -F lambdaworks-felt; cd .. ``` +The `-F lambdaworks-felt` part adds usage of [`lambdaworks-math`](https://github.com/lambdaclass/lambdaworks) as the backend for `Felt252`. This improves performance by more than 20%, and will be the default in the future. + Once the binary is built, it can be found in `target/release/` under the name `cairo-rvm-cli`. To compile a program, use `cairo-compile [path_to_the_.cairo_file] --output [desired_path_of_the_compiled_.json_file]`. For example: diff --git a/deps/parse-hyperlinks/src/lib.rs b/deps/parse-hyperlinks/src/lib.rs index 8da099d0c5..5bbbc7dd10 100644 --- a/deps/parse-hyperlinks/src/lib.rs +++ b/deps/parse-hyperlinks/src/lib.rs @@ -15,7 +15,7 @@ use nom::IResult; /// ``` /// use nom::bytes::complete::tag; /// use nom::sequence::delimited; -/// use parse_hyperlinks::take_until_unbalanced; +/// use cairo_take_until_unbalanced::take_until_unbalanced; /// /// let mut parser = delimited(tag("<"), take_until_unbalanced('<', '>'), tag(">")); /// assert_eq!(parser("<inside>abc"), Ok(("abc", "inside"))); diff --git a/felt/Cargo.toml b/felt/Cargo.toml index 51a4b2a8f0..37686c2141 100644 --- a/felt/Cargo.toml +++ b/felt/Cargo.toml @@ -9,6 +9,7 @@ description = "Field elements representation for the Cairo VM" default = ["std"] std = [] alloc = [] +lambdaworks-felt = ["dep:lambdaworks-math"] [dependencies] num-integer = { version = "0.1.45", default-features = false } @@ -18,6 +19,7 @@ lazy_static = { version = "1.4.0", default-features = false, features = [ "spin_no_std", ] } serde = { version = "1.0", features = ["derive"], default-features = false } +lambdaworks-math = { version = "0.1.1", default-features = false, optional=true } [dev-dependencies] proptest = "1.1.0" diff --git a/felt/src/arbitrary.rs b/felt/src/arbitrary_bigint_felt.rs similarity index 100% rename from felt/src/arbitrary.rs rename to felt/src/arbitrary_bigint_felt.rs diff --git a/felt/src/arbitrary_lambdaworks.rs b/felt/src/arbitrary_lambdaworks.rs new file mode 100644 index 0000000000..11b9df1aa4 --- /dev/null +++ b/felt/src/arbitrary_lambdaworks.rs @@ -0,0 +1,47 @@ +use lambdaworks_math::{field::element::FieldElement, unsigned_integer::element::UnsignedInteger}; +use num_traits::Zero; +use proptest::prelude::*; + +use crate::{Felt252, FIELD_HIGH, FIELD_LOW}; + +/// Returns a [`Strategy`] that generates any valid Felt252 +fn any_felt252() -> impl Strategy { + (0..=FIELD_HIGH) + // turn range into `impl Strategy` + .prop_map(|x| x) + // choose second 128-bit limb capped by first one + .prop_flat_map(|high| { + let low = if high == FIELD_HIGH { + (0..FIELD_LOW).prop_map(|x| x).sboxed() + } else { + any::().sboxed() + }; + (Just(high), low) + }) + // turn (u128, u128) into limbs array and then into Felt252 + .prop_map(|(high, low)| { + let limbs = [ + (high >> 64) as u64, + (high & ((1 << 64) - 1)) as u64, + (low >> 64) as u64, + (low & ((1 << 64) - 1)) as u64, + ]; + FieldElement::new(UnsignedInteger::from_limbs(limbs)) + }) + .prop_map(|value| Felt252 { value }) +} + +/// Returns a [`Strategy`] that generates any nonzero Felt252 +pub fn nonzero_felt252() -> impl Strategy { + any_felt252().prop_filter("is zero", |x| !x.is_zero()) +} + +impl Arbitrary for Felt252 { + type Parameters = (); + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + any_felt252().sboxed() + } + + type Strategy = SBoxedStrategy; +} diff --git a/felt/src/bigint_felt.rs b/felt/src/bigint_felt.rs index f4fa998d6a..2b036dfa12 100644 --- a/felt/src/bigint_felt.rs +++ b/felt/src/bigint_felt.rs @@ -12,7 +12,7 @@ use core::{ }, }; -use crate::{FeltOps, ParseFeltError}; +use crate::{lib_bigint_felt::FeltOps, ParseFeltError}; pub const FIELD_HIGH: u128 = (1 << 123) + (17 << 64); // this is equal to 10633823966279327296825105735305134080 pub const FIELD_LOW: u128 = 1; @@ -840,12 +840,6 @@ impl fmt::Debug for FeltBigInt { } } -impl fmt::Display for ParseFeltError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{ParseFeltError:?}") - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/felt/src/lib.rs b/felt/src/lib.rs index 6d9f09b9e2..d4a8d1e56f 100644 --- a/felt/src/lib.rs +++ b/felt/src/lib.rs @@ -1,1665 +1,38 @@ #![cfg_attr(not(feature = "std"), no_std)] + #[allow(unused_imports)] #[macro_use] #[cfg(all(not(feature = "std"), feature = "alloc"))] pub extern crate alloc; +#[cfg(all(test, not(feature = "lambdaworks-felt")))] +mod arbitrary_bigint_felt; +#[cfg(all(test, feature = "lambdaworks-felt"))] +mod arbitrary_lambdaworks; +#[cfg(not(feature = "lambdaworks-felt"))] mod bigint_felt; +#[cfg(not(feature = "lambdaworks-felt"))] +mod lib_bigint_felt; +#[cfg(feature = "lambdaworks-felt")] +mod lib_lambdaworks; -#[cfg(test)] -pub mod arbitrary; - -use 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::fmt; -use core::{ - convert::Into, - fmt, - iter::Sum, - ops::{ - Add, AddAssign, BitAnd, BitOr, BitXor, Div, Mul, MulAssign, Neg, Rem, Shl, Shr, ShrAssign, - Sub, SubAssign, - }, -}; +#[cfg(feature = "lambdaworks-felt")] +pub use lib_lambdaworks::Felt252; -#[cfg(all(not(feature = "std"), feature = "alloc"))] -use alloc::{string::String, vec::Vec}; +#[cfg(not(feature = "lambdaworks-felt"))] +pub use lib_bigint_felt::Felt252; pub const PRIME_STR: &str = "0x800000000000011000000000000000000000000000000000000000000000001"; // in decimal, this is equal to 3618502788666131213697322783095070105623107215331596699973092056135872020481 - -pub(crate) trait FeltOps { - fn new>>(value: T) -> Self; - - fn modpow( - &self, - exponent: &FeltBigInt, - modulus: &FeltBigInt, - ) -> Self; - - fn iter_u64_digits(&self) -> U64Digits; - - #[cfg(any(feature = "std", feature = "alloc"))] - fn to_signed_bytes_le(&self) -> Vec; - - #[cfg(any(feature = "std", feature = "alloc"))] - fn to_bytes_be(&self) -> Vec; - - fn parse_bytes(buf: &[u8], radix: u32) -> Option>; - - fn from_bytes_be(bytes: &[u8]) -> Self; - - #[cfg(any(feature = "std", feature = "alloc"))] - fn to_str_radix(&self, radix: u32) -> String; - - /// Converts [`Felt252`] into a [`BigInt`] number in the range: `(- FIELD / 2, FIELD / 2)`. - /// - /// # Examples - /// - /// ``` - /// # use crate::cairo_felt::Felt252; - /// # use num_bigint::BigInt; - /// # use num_traits::Bounded; - /// let positive = Felt252::new(5); - /// assert_eq!(positive.to_bigint(), Into::::into(5)); - /// - /// let negative = Felt252::max_value(); - /// assert_eq!(negative.to_bigint(), Into::::into(-1)); - /// ``` - fn to_signed_felt(&self) -> BigInt; - - // Converts [`Felt252`]'s representation directly into a [`BigInt`]. - // Equivalent to doing felt.to_biguint().to_bigint(). - fn to_bigint(&self) -> BigInt; - - /// Converts [`Felt252`] into a [`BigUint`] number. - /// - /// # Examples - /// - /// ``` - /// # use crate::cairo_felt::Felt252; - /// # use num_bigint::BigUint; - /// # use num_traits::{Num, Bounded}; - /// let positive = Felt252::new(5); - /// assert_eq!(positive.to_biguint(), Into::::into(5_u32)); - /// - /// let negative = Felt252::max_value(); - /// assert_eq!(negative.to_biguint(), BigUint::from_str_radix("800000000000011000000000000000000000000000000000000000000000000", 16).unwrap()); - /// ``` - 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") - }; -} +pub const FIELD_HIGH: u128 = (1 << 123) + (17 << 64); // this is equal to 10633823966279327296825105735305134080 +pub const FIELD_LOW: u128 = 1; #[derive(Clone, Debug, PartialEq, Eq)] pub struct ParseFeltError; -#[derive(Eq, Hash, PartialEq, PartialOrd, Ord, Clone, Deserialize, Default, Serialize)] -pub struct Felt252 { - value: FeltBigInt, -} - -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 Felt252 { - pub fn new>(value: T) -> Self { - value.into() - } - 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"))] - pub fn to_signed_bytes_le(&self) -> Vec { - self.value.to_signed_bytes_le() - } - #[cfg(any(feature = "std", feature = "alloc"))] - pub fn to_bytes_be(&self) -> Vec { - self.value.to_bytes_be() - } - - pub fn parse_bytes(buf: &[u8], radix: u32) -> Option { - Some(Self { - value: FeltBigInt::parse_bytes(buf, radix)?, - }) - } - pub fn from_bytes_be(bytes: &[u8]) -> Self { - Self { - value: FeltBigInt::from_bytes_be(bytes), - } - } - #[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 { - // Based on Tonelli-Shanks' algorithm for finding square roots - // and sympy's library implementation of said algorithm. - if self.is_zero() || self.is_one() { - return self.clone(); - } - - let max_felt = Felt252::max_value(); - let trailing_prime = Felt252::max_value() >> 192; // 0x800000000000011 - - 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 ≡ -1 (mod CAIRO_PRIME) - 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 for Felt252 { - type Output = Self; - fn add(self, rhs: u32) -> Self { - Self { - value: self.value + rhs, - } - } -} - -impl Add for Felt252 { - type Output = Self; - fn add(self, rhs: usize) -> Self { - Self { - value: self.value + rhs, - } - } -} - -impl<'a> Add for &'a Felt252 { - type Output = Felt252; - fn add(self, rhs: usize) -> Self::Output { - Self::Output { - value: &self.value + rhs, - } - } -} - -impl Add for &Felt252 { - type Output = Felt252; - fn add(self, rhs: u64) -> Self::Output { - Self::Output { - value: &self.value + rhs, - } - } -} - -// This is special cased and optimized compared to the obvious implementation -// due to `pc_update` relying on this, which makes it a major bottleneck for -// execution. Testing for this function is extensive, comprised of explicit -// edge and special cases testing and property tests, all comparing to the -// more intuitive `(rhs + self).to_u64()` implementation. -// This particular implementation is much more complex than a slightly more -// intuitive one based on a single match. However, this is 8-62% faster -// depending on the case being bencharked, with an average of 32%, so it's -// worth it. -impl Add<&Felt252> for u64 { - type Output = Option; - - fn add(self, rhs: &Felt252) -> Option { - 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); - - // Iterate through the 64 bits digits in little-endian order to - // characterize how the sum will behave. - let mut rhs_digits = rhs.iter_u64_digits(); - // No digits means `rhs` is `0`, so the sum is simply `self`. - let Some(low) = rhs_digits.next() else { - return Some(self); - }; - // A single digit means this is effectively the sum of two `u64` numbers. - let Some(h0) = rhs_digits.next() else { - return self.checked_add(low) - }; - // Now we need to compare the 3 most significant digits. - // There are two relevant cases from now on, either `rhs` behaves like a - // substraction of a `u64` or the result of the sum falls out of range. - let (h1, h2) = (rhs_digits.next()?, rhs_digits.next()?); - match (h0, h1, h2) { - // The 3 MSB only match the prime for Felt252::max_value(), which is -1 - // in the signed field, so this is equivalent to substracting 1 to `self`. - #[allow(clippy::suspicious_arithmetic_impl)] - PRIME_DIGITS_LE_HI => self.checked_sub(1), - // For the remaining values between `[-u64::MAX..0]` (where `{0, -1}` have - // already been covered) the MSB matches that of `PRIME - u64::MAX`. - // Because we're in the negative number case, we count down. Because `0` - // and `-1` correspond to different MSBs, `0` and `1` in the LSB are less - // than `-u64::MAX`, the smallest value we can add to (read, substract it's - // magnitude from) a `u64` number, meaning we exclude them from the valid - // case. - // For the remaining range, we make take the absolute value module-2 while - // correcting by substracting `1` (note we actually substract `2` because - // the absolute value itself requires substracting `1`. - #[allow(clippy::suspicious_arithmetic_impl)] - PRIME_MINUS_U64_MAX_DIGITS_LE_HI if low >= 2 => { - (self).checked_sub(u64::MAX - (low - 2)) - } - // Any other case will result in an addition that is out of bounds, so - // the addition fails, returning `None`. - _ => 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>(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 for Felt252 { - type Output = Self; - fn sub(self, rhs: u32) -> Self { - Self { - value: self.value - rhs, - } - } -} - -impl<'a> Sub for &'a Felt252 { - type Output = Felt252; - fn sub(self, rhs: u32) -> Self::Output { - Self::Output { - value: &self.value - rhs, - } - } -} - -impl Sub 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 for Felt252 { - type Output = Self; - fn pow(self, rhs: u32) -> Self { - Self { - value: self.value.pow(rhs), - } - } -} - -impl<'a> Pow 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 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 { - 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 for Felt252 { - type Output = Self; - fn shl(self, rhs: u32) -> Self { - Self { - value: self.value << rhs, - } - } -} - -impl<'a> Shl for &'a Felt252 { - type Output = Felt252; - fn shl(self, rhs: u32) -> Self::Output { - Self::Output { - value: &self.value << rhs, - } - } -} - -impl Shl for Felt252 { - type Output = Self; - fn shl(self, rhs: usize) -> Self { - Self { - value: self.value << rhs, - } - } -} - -impl<'a> Shl for &'a Felt252 { - type Output = Felt252; - fn shl(self, rhs: usize) -> Self::Output { - Self::Output { - value: &self.value << rhs, - } - } -} - -impl Shr for Felt252 { - type Output = Self; - fn shr(self, rhs: u32) -> Self { - Self { - value: self.value >> rhs, - } - } -} - -impl<'a> Shr for &'a Felt252 { - type Output = Felt252; - fn shr(self, rhs: u32) -> Self::Output { - Self::Output { - value: &self.value >> rhs, - } - } -} - -impl ShrAssign 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 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 { - self.value.to_u128() - } - - fn to_u64(&self) -> Option { - self.value.to_u64() - } - - fn to_i64(&self) -> Option { - self.value.to_i64() - } -} - -impl FromPrimitive for Felt252 { - fn from_u64(n: u64) -> Option { - FeltBigInt::from_u64(n).map(|n| Self { value: n }) - } - - fn from_i64(n: i64) -> Option { - FeltBigInt::from_i64(n).map(|n| Self { value: n }) - } -} - -impl fmt::Display for Felt252 { +impl fmt::Display for ParseFeltError { 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() {} - fn assertion() { - assert_felt_ops::<$type>(); - } - }; - }; -} - -macro_rules! assert_felt_impl { - ($type:ty) => { - const _: () = { - fn assert_add() {} - fn assert_add_ref<'a, T: Add<&'a $type>>() {} - fn assert_add_u32>() {} - fn assert_add_usize>() {} - fn assert_add_assign() {} - fn assert_add_assign_ref<'a, T: AddAssign<&'a $type>>() {} - fn assert_sum>() {} - fn assert_neg() {} - fn assert_sub() {} - fn assert_sub_ref<'a, T: Sub<&'a $type>>() {} - fn assert_sub_assign() {} - fn assert_sub_assign_ref<'a, T: SubAssign<&'a $type>>() {} - fn assert_sub_u32>() {} - fn assert_sub_usize>() {} - fn assert_mul() {} - fn assert_mul_ref<'a, T: Mul<&'a $type>>() {} - fn assert_mul_assign_ref<'a, T: MulAssign<&'a $type>>() {} - fn assert_pow_u32>() {} - fn assert_pow_felt<'a, T: Pow<&'a $type>>() {} - fn assert_div() {} - fn assert_ref_div>() {} - fn assert_rem() {} - fn assert_rem_ref<'a, T: Rem<&'a $type>>() {} - fn assert_zero() {} - fn assert_one() {} - fn assert_bounded() {} - fn assert_num() {} - fn assert_integer() {} - fn assert_signed() {} - fn assert_shl_u32>() {} - fn assert_shl_usize>() {} - fn assert_shr_u32>() {} - fn assert_shr_assign_usize>() {} - fn assert_bitand() {} - fn assert_bitand_ref<'a, T: BitAnd<&'a $type>>() {} - fn assert_ref_bitand>() {} - fn assert_bitor() {} - fn assert_bitxor() {} - fn assert_from_primitive() {} - fn assert_to_primitive() {} - fn assert_display() {} - fn assert_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); -assert_felt_impl!(FeltBigInt); -assert_felt_impl!(Felt252); - -#[cfg(test)] -mod test { - use super::*; - use crate::arbitrary::nonzero_felt252; - use core::cmp; - use rstest::rstest; - - use proptest::prelude::*; - - proptest! { - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - // Property-based test that ensures, for 100 felt values that are randomly generated - // each time tests are run, that a new felt doesn't fall outside the range [0, p]. - // In this and some of the following tests, The value of {x} can be either [0] or a - // very large number, in order to try to overflow the value of {p} and thus ensure the - // modular arithmetic is working correctly. - 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 to_be_bytes(ref x in any::()) { - 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::()) { - let mut bytes = x.to_le_bytes(); - // Convert to big endian for test - 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::()) { - let digits: [u64; 4] = x.to_le_digits(); - let mut bytes: Vec<_> = digits - .into_iter() - .flat_map(|x| x.to_le_bytes()) - .collect(); - // Convert to big endian for test - 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::()) { - 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)] - // Property-based test that ensures, for 100 felt values that are randomly - // generated each time tests are run, that a felt created using Felt252::from_bytes_be doesn't - // fall outside the range [0, p]. - // In this and some of the following tests, The value of {x} can be either [0] or a very large number, - // in order to try to overflow the value of {p} and thus ensure the modular arithmetic is working correctly. - 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)] - // Property-based test that ensures, for 100 felt values that are randomly generated each time - // tests are run, that the negative of a felt doesn't fall outside the range [0, p]. - fn neg_in_range(x in any::()) { - 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); - - // test reference variant - let neg = -&x; - let as_uint = &neg.to_biguint(); - prop_assert!(as_uint < p); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - // Property-based test that ensures, for 100 {x} and {y} values that are randomly generated - // each time tests are run, that a subtraction between two felts {x} and {y} and doesn't fall - // outside the range [0, p]. The values of {x} and {y} can be either [0] or a very large number. - fn sub(ref x in any::(), ref y in any::()) { - 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)] - // Property-based test that ensures, for 100 {x} and {y} values that are randomly generated - // each time tests are run, that a subtraction with assignment between two felts {x} and {y} - // and doesn't fall outside the range [0, p]. The values of {x} and {y} can be either [0] or a very large number. - fn sub_assign_in_range(mut x in any::(), y in any::()) { - 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); - - // test reference variant - x -= &y; - let as_uint = &x.to_biguint(); - prop_assert!(as_uint < p, "{}", as_uint); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - // Property-based test that ensures, for 100 {x} and {y} values that are randomly - // generated each time tests are run, that a multiplication between two felts {x} - // and {y} and doesn't fall outside the range [0, p]. The values of {x} and {y} - // can be either [0] or a very large number. - fn mul(ref x in any::(), ref y in any::()) { - 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)] - // Property-based test that ensures, for 100 pairs of {x} and {y} values that - // are randomly generated each time tests are run, that a multiplication with - // assignment between two felts {x} and {y} and doesn't fall outside the range [0, p]. - // The values of {x} and {y} can be either [0] or a very large number. - fn mul_assign_in_range(mut x in any::(), y in any::()) { - 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)] - // Property-based test that ensures, for 100 pairs of {x} and {y} values that are - // randomly generated each time tests are run, that the result of the division of - // {x} by {y} is the inverse multiplicative of {x} --that is, multiplying the result - // by {y} returns the original number {x}. The values of {x} and {y} can be either - // [0] or a very large number. - fn div_is_mul_inv(ref x in any::(), 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)] - // Property-based test that ensures, for 100 {value}s that are randomly generated - // each time tests are run, that performing a bit shift to the left by {shift_amount} - // of bits (between 0 and 999) returns a result that is inside of the range [0, p]. - fn shift_left_in_range(value in any::(), 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)] - // Property-based test that ensures, for 100 {value}s that are randomly - // generated each time tests are run, that performing a bit shift to the right - // by {shift_amount} of bits (between 0 and 999) returns a result that is inside of the range [0, p]. - fn shift_right_in_range(value in any::(), 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)] - // Property-based test that ensures, for 100 {value}s that are randomly generated - // each time tests are run, that performing a bit shift to the right by {shift_amount} - // of bits (between 0 and 999), with assignment, returns a result that is inside of the range [0, p]. - // "With assignment" means that the result of the operation is autommatically assigned - // to the variable value, replacing its previous content. - fn shift_right_assign_in_range(mut value in any::(), 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)] - // Property based test that ensures, for 100 pairs of values {x} and {y} - // generated at random each time tests are run, that performing a BitAnd - // operation between them returns a result that is inside of the range [0, p]. - fn bitand_in_range(x in any::(), y in any::()){ - 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)] - // Property based test that ensures, for 100 pairs of values {x} and {y} - // generated at random each time tests are run, that performing a BitOr - // operation between them returns a result that is inside of the range [0, p]. - fn bitor_in_range(x in any::(), y in any::()){ - 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)] - // Property based test that ensures, for 100 pairs of values {x} and {y} - // generated at random each time tests are run, that performing a BitXor - // operation between them returns a result that is inside of the range [0, p]. - fn bitxor_in_range(x in any::(), y in any::()){ - 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)] - // Property-based test that ensures, for 100 values {x} that are randomly - // generated each time tests are run, that raising {x} to the {y}th power - // returns a result that is inside of the range [0, p]. - fn pow_in_range(base in any::(), 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); - - // test reference variant - 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)] - // Property-based test that ensures, for 100 values {x} that are randomly - // generated each time tests are run, that raising {x} to the {y}th power - // returns a result that is inside of the range [0, p]. - fn pow_felt_in_range(base in any::(), exponent in any::()){ - 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); - - // test reference variant - 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)] - // Property based test that ensures, for 100 pairs of values {x} and {y} - // generated at random each time tests are run, that performing a Sum operation - // between them returns a result that is inside of the range [0, p]. - fn sum_in_range(x in any::(), y in any::()){ - 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)] - // Property test to check that the remainder of a division between 100 pairs of - // values {x} and {y},generated at random each time tests are run, falls in the - // range [0, p]. x and y can either take the value of 0 or a large integer. - // In Cairo, the result of x / y is defined to always satisfy the equation - // (x / y) * y == x, so the remainder is 0 most of the time. - fn rem_in_range(x in any::(), 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); - - // test reference variant - 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)] - // Property based test that ensures, for 100 Felt252s {x} generated at - // random each time tests are run, that converting them into the u64 type - // returns a result that is inside of the range [0, p]. - fn from_u64_and_to_u64_primitive(x in any::()) { - 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::()) { - 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] - // Property test to check that lcm(x, y) works. Since we're operating in a prime field, lcm - // will just be the smaller number. - fn lcm_doesnt_panic(x in any::(), y in any::()) { - let lcm = x.lcm(&y); - prop_assert!(lcm == cmp::max(x, y)); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - // Property test to check that is_multiple_of(x, y) works. Since we're operating in a prime field, is_multiple_of - // will always be true - fn is_multiple_of_doesnt_panic(x in any::(), y in any::()) { - prop_assert!(x.is_multiple_of(&y)); - } - - #[test] - fn divides_doesnt_panic(x in any::(), y in any::()) { - prop_assert!(x.divides(&y)); - } - - #[test] - fn gcd_doesnt_panic(x in any::(), y in any::()) { - let gcd1 = x.gcd(&y); - let gcd2 = y.gcd(&x); - prop_assert_eq!(gcd1, gcd2); - } - - #[test] - fn is_even(x in any::()) { - prop_assert_eq!(x.is_even(), x.to_biguint().is_even()); - } - - #[test] - fn is_odd(x in any::()) { - prop_assert_eq!(x.is_odd(), x.to_biguint().is_odd()); - } - - /// Tests the additive identity of the implementation of Zero trait for felts - /// - /// ```{.text} - /// x + 0 = x ∀ x - /// 0 + x = x ∀ x - /// ``` - #[test] - fn zero_additive_identity(ref x in any::()) { - let zero = Felt252::zero(); - prop_assert_eq!(x, &(x + &zero)); - prop_assert_eq!(x, &(&zero + x)); - } - - /// Tests the multiplicative identity of the implementation of One trait for felts - /// - /// ```{.text} - /// x * 1 = x ∀ x - /// 1 * x = x ∀ x - /// ``` - #[test] - fn one_multiplicative_identity(ref x in any::()) { - 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::()) { - prop_assert!(x.is_positive()) - } - - #[test] - fn felt_is_never_negative(x in any::()) { - 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::(), y in any::()) { - 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::()) { - prop_assert_eq!(&x, &x.abs()) - } - - #[test] - fn modpow_in_range(x in any::(), y in any::()) { - let p = BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap(); - - let p_felt = Felt252::max_value(); - - let modpow = x.modpow(&y, &p_felt).to_biguint(); - prop_assert!(modpow < p, "{}", modpow); - } - - #[test] - fn sqrt_in_range(x in any::()) { - 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::()) { - 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::(), ref felt in any::()) { - let sum = (felt + x).to_u64(); - prop_assert_eq!(x + felt, sum); - } - - #[test] - fn add_to_u64_extremes(x in any::()) { - 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::(), y in any::()) { - 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::(), y in any::()) { - 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::(), y in any::()) { - 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::(), y in any::()) { - prop_assert_eq!(x.clone() - y + y, x) - } - - #[test] - fn sub_usize_in_range(x in any::(), y in any::()) { - 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::(), y in any::()) { - prop_assert_eq!(x.clone() - y + y, x) - } - - #[test] - fn add_in_range(x in any::(), y in any::()) { - 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::(), ref y in any::()) { - let expected_y = x + y - x; - prop_assert_eq!(&expected_y, y, "{}", y); - } - - #[test] - fn add_assign_in_range(mut x in any::(), y in any::()) { - 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); - - // test reference variant - 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)] - // Checks that the result of adding two zeroes is zero - 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)] - // Checks that the result of multiplying two zeroes is zero - 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)] - // Checks that the result of performing a bit and operation between zeroes is zero - 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)] - // Checks that the result of perfforming a bit or operation between zeroes is zero - 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)] - // Checks that the result of perfforming a bit xor operation between zeroes is zero - 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)] - // Tests that the maximum value a Felt252 can take is equal to (prime - 1) - 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)] - // Tests that the minimum value a Felt252 can take is equal to zero. - 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) + write!(f, "{ParseFeltError:?}") } } diff --git a/felt/src/lib_bigint_felt.rs b/felt/src/lib_bigint_felt.rs new file mode 100644 index 0000000000..18df387924 --- /dev/null +++ b/felt/src/lib_bigint_felt.rs @@ -0,0 +1,1661 @@ +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}; + +pub(crate) trait FeltOps { + fn new>>(value: T) -> Self; + + fn modpow( + &self, + exponent: &FeltBigInt, + modulus: &FeltBigInt, + ) -> Self; + + fn iter_u64_digits(&self) -> U64Digits; + + #[cfg(any(feature = "std", feature = "alloc"))] + fn to_signed_bytes_le(&self) -> Vec; + + #[cfg(any(feature = "std", feature = "alloc"))] + fn to_bytes_be(&self) -> Vec; + + fn parse_bytes(buf: &[u8], radix: u32) -> Option>; + + fn from_bytes_be(bytes: &[u8]) -> Self; + + #[cfg(any(feature = "std", feature = "alloc"))] + fn to_str_radix(&self, radix: u32) -> String; + + /// Converts [`Felt252`] into a [`BigInt`] number in the range: `(- FIELD / 2, FIELD / 2)`. + /// + /// # Examples + /// + /// ``` + /// # use crate::cairo_felt::Felt252; + /// # use num_bigint::BigInt; + /// # use num_traits::Bounded; + /// let positive = Felt252::new(5); + /// assert_eq!(positive.to_signed_felt(), Into::::into(5)); + /// + /// let negative = Felt252::max_value(); + /// assert_eq!(negative.to_signed_felt(), Into::::into(-1)); + /// ``` + fn to_signed_felt(&self) -> BigInt; + + // Converts [`Felt252`]'s representation directly into a [`BigInt`]. + // Equivalent to doing felt.to_biguint().to_bigint(). + fn to_bigint(&self) -> BigInt; + + /// Converts [`Felt252`] into a [`BigUint`] number. + /// + /// # Examples + /// + /// ``` + /// # use crate::cairo_felt::Felt252; + /// # use num_bigint::BigUint; + /// # use num_traits::{Num, Bounded}; + /// let positive = Felt252::new(5); + /// assert_eq!(positive.to_biguint(), Into::::into(5_u32)); + /// + /// let negative = Felt252::max_value(); + /// assert_eq!(negative.to_biguint(), BigUint::from_str_radix("800000000000011000000000000000000000000000000000000000000000000", 16).unwrap()); + /// ``` + 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") + }; +} + +#[derive(Eq, Hash, PartialEq, PartialOrd, Ord, Clone, Deserialize, Default, Serialize)] +pub struct Felt252 { + pub(crate) value: FeltBigInt, +} + +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 for Felt252 { + fn from(flag: bool) -> Self { + if flag { + Self::one() + } else { + Self::zero() + } + } +} + +impl Felt252 { + pub fn new>(value: T) -> Self { + value.into() + } + 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"))] + pub fn to_signed_bytes_le(&self) -> Vec { + self.value.to_signed_bytes_le() + } + #[cfg(any(feature = "std", feature = "alloc"))] + pub fn to_bytes_be(&self) -> Vec { + self.value.to_bytes_be() + } + + pub fn parse_bytes(buf: &[u8], radix: u32) -> Option { + Some(Self { + value: FeltBigInt::parse_bytes(buf, radix)?, + }) + } + pub fn from_bytes_be(bytes: &[u8]) -> Self { + Self { + value: FeltBigInt::from_bytes_be(bytes), + } + } + #[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 { + // Based on Tonelli-Shanks' algorithm for finding square roots + // and sympy's library implementation of said algorithm. + if self.is_zero() || self.is_one() { + return self.clone(); + } + + let max_felt = Felt252::max_value(); + let trailing_prime = Felt252::max_value() >> 192; // 0x800000000000011 + + 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 ≡ -1 (mod CAIRO_PRIME) + 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 for Felt252 { + type Output = Self; + fn add(self, rhs: u32) -> Self { + Self { + value: self.value + rhs, + } + } +} + +impl Add for Felt252 { + type Output = Self; + fn add(self, rhs: usize) -> Self { + Self { + value: self.value + rhs, + } + } +} + +impl<'a> Add for &'a Felt252 { + type Output = Felt252; + fn add(self, rhs: usize) -> Self::Output { + Self::Output { + value: &self.value + rhs, + } + } +} + +impl Add for &Felt252 { + type Output = Felt252; + fn add(self, rhs: u64) -> Self::Output { + Self::Output { + value: &self.value + rhs, + } + } +} + +// This is special cased and optimized compared to the obvious implementation +// due to `pc_update` relying on this, which makes it a major bottleneck for +// execution. Testing for this function is extensive, comprised of explicit +// edge and special cases testing and property tests, all comparing to the +// more intuitive `(rhs + self).to_u64()` implementation. +// This particular implementation is much more complex than a slightly more +// intuitive one based on a single match. However, this is 8-62% faster +// depending on the case being bencharked, with an average of 32%, so it's +// worth it. +impl Add<&Felt252> for u64 { + type Output = Option; + + fn add(self, rhs: &Felt252) -> Option { + 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); + + // Iterate through the 64 bits digits in little-endian order to + // characterize how the sum will behave. + let mut rhs_digits = rhs.iter_u64_digits(); + // No digits means `rhs` is `0`, so the sum is simply `self`. + let Some(low) = rhs_digits.next() else { + return Some(self); + }; + // A single digit means this is effectively the sum of two `u64` numbers. + let Some(h0) = rhs_digits.next() else { + return self.checked_add(low) + }; + // Now we need to compare the 3 most significant digits. + // There are two relevant cases from now on, either `rhs` behaves like a + // substraction of a `u64` or the result of the sum falls out of range. + let (h1, h2) = (rhs_digits.next()?, rhs_digits.next()?); + match (h0, h1, h2) { + // The 3 MSB only match the prime for Felt252::max_value(), which is -1 + // in the signed field, so this is equivalent to substracting 1 to `self`. + #[allow(clippy::suspicious_arithmetic_impl)] + PRIME_DIGITS_LE_HI => self.checked_sub(1), + // For the remaining values between `[-u64::MAX..0]` (where `{0, -1}` have + // already been covered) the MSB matches that of `PRIME - u64::MAX`. + // Because we're in the negative number case, we count down. Because `0` + // and `-1` correspond to different MSBs, `0` and `1` in the LSB are less + // than `-u64::MAX`, the smallest value we can add to (read, substract it's + // magnitude from) a `u64` number, meaning we exclude them from the valid + // case. + // For the remaining range, we make take the absolute value module-2 while + // correcting by substracting `1` (note we actually substract `2` because + // the absolute value itself requires substracting `1`. + #[allow(clippy::suspicious_arithmetic_impl)] + PRIME_MINUS_U64_MAX_DIGITS_LE_HI if low >= 2 => { + (self).checked_sub(u64::MAX - (low - 2)) + } + // Any other case will result in an addition that is out of bounds, so + // the addition fails, returning `None`. + _ => 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>(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 for Felt252 { + type Output = Self; + fn sub(self, rhs: u32) -> Self { + Self { + value: self.value - rhs, + } + } +} + +impl<'a> Sub for &'a Felt252 { + type Output = Felt252; + fn sub(self, rhs: u32) -> Self::Output { + Self::Output { + value: &self.value - rhs, + } + } +} + +impl Sub 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 for Felt252 { + type Output = Self; + fn pow(self, rhs: u32) -> Self { + Self { + value: self.value.pow(rhs), + } + } +} + +impl<'a> Pow 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 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 { + 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 for Felt252 { + type Output = Self; + fn shl(self, rhs: u32) -> Self { + Self { + value: self.value << rhs, + } + } +} + +impl<'a> Shl for &'a Felt252 { + type Output = Felt252; + fn shl(self, rhs: u32) -> Self::Output { + Self::Output { + value: &self.value << rhs, + } + } +} + +impl Shl for Felt252 { + type Output = Self; + fn shl(self, rhs: usize) -> Self { + Self { + value: self.value << rhs, + } + } +} + +impl<'a> Shl for &'a Felt252 { + type Output = Felt252; + fn shl(self, rhs: usize) -> Self::Output { + Self::Output { + value: &self.value << rhs, + } + } +} + +impl Shr for Felt252 { + type Output = Self; + fn shr(self, rhs: u32) -> Self { + Self { + value: self.value >> rhs, + } + } +} + +impl<'a> Shr for &'a Felt252 { + type Output = Felt252; + fn shr(self, rhs: u32) -> Self::Output { + Self::Output { + value: &self.value >> rhs, + } + } +} + +impl ShrAssign 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 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 { + self.value.to_u128() + } + + fn to_u64(&self) -> Option { + self.value.to_u64() + } + + fn to_i64(&self) -> Option { + self.value.to_i64() + } +} + +impl FromPrimitive for Felt252 { + fn from_u64(n: u64) -> Option { + FeltBigInt::from_u64(n).map(|n| Self { value: n }) + } + + fn from_i64(n: i64) -> Option { + 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() {} + fn assertion() { + assert_felt_ops::<$type>(); + } + }; + }; +} + +macro_rules! assert_felt_impl { + ($type:ty) => { + const _: () = { + fn assert_add() {} + fn assert_add_ref<'a, T: Add<&'a $type>>() {} + fn assert_add_u32>() {} + fn assert_add_usize>() {} + fn assert_add_assign() {} + fn assert_add_assign_ref<'a, T: AddAssign<&'a $type>>() {} + fn assert_sum>() {} + fn assert_neg() {} + fn assert_sub() {} + fn assert_sub_ref<'a, T: Sub<&'a $type>>() {} + fn assert_sub_assign() {} + fn assert_sub_assign_ref<'a, T: SubAssign<&'a $type>>() {} + fn assert_sub_u32>() {} + fn assert_sub_usize>() {} + fn assert_mul() {} + fn assert_mul_ref<'a, T: Mul<&'a $type>>() {} + fn assert_mul_assign_ref<'a, T: MulAssign<&'a $type>>() {} + fn assert_pow_u32>() {} + fn assert_pow_felt<'a, T: Pow<&'a $type>>() {} + fn assert_div() {} + fn assert_ref_div>() {} + fn assert_rem() {} + fn assert_rem_ref<'a, T: Rem<&'a $type>>() {} + fn assert_zero() {} + fn assert_one() {} + fn assert_bounded() {} + fn assert_num() {} + fn assert_integer() {} + fn assert_signed() {} + fn assert_shl_u32>() {} + fn assert_shl_usize>() {} + fn assert_shr_u32>() {} + fn assert_shr_assign_usize>() {} + fn assert_bitand() {} + fn assert_bitand_ref<'a, T: BitAnd<&'a $type>>() {} + fn assert_ref_bitand>() {} + fn assert_bitor() {} + fn assert_bitxor() {} + fn assert_from_primitive() {} + fn assert_to_primitive() {} + fn assert_display() {} + fn assert_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); +assert_felt_impl!(FeltBigInt); +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)] + // Property-based test that ensures, for 100 felt values that are randomly generated + // each time tests are run, that a new felt doesn't fall outside the range [0, p]. + // In this and some of the following tests, The value of {x} can be either [0] or a + // very large number, in order to try to overflow the value of {p} and thus ensure the + // modular arithmetic is working correctly. + 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 to_be_bytes(ref x in any::()) { + 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::()) { + let mut bytes = x.to_le_bytes(); + // Convert to big endian for test + 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::()) { + let digits: [u64; 4] = x.to_le_digits(); + let mut bytes: Vec<_> = digits + .into_iter() + .flat_map(|x| x.to_le_bytes()) + .collect(); + // Convert to big endian for test + 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::()) { + 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)] + // Property-based test that ensures, for 100 felt values that are randomly + // generated each time tests are run, that a felt created using Felt252::from_bytes_be doesn't + // fall outside the range [0, p]. + // In this and some of the following tests, The value of {x} can be either [0] or a very large number, + // in order to try to overflow the value of {p} and thus ensure the modular arithmetic is working correctly. + 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)] + // Property-based test that ensures, for 100 felt values that are randomly generated each time + // tests are run, that the negative of a felt doesn't fall outside the range [0, p]. + fn neg_in_range(x in any::()) { + 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); + + // test reference variant + let neg = -&x; + let as_uint = &neg.to_biguint(); + prop_assert!(as_uint < p); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + // Property-based test that ensures, for 100 {x} and {y} values that are randomly generated + // each time tests are run, that a subtraction between two felts {x} and {y} and doesn't fall + // outside the range [0, p]. The values of {x} and {y} can be either [0] or a very large number. + fn sub(ref x in any::(), ref y in any::()) { + 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)] + // Property-based test that ensures, for 100 {x} and {y} values that are randomly generated + // each time tests are run, that a subtraction with assignment between two felts {x} and {y} + // and doesn't fall outside the range [0, p]. The values of {x} and {y} can be either [0] or a very large number. + fn sub_assign_in_range(mut x in any::(), y in any::()) { + 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); + + // test reference variant + x -= &y; + let as_uint = &x.to_biguint(); + prop_assert!(as_uint < p, "{}", as_uint); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + // Property-based test that ensures, for 100 {x} and {y} values that are randomly + // generated each time tests are run, that a multiplication between two felts {x} + // and {y} and doesn't fall outside the range [0, p]. The values of {x} and {y} + // can be either [0] or a very large number. + fn mul(ref x in any::(), ref y in any::()) { + 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)] + // Property-based test that ensures, for 100 pairs of {x} and {y} values that + // are randomly generated each time tests are run, that a multiplication with + // assignment between two felts {x} and {y} and doesn't fall outside the range [0, p]. + // The values of {x} and {y} can be either [0] or a very large number. + fn mul_assign_in_range(mut x in any::(), y in any::()) { + 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)] + // Property-based test that ensures, for 100 pairs of {x} and {y} values that are + // randomly generated each time tests are run, that the result of the division of + // {x} by {y} is the inverse multiplicative of {x} --that is, multiplying the result + // by {y} returns the original number {x}. The values of {x} and {y} can be either + // [0] or a very large number. + fn div_is_mul_inv(ref x in any::(), 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)] + // Property-based test that ensures, for 100 {value}s that are randomly generated + // each time tests are run, that performing a bit shift to the left by {shift_amount} + // of bits (between 0 and 999) returns a result that is inside of the range [0, p]. + fn shift_left_in_range(value in any::(), 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)] + // Property-based test that ensures, for 100 {value}s that are randomly + // generated each time tests are run, that performing a bit shift to the right + // by {shift_amount} of bits (between 0 and 999) returns a result that is inside of the range [0, p]. + fn shift_right_in_range(value in any::(), 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)] + // Property-based test that ensures, for 100 {value}s that are randomly generated + // each time tests are run, that performing a bit shift to the right by {shift_amount} + // of bits (between 0 and 999), with assignment, returns a result that is inside of the range [0, p]. + // "With assignment" means that the result of the operation is autommatically assigned + // to the variable value, replacing its previous content. + fn shift_right_assign_in_range(mut value in any::(), 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)] + // Property based test that ensures, for 100 pairs of values {x} and {y} + // generated at random each time tests are run, that performing a BitAnd + // operation between them returns a result that is inside of the range [0, p]. + fn bitand_in_range(x in any::(), y in any::()){ + 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)] + // Property based test that ensures, for 100 pairs of values {x} and {y} + // generated at random each time tests are run, that performing a BitOr + // operation between them returns a result that is inside of the range [0, p]. + fn bitor_in_range(x in any::(), y in any::()){ + 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)] + // Property based test that ensures, for 100 pairs of values {x} and {y} + // generated at random each time tests are run, that performing a BitXor + // operation between them returns a result that is inside of the range [0, p]. + fn bitxor_in_range(x in any::(), y in any::()){ + 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)] + // Property-based test that ensures, for 100 values {x} that are randomly + // generated each time tests are run, that raising {x} to the {y}th power + // returns a result that is inside of the range [0, p]. + fn pow_in_range(base in any::(), 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); + + // test reference variant + 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)] + // Property-based test that ensures, for 100 values {x} that are randomly + // generated each time tests are run, that raising {x} to the {y}th power + // returns a result that is inside of the range [0, p]. + fn pow_felt_in_range(base in any::(), exponent in any::()){ + 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); + + // test reference variant + 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)] + // Property based test that ensures, for 100 pairs of values {x} and {y} + // generated at random each time tests are run, that performing a Sum operation + // between them returns a result that is inside of the range [0, p]. + fn sum_in_range(x in any::(), y in any::()){ + 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)] + // Property test to check that the remainder of a division between 100 pairs of + // values {x} and {y},generated at random each time tests are run, falls in the + // range [0, p]. x and y can either take the value of 0 or a large integer. + // In Cairo, the result of x / y is defined to always satisfy the equation + // (x / y) * y == x, so the remainder is 0 most of the time. + fn rem_in_range(x in any::(), 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); + + // test reference variant + 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)] + // Property based test that ensures, for 100 Felt252s {x} generated at + // random each time tests are run, that converting them into the u64 type + // returns a result that is inside of the range [0, p]. + fn from_u64_and_to_u64_primitive(x in any::()) { + 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::()) { + 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] + // Property test to check that lcm(x, y) works. Since we're operating in a prime field, lcm + // will just be the smaller number. + fn lcm_doesnt_panic(x in any::(), y in any::()) { + let lcm = x.lcm(&y); + prop_assert!(lcm == cmp::max(x, y)); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + // Property test to check that is_multiple_of(x, y) works. Since we're operating in a prime field, is_multiple_of + // will always be true + fn is_multiple_of_doesnt_panic(x in any::(), y in any::()) { + prop_assert!(x.is_multiple_of(&y)); + } + + #[test] + fn divides_doesnt_panic(x in any::(), y in any::()) { + prop_assert!(x.divides(&y)); + } + + #[test] + fn gcd_doesnt_panic(x in any::(), y in any::()) { + let gcd1 = x.gcd(&y); + let gcd2 = y.gcd(&x); + prop_assert_eq!(gcd1, gcd2); + } + + #[test] + fn is_even(x in any::()) { + prop_assert_eq!(x.is_even(), x.to_biguint().is_even()); + } + + #[test] + fn is_odd(x in any::()) { + prop_assert_eq!(x.is_odd(), x.to_biguint().is_odd()); + } + + /// Tests the additive identity of the implementation of Zero trait for felts + /// + /// ```{.text} + /// x + 0 = x ∀ x + /// 0 + x = x ∀ x + /// ``` + #[test] + fn zero_additive_identity(ref x in any::()) { + let zero = Felt252::zero(); + prop_assert_eq!(x, &(x + &zero)); + prop_assert_eq!(x, &(&zero + x)); + } + + /// Tests the multiplicative identity of the implementation of One trait for felts + /// + /// ```{.text} + /// x * 1 = x ∀ x + /// 1 * x = x ∀ x + /// ``` + #[test] + fn one_multiplicative_identity(ref x in any::()) { + 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::()) { + prop_assert!(x.is_positive()) + } + + #[test] + fn felt_is_never_negative(x in any::()) { + 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::(), y in any::()) { + 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::()) { + prop_assert_eq!(&x, &x.abs()) + } + + #[test] + fn modpow_in_range(x in any::(), y in any::()) { + let p = BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap(); + + let p_felt = Felt252::max_value(); + + let modpow = x.modpow(&y, &p_felt).to_biguint(); + prop_assert!(modpow < p, "{}", modpow); + } + + #[test] + fn sqrt_in_range(x in any::()) { + 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::()) { + 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::(), ref felt in any::()) { + let sum = (felt + x).to_u64(); + prop_assert_eq!(x + felt, sum); + } + + #[test] + fn add_to_u64_extremes(x in any::()) { + 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::(), y in any::()) { + 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::(), y in any::()) { + 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::(), y in any::()) { + 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::(), y in any::()) { + prop_assert_eq!(x.clone() - y + y, x) + } + + #[test] + fn sub_usize_in_range(x in any::(), y in any::()) { + 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::(), y in any::()) { + prop_assert_eq!(x.clone() - y + y, x) + } + + #[test] + fn add_in_range(x in any::(), y in any::()) { + 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::(), ref y in any::()) { + let expected_y = x + y - x; + prop_assert_eq!(&expected_y, y, "{}", y); + } + + #[test] + fn add_assign_in_range(mut x in any::(), y in any::()) { + 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); + + // test reference variant + 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)] + // Checks that the result of adding two zeroes is zero + 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)] + // Checks that the result of multiplying two zeroes is zero + 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)] + // Checks that the result of performing a bit and operation between zeroes is zero + 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)] + // Checks that the result of perfforming a bit or operation between zeroes is zero + 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)] + // Checks that the result of perfforming a bit xor operation between zeroes is zero + 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)] + // Tests that the maximum value a Felt252 can take is equal to (prime - 1) + 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)] + // Tests that the minimum value a Felt252 can take is equal to zero. + 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) + } +} diff --git a/felt/src/lib_lambdaworks.rs b/felt/src/lib_lambdaworks.rs new file mode 100644 index 0000000000..635fb13abd --- /dev/null +++ b/felt/src/lib_lambdaworks.rs @@ -0,0 +1,1688 @@ +use core::{ + convert::Into, + fmt, + iter::Sum, + ops::{ + Add, AddAssign, BitAnd, BitOr, BitXor, Div, Mul, MulAssign, Neg, Rem, Shl, Shr, ShrAssign, + Sub, SubAssign, + }, +}; +use lambdaworks_math::{ + field::{ + element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, + }, + unsigned_integer::element::UnsignedInteger, +}; +use lazy_static::lazy_static; +use num_bigint::{BigInt, BigUint, Sign, ToBigInt}; +use num_integer::Integer; +use num_traits::{Bounded, FromPrimitive, Num, One, Pow, Signed, ToPrimitive, Zero}; +use serde::{Deserialize, Serialize}; + +#[cfg(all(not(feature = "std"), feature = "alloc"))] +use alloc::{string::String, vec::Vec}; + +use crate::{ParseFeltError, FIELD_HIGH, FIELD_LOW}; + +lazy_static! { + pub static ref CAIRO_PRIME_BIGUINT: BigUint = + (Into::::into(FIELD_HIGH) << 128) + Into::::into(FIELD_LOW); + pub static ref SIGNED_FELT_MAX: BigUint = (&*CAIRO_PRIME_BIGUINT).shr(1_u32); + pub static ref CAIRO_SIGNED_PRIME: BigInt = CAIRO_PRIME_BIGUINT + .to_bigint() + .expect("Conversion BigUint -> BigInt can't fail"); +} + +#[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") + }; +} + +#[derive(Eq, Hash, PartialEq, Clone, Deserialize, Serialize)] +#[serde(from = "BigInt")] +#[serde(into = "BigInt")] +pub struct Felt252 { + pub(crate) value: FieldElement, +} + +// TODO: remove and change for transformation + compare +impl PartialOrd for Felt252 { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +// TODO: remove and change for transformation + compare +// Also, maybe this could be changed to compare against zero without changing montgomeryness +impl Ord for Felt252 { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.value + .representative() + .cmp(&other.value.representative()) + } +} + +impl Default for Felt252 { + fn default() -> Self { + Self { + value: FieldElement::zero(), + } + } +} + +macro_rules! from_num { + ($type:ty, $cast:ty) => { + impl From<$type> for Felt252 { + fn from(value: $type) -> Self { + let uplifted: $cast = value as $cast; + uplifted.into() + } + } + }; +} + +from_num!(isize, i64); +from_num!(i8, i64); +from_num!(i16, i64); +from_num!(i32, i64); + +// TODO: move to upstream? +impl From for Felt252 { + fn from(value: i64) -> Self { + let value = if !value.is_negative() { + FieldElement::new(UnsignedInteger::from_u64(value as u64)) + } else { + let abs_minus_one = UnsignedInteger::from_u64(-(value + 1) as u64); + FieldElement::zero() - FieldElement::one() - FieldElement::new(abs_minus_one) + }; + Self { value } + } +} + +// TODO: move to upstream? +impl From for Felt252 { + fn from(value: i128) -> Self { + let value = if !value.is_negative() { + FieldElement::new(UnsignedInteger::from_u128(value as u128)) + } else { + let abs_minus_one = UnsignedInteger::from_u128(-(value + 1) as u128); + FieldElement::zero() - FieldElement::one() - FieldElement::new(abs_minus_one) + }; + Self { value } + } +} + +from_num!(usize, u64); +from_num!(u8, u64); +from_num!(u16, u64); +from_num!(u32, u64); + +// TODO: move to upstream? +impl From for Felt252 { + fn from(value: u64) -> Self { + let value = FieldElement::new(UnsignedInteger::from_u64(value)); + Self { value } + } +} + +// TODO: move to upstream? +impl From for Felt252 { + fn from(value: u128) -> Self { + let value = FieldElement::new(UnsignedInteger::from_u128(value)); + Self { value } + } +} + +impl From for Felt252 { + fn from(flag: bool) -> Self { + if flag { + Self::one() + } else { + Self::zero() + } + } +} + +// TODO: bury BigUint? +impl From for Felt252 { + fn from(mut value: BigUint) -> Self { + if value >= *CAIRO_PRIME_BIGUINT { + value = value.mod_floor(&CAIRO_PRIME_BIGUINT); + } + let mut limbs = [0; 4]; + for (i, l) in (0..4).rev().zip(value.iter_u64_digits()) { + limbs[i] = l; + } + let value = FieldElement::new(UnsignedInteger::from_limbs(limbs)); + Self { value } + } +} + +// TODO: bury BigInt? +// NOTE: used for deserialization +impl From for Felt252 { + fn from(value: BigInt) -> Self { + let val = value.mod_floor(&CAIRO_PRIME_BIGUINT.to_bigint().expect("cannot fail")); + let mut limbs = [0; 4]; + for (i, l) in (0..4).rev().zip(val.iter_u64_digits()) { + limbs[i] = l; + } + let value = FieldElement::new(UnsignedInteger::from_limbs(limbs)); + Self { value } + } +} + +// NOTE: used for serialization +impl From for BigInt { + fn from(value: Felt252) -> Self { + value.to_bigint() + } +} + +impl Felt252 { + pub fn new>(value: T) -> Self { + value.into() + } + + pub fn iter_u64_digits(&self) -> impl Iterator { + self.value.representative().limbs.into_iter().rev() + } + + pub fn to_le_bytes(&self) -> [u8; 32] { + // TODO: upstream should return array + let mut bytes = [0; 32]; + let digits = self.to_le_digits(); + for (i, d) in digits.into_iter().enumerate() { + let idx = i * 8; + bytes[idx..(idx + 8)].copy_from_slice(&d.to_le_bytes()); + } + bytes + } + + pub fn to_be_bytes(&self) -> [u8; 32] { + // TODO: upstream should return array + let mut bytes = [0; 32]; + let digits = self.to_be_digits(); + for (i, d) in digits.into_iter().enumerate() { + let idx = i * 8; + bytes[idx..(idx + 8)].copy_from_slice(&d.to_be_bytes()); + } + bytes + } + + pub fn to_le_digits(&self) -> [u64; 4] { + let mut rep = self.value.representative(); + rep.limbs.reverse(); + rep.limbs + } + + pub fn to_be_digits(&self) -> [u64; 4] { + self.value.representative().limbs + } + + pub fn parse_bytes(bytes: &[u8], radix: u32) -> Option { + Some(BigInt::parse_bytes(bytes, radix)?.into()) + } + + pub fn from_bytes_be(bytes: &[u8]) -> Self { + Self::from(BigUint::from_bytes_be(bytes)) + } + + #[cfg(any(feature = "std", feature = "alloc"))] + pub fn to_str_radix(&self, radix: u32) -> String { + if radix == 16 { + let mut res = format!("{}", self.value); + res.replace_range(..2, ""); + res + } else { + self.to_biguint().to_str_radix(radix) + } + } + + /// Converts [`Felt252`] into a [`BigInt`] number in the range: `(- FIELD / 2, FIELD / 2)`. + /// + /// # Examples + /// + /// ``` + /// # use crate::cairo_felt::Felt252; + /// # use num_bigint::BigInt; + /// # use num_traits::Bounded; + /// let positive = Felt252::new(5); + /// assert_eq!(positive.to_signed_felt(), Into::::into(5)); + /// + /// let negative = Felt252::max_value(); + /// assert_eq!(negative.to_signed_felt(), Into::::into(-1)); + /// ``` + pub fn to_signed_felt(&self) -> BigInt { + let biguint = self.to_biguint(); + if biguint > *SIGNED_FELT_MAX { + BigInt::from_biguint(num_bigint::Sign::Minus, &*CAIRO_PRIME_BIGUINT - &biguint) + } else { + biguint.to_bigint().expect("cannot fail") + } + } + + // Converts [`Felt252`]'s representation directly into a [`BigInt`]. + // Equivalent to doing felt.to_biguint().to_bigint(). + pub fn to_bigint(&self) -> BigInt { + BigInt::from_biguint(Sign::Plus, self.to_biguint()) + } + + /// Converts [`Felt252`] into a [`BigUint`] number. + /// + /// # Examples + /// + /// ``` + /// # use crate::cairo_felt::Felt252; + /// # use num_bigint::BigUint; + /// # use num_traits::{Num, Bounded}; + /// let positive = Felt252::new(5); + /// assert_eq!(positive.to_biguint(), Into::::into(5_u32)); + /// + /// let negative = Felt252::max_value(); + /// assert_eq!(negative.to_biguint(), BigUint::from_str_radix("800000000000011000000000000000000000000000000000000000000000000", 16).unwrap()); + /// ``` + pub fn to_biguint(&self) -> BigUint { + let big_digits = self + .iter_u64_digits() + .flat_map(|limb| [limb as u32, (limb >> 32) as u32]) + .collect(); + BigUint::new(big_digits) + } + + pub fn sqrt(&self) -> Self { + // Safety: must be called with residual + let (root_1, root_2) = self.value.sqrt().unwrap(); + let value = FieldElement::new(root_1.representative().min(root_2.representative())); + Self { value } + } + + pub fn bits(&self) -> u64 { + // TODO: move upstream + let rep = self.value.representative(); + match rep.limbs { + [0, 0, 0, 0] => 0, + [0, 0, 0, l0] => u64::BITS - l0.leading_zeros(), + [0, 0, l1, _] => 2 * u64::BITS - l1.leading_zeros(), + [0, l2, _, _] => 3 * u64::BITS - l2.leading_zeros(), + [l3, _, _, _] => 4 * u64::BITS - l3.leading_zeros(), + } + .into() + } + + pub fn prime() -> BigUint { + CAIRO_PRIME_BIGUINT.clone() + } +} + +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 for Felt252 { + type Output = Self; + fn add(self, rhs: u32) -> Self { + let rhs = UnsignedInteger::from_u64(rhs.into()); + Self { + value: self.value + FieldElement::new(rhs), + } + } +} + +impl Add for Felt252 { + type Output = Self; + fn add(self, rhs: usize) -> Self { + let rhs = UnsignedInteger::from_u64(rhs as u64); + Self { + value: self.value + FieldElement::new(rhs), + } + } +} + +impl<'a> Add for &'a Felt252 { + type Output = Felt252; + fn add(self, rhs: usize) -> Self::Output { + let rhs = UnsignedInteger::from_u64(rhs as u64); + Self::Output { + value: &self.value + FieldElement::new(rhs), + } + } +} + +impl Add for &Felt252 { + type Output = Felt252; + fn add(self, rhs: u64) -> Self::Output { + let rhs = UnsignedInteger::from_u64(rhs); + Self::Output { + value: &self.value + FieldElement::new(rhs), + } + } +} + +// TODO: verify if this optimization causes a speed-up in the current implementation. +// This is special cased and optimized compared to the obvious implementation +// due to `pc_update` relying on this, which makes it a major bottleneck for +// execution. Testing for this function is extensive, comprised of explicit +// edge and special cases testing and property tests, all comparing to the +// more intuitive `(rhs + self).to_u64()` implementation. +impl Add<&Felt252> for u64 { + type Output = Option; + + fn add(self, rhs: &Felt252) -> Option { + const PRIME_DIGITS_BE_HI: [u64; 3] = + [0x0800000000000011, 0x0000000000000000, 0x0000000000000000]; + const PRIME_MINUS_U64_MAX_DIGITS_BE_HI: [u64; 3] = + [0x0800000000000010, 0xffffffffffffffff, 0xffffffffffffffff]; + + // Match with the 64 bits digits in big-endian order to + // characterize how the sum will behave. + match rhs.to_be_digits() { + // All digits are `0`, so the sum is simply `self`. + [0, 0, 0, 0] => Some(self), + // A single digit means this is effectively the sum of two `u64` numbers. + [0, 0, 0, low] => self.checked_add(low), + // Now we need to compare the 3 most significant digits. + // There are two relevant cases from now on, either `rhs` behaves like a + // substraction of a `u64` or the result of the sum falls out of range. + + // The 3 MSB only match the prime for Felt252::max_value(), which is -1 + // in the signed field, so this is equivalent to substracting 1 to `self`. + [hi @ .., _] if hi == PRIME_DIGITS_BE_HI => self.checked_sub(1), + + // For the remaining values between `[-u64::MAX..0]` (where `{0, -1}` have + // already been covered) the MSB matches that of `PRIME - u64::MAX`. + // Because we're in the negative number case, we count down. Because `0` + // and `-1` correspond to different MSBs, `0` and `1` in the LSB are less + // than `-u64::MAX`, the smallest value we can add to (read, substract its + // magnitude from) a `u64` number, meaning we exclude them from the valid + // case. + // For the remaining range, we take the absolute value module-2 while + // correcting by substracting `1` (note we actually substract `2` because + // the absolute value itself requires substracting `1`. + [hi @ .., low] if hi == PRIME_MINUS_U64_MAX_DIGITS_BE_HI && low >= 2 => { + (self).checked_sub(u64::MAX - (low - 2)) + } + // Any other case will result in an addition that is out of bounds, so + // the addition fails, returning `None`. + _ => 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) { + // TODO: optimize and move upstream + self.value += rhs.value.clone(); + } +} + +impl Sum for Felt252 { + fn sum>(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, + } + } +} + +// a - b = a + (-b), but clippy doesn't know that +#[allow(clippy::suspicious_arithmetic_impl)] +impl Sub<&Felt252> for usize { + type Output = Felt252; + fn sub(self, rhs: &Self::Output) -> Self::Output { + let neg = Self::Output { + value: (&rhs.value).neg(), + }; + neg + self + } +} + +impl SubAssign for Felt252 { + fn sub_assign(&mut self, rhs: Self) { + // TODO: optimize and move to upstream + self.value = &self.value - rhs.value + } +} + +impl<'a> SubAssign<&'a Felt252> for Felt252 { + fn sub_assign(&mut self, rhs: &Self) { + // TODO: optimize and move to upstream + self.value = &self.value - &rhs.value + } +} + +impl Sub for Felt252 { + type Output = Self; + fn sub(self, rhs: u32) -> Self { + let value = self.value - FieldElement::new(UnsignedInteger::from_u64(rhs as u64)); + Self { value } + } +} + +impl<'a> Sub for &'a Felt252 { + type Output = Felt252; + fn sub(self, rhs: u32) -> Self::Output { + self.clone() - rhs + } +} + +impl Sub for Felt252 { + type Output = Self; + fn sub(self, rhs: usize) -> Self { + let value = self.value - FieldElement::new(UnsignedInteger::from_u64(rhs as u64)); + Self { value } + } +} + +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 = &self.value * &rhs.value; + } +} + +impl Pow for Felt252 { + type Output = Self; + fn pow(self, rhs: u32) -> Self { + Self { + value: self.value.pow(rhs), + } + } +} + +impl<'a> Pow 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.representative()), + } + } +} + +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 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::zero() + } +} + +impl<'a> Rem<&'a Felt252> for Felt252 { + type Output = Self; + fn rem(self, _rhs: &Self) -> Self { + Self::zero() + } +} + +impl Zero for Felt252 { + fn zero() -> Self { + Self { + value: FieldElement::from_raw(&Stark252PrimeField::ZERO), + } + } + + fn is_zero(&self) -> bool { + self.value == FieldElement::from_raw(&Stark252PrimeField::ZERO) + } +} + +impl One for Felt252 { + fn one() -> Self { + let value = FieldElement::from_raw(&Stark252PrimeField::ONE); + Self { value } + } + + fn is_one(&self) -> bool { + self.value == FieldElement::from_raw(&Stark252PrimeField::ONE) + } +} + +impl Bounded for Felt252 { + fn min_value() -> Self { + Self { + value: FieldElement::zero(), + } + } + + fn max_value() -> Self { + Self { + value: FieldElement::zero() - FieldElement::one(), + } + } +} + +impl Num for Felt252 { + type FromStrRadixErr = ParseFeltError; + fn from_str_radix(string: &str, radix: u32) -> Result { + let res = if radix == 16 { + let value = FieldElement::from_hex(string).map_err(|_| ParseFeltError)?; + Self { value } + } else { + let biguint = BigInt::from_str_radix(string, radix).map_err(|_| ParseFeltError)?; + biguint.into() + }; + Ok(res) + } +} + +impl Integer for Felt252 { + fn div_floor(&self, rhs: &Self) -> Self { + let (d, _) = self.div_rem(rhs); + d + } + + fn mod_floor(&self, rhs: &Self) -> Self { + let (_, m) = self.div_rem(rhs); + m + } + + fn div_rem(&self, other: &Self) -> (Self, Self) { + let (div, rem) = self.to_biguint().div_mod_floor(&other.to_biguint()); + (Self::from(div), Self::from(rem)) + } + + // NOTE: we overload because the default impl calls div_floor AND mod_floor. + fn div_mod_floor(&self, rhs: &Self) -> (Self, Self) { + // NOTE: for positive integers, to floor and truncate is the same, so div_rem == div_mod_floor. + self.div_rem(rhs) + } + + fn divides(&self, _other: &Self) -> bool { + !self.is_zero() + } + + fn gcd(&self, other: &Self) -> Self { + Self::from(self.to_biguint().gcd(&other.to_biguint())) + } + + fn is_even(&self) -> bool { + self.value.representative().limbs[3] & 1 == 0 + } + + fn is_multiple_of(&self, other: &Self) -> bool { + !other.is_zero() + } + + fn is_odd(&self) -> bool { + !self.is_even() + } + + fn lcm(&self, other: &Self) -> Self { + self.max(other).clone() + } +} + +impl Signed for Felt252 { + fn abs(&self) -> Self { + self.clone() + } + + fn abs_sub(&self, other: &Self) -> Self { + self.max(other) - self.min(other) + } + + fn signum(&self) -> Self { + if self.is_zero() { + Self::zero() + } else { + Self::one() + } + } + + fn is_positive(&self) -> bool { + !self.is_zero() + } + + fn is_negative(&self) -> bool { + false + } +} + +// ------------------- +// Bit-wise operations +// NOTE: these do bit shifting on the representative + +impl Shl for Felt252 { + type Output = Self; + fn shl(self, rhs: u32) -> Self { + &self << rhs + } +} + +impl<'a> Shl for &'a Felt252 { + type Output = Felt252; + #[allow(clippy::suspicious_arithmetic_impl)] + fn shl(self, rhs: u32) -> Self::Output { + // TODO: upstream should be able to receive usize + Felt252::from(2).pow(rhs) * self + } +} + +impl Shl for Felt252 { + type Output = Self; + fn shl(self, rhs: usize) -> Self { + &self << rhs + } +} + +impl<'a> Shl for &'a Felt252 { + type Output = Felt252; + fn shl(self, rhs: usize) -> Self::Output { + self << (rhs as u32) + } +} + +impl Shr for Felt252 { + type Output = Self; + fn shr(self, rhs: u32) -> Self { + &self >> rhs + } +} + +impl<'a> Shr for &'a Felt252 { + type Output = Felt252; + fn shr(self, rhs: u32) -> Self::Output { + self >> (rhs as usize) + } +} + +impl Shr for Felt252 { + type Output = Felt252; + fn shr(self, rhs: usize) -> Self::Output { + &self >> rhs + } +} + +impl<'a> Shr for &'a Felt252 { + type Output = Felt252; + fn shr(self, rhs: usize) -> Self::Output { + // TODO: upstream should do this check + if rhs >= 64 * 4 { + Felt252::zero() + } else { + let value = FieldElement::new(self.value.representative() >> rhs); + Self::Output { value } + } + } +} + +impl ShrAssign for Felt252 { + fn shr_assign(&mut self, rhs: usize) { + // TODO: optimize and move upstream + *self = self.clone() >> rhs; + } +} + +// TODO: move to upstream +impl<'a> BitAnd for &'a Felt252 { + type Output = Felt252; + fn bitand(self, rhs: Self) -> Self::Output { + self.clone() & rhs + } +} + +// TODO: move to upstream +impl<'a> BitAnd<&'a Felt252> for Felt252 { + type Output = Self; + fn bitand(self, rhs: &Self) -> Self { + rhs & self + } +} + +impl<'a> BitAnd for &'a Felt252 { + type Output = Felt252; + fn bitand(self, rhs: Self::Output) -> Self::Output { + // TODO: move to upstream + let a = self.value.representative(); + let b = rhs.value.representative(); + let value = FieldElement::new(a & b); + Self::Output { value } + } +} + +impl<'a> BitOr for &'a Felt252 { + type Output = Felt252; + fn bitor(self, rhs: Self) -> Self::Output { + // TODO: move to upstream + let mut a = self.value.representative(); + let b = rhs.value.representative(); + + for i in 0..a.limbs.len() { + a.limbs[i] |= b.limbs[i]; + } + let value = FieldElement::new(a); + // let value = FieldElement::new(a | b); + Self::Output { value } + } +} + +impl<'a> BitXor for &'a Felt252 { + type Output = Felt252; + fn bitxor(self, rhs: Self) -> Self::Output { + // TODO: move to upstream + let mut a = self.value.representative(); + let b = rhs.value.representative(); + + for i in 0..a.limbs.len() { + a.limbs[i] ^= b.limbs[i]; + } + let value = FieldElement::new(a); + // let value = FieldElement::new(a ^ b); + Self::Output { value } + } +} + +// TODO: move to upstream +impl ToPrimitive for Felt252 { + fn to_u128(&self) -> Option { + match self.value.representative().limbs { + [0, 0, high, low] => Some(((high as u128) << 64) | low as u128), + _ => None, + } + } + + fn to_u64(&self) -> Option { + match self.value.representative().limbs { + [0, 0, 0, val] => Some(val), + _ => None, + } + } + + fn to_i64(&self) -> Option { + // NOTE: result can't be negative + self.to_u64().as_ref().and_then(u64::to_i64) + } +} + +impl FromPrimitive for Felt252 { + fn from_u64(n: u64) -> Option { + Some(Felt252 { + value: FieldElement::from(n), + }) + } + + fn from_i64(n: i64) -> Option { + let res = (!n.is_negative()).then(|| FieldElement::from(n as u64)); + res.map(|value| Felt252 { value }) + } +} + +impl fmt::Display for Felt252 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.to_str_radix(10)) + } +} + +impl fmt::Debug for Felt252 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.to_str_radix(10)) + } +} + +#[cfg(test)] +mod test { + use core::cmp; + + use super::*; + use crate::{arbitrary_lambdaworks::nonzero_felt252, PRIME_STR}; + use num_integer::Integer; + use rstest::rstest; + + use proptest::prelude::*; + + proptest! { + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + // Property-based test that ensures, for 100 felt values that are randomly generated + // each time tests are run, that a new felt doesn't fall outside the range [0, p]. + // In this and some of the following tests, The value of {x} can be either [0] or a + // very large number, in order to try to overflow the value of {p} and thus ensure the + // modular arithmetic is working correctly. + 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 to_be_bytes(ref x in any::()) { + 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::()) { + let mut bytes = x.to_le_bytes(); + // Convert to big endian for test + 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::()) { + let digits: [u64; 4] = x.to_le_digits(); + let mut bytes: Vec<_> = digits + .into_iter() + .flat_map(|x| x.to_le_bytes()) + .collect(); + // Convert to big endian for test + 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::()) { + 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)] + // Property-based test that ensures, for 100 felt values that are randomly + // generated each time tests are run, that a felt created using Felt252::from_bytes_be doesn't + // fall outside the range [0, p]. + // In this and some of the following tests, The value of {x} can be either [0] or a very large number, + // in order to try to overflow the value of {p} and thus ensure the modular arithmetic is working correctly. + 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)] + // Property-based test that ensures, for 100 felt values that are randomly generated each time + // tests are run, that the negative of a felt doesn't fall outside the range [0, p]. + fn neg_in_range(x in any::()) { + 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); + + // test reference variant + let neg = -&x; + let as_uint = &neg.to_biguint(); + prop_assert!(as_uint < p); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + // Property-based test that ensures, for 100 {x} and {y} values that are randomly generated + // each time tests are run, that a subtraction between two felts {x} and {y} and doesn't fall + // outside the range [0, p]. The values of {x} and {y} can be either [0] or a very large number. + fn sub(ref x in any::(), ref y in any::()) { + 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)] + // Property-based test that ensures, for 100 {x} and {y} values that are randomly generated + // each time tests are run, that a subtraction with assignment between two felts {x} and {y} + // and doesn't fall outside the range [0, p]. The values of {x} and {y} can be either [0] or a very large number. + fn sub_assign_in_range(mut x in any::(), y in any::()) { + 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); + + // test reference variant + x -= &y; + let as_uint = &x.to_biguint(); + prop_assert!(as_uint < p, "{}", as_uint); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + // Property-based test that ensures, for 100 {x} and {y} values that are randomly + // generated each time tests are run, that a multiplication between two felts {x} + // and {y} and doesn't fall outside the range [0, p]. The values of {x} and {y} + // can be either [0] or a very large number. + fn mul(ref x in any::(), ref y in any::()) { + 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)] + // Property-based test that ensures, for 100 pairs of {x} and {y} values that + // are randomly generated each time tests are run, that a multiplication with + // assignment between two felts {x} and {y} and doesn't fall outside the range [0, p]. + // The values of {x} and {y} can be either [0] or a very large number. + fn mul_assign_in_range(mut x in any::(), y in any::()) { + 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)] + // Property-based test that ensures, for 100 pairs of {x} and {y} values that are + // randomly generated each time tests are run, that the result of the division of + // {x} by {y} is the inverse multiplicative of {x} --that is, multiplying the result + // by {y} returns the original number {x}. The values of {x} and {y} can be either + // [0] or a very large number. + fn div_is_mul_inv(ref x in any::(), 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)] + // Property-based test that ensures, for 100 {value}s that are randomly generated + // each time tests are run, that performing a bit shift to the left by {shift_amount} + // of bits (between 0 and 999) returns a result that is inside of the range [0, p]. + fn shift_left_in_range(value in any::(), 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_left_equals_old_shl(value in any::(), shift_amount in 0..1000_u32) { + let expected = (value.to_biguint() << shift_amount).mod_floor(&Felt252::prime()); + + let result = (&value << shift_amount).to_biguint(); + prop_assert_eq!(&result, &expected); + + let result = (value << shift_amount).to_biguint(); + prop_assert_eq!(&result, &expected); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + // Property-based test that ensures, for 100 {value}s that are randomly + // generated each time tests are run, that performing a bit shift to the right + // by {shift_amount} of bits (between 0 and 999) returns a result that is inside of the range [0, p]. + fn shift_right_in_range(value in any::(), 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)] + // Property-based test that ensures, for 100 {value}s that are randomly generated + // each time tests are run, that performing a bit shift to the right by {shift_amount} + // of bits (between 0 and 999), with assignment, returns a result that is inside of the range [0, p]. + // "With assignment" means that the result of the operation is autommatically assigned + // to the variable value, replacing its previous content. + fn shift_right_assign_in_range(mut value in any::(), 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 shift_right_equals_old_shr(value in any::(), shift_amount in 0..1000_u32) { + let expected = (value.to_biguint() >> shift_amount).mod_floor(&Felt252::prime()); + + let result = (&value >> shift_amount).to_biguint(); + prop_assert_eq!(&result, &expected); + + let result = (value >> shift_amount).to_biguint(); + prop_assert_eq!(&result, &expected); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + // Property based test that ensures, for 100 pairs of values {x} and {y} + // generated at random each time tests are run, that performing a BitAnd + // operation between them returns a result that is inside of the range [0, p]. + fn bitand_in_range(x in any::(), y in any::()){ + 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)] + // Property based test that ensures, for 100 pairs of values {x} and {y} + // generated at random each time tests are run, that performing a BitOr + // operation between them returns a result that is inside of the range [0, p]. + fn bitor_in_range(x in any::(), y in any::()){ + 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)] + // Property based test that ensures, for 100 pairs of values {x} and {y} + // generated at random each time tests are run, that performing a BitXor + // operation between them returns a result that is inside of the range [0, p]. + fn bitxor_in_range(x in any::(), y in any::()){ + 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)] + // Property-based test that ensures, for 100 values {x} that are randomly + // generated each time tests are run, that raising {x} to the {y}th power + // returns a result that is inside of the range [0, p]. + fn pow_in_range(base in any::(), 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); + + // test reference variant + 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)] + // Property-based test that ensures, for 100 values {x} that are randomly + // generated each time tests are run, that raising {x} to the {y}th power + // returns a result that is inside of the range [0, p]. + fn pow_felt_in_range(base in any::(), exponent in any::()){ + 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); + + // test reference variant + 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)] + // Property based test that ensures, for 100 pairs of values {x} and {y} + // generated at random each time tests are run, that performing a Sum operation + // between them returns a result that is inside of the range [0, p]. + fn sum_in_range(x in any::(), y in any::()){ + 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)] + // Property test to check that the remainder of a division between 100 pairs of + // values {x} and {y},generated at random each time tests are run, falls in the + // range [0, p]. x and y can either take the value of 0 or a large integer. + // In Cairo, the result of x / y is defined to always satisfy the equation + // (x / y) * y == x, so the remainder is 0 most of the time. + fn rem_in_range(x in any::(), 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); + + // test reference variant + 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)] + // Property based test that ensures, for 100 Felt252s {x} generated at + // random each time tests are run, that converting them into the u64 type + // returns a result that is inside of the range [0, p]. + fn from_u64_and_to_u64_primitive(x in any::()) { + 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::()) { + let x = x.checked_abs().unwrap_or(0); + 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] + // Property test to check that lcm(x, y) works. Since we're operating in a prime field, lcm + // will just be the smaller number. + fn lcm_doesnt_panic(x in any::(), y in any::()) { + let lcm = x.lcm(&y); + prop_assert!(lcm == cmp::max(x, y)); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + // Property test to check that is_multiple_of(x, y) works. Since we're operating in a prime field, is_multiple_of + // will always be true + fn is_multiple_of_doesnt_panic(x in any::(), y in any::()) { + prop_assert!(x.is_multiple_of(&y)); + } + + #[test] + fn divides_doesnt_panic(x in any::(), y in any::()) { + prop_assert!(x.divides(&y)); + } + + #[test] + fn gcd_doesnt_panic(x in any::(), y in any::()) { + let gcd1 = x.gcd(&y); + let gcd2 = y.gcd(&x); + prop_assert_eq!(gcd1, gcd2); + } + + #[test] + fn is_even(x in any::()) { + prop_assert_eq!(x.is_even(), x.to_biguint().is_even()); + } + + #[test] + fn is_odd(x in any::()) { + prop_assert_eq!(x.is_odd(), x.to_biguint().is_odd()); + } + + /// Tests the additive identity of the implementation of Zero trait for felts + /// + /// ```{.text} + /// x + 0 = x ∀ x + /// 0 + x = x ∀ x + /// ``` + #[test] + fn zero_additive_identity(ref x in any::()) { + let zero = Felt252::zero(); + prop_assert_eq!(x, &(x + &zero)); + prop_assert_eq!(x, &(&zero + x)); + } + + /// Tests the multiplicative identity of the implementation of One trait for felts + /// + /// ```{.text} + /// x * 1 = x ∀ x + /// 1 * x = x ∀ x + /// ``` + #[test] + fn one_multiplicative_identity(ref x in any::()) { + 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::()) { + prop_assume!(!x.is_zero()); + prop_assert!(x.is_positive()) + } + + #[test] + fn felt_is_never_negative(x in any::()) { + 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::(), y in any::()) { + 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::()) { + prop_assert_eq!(&x, &x.abs()) + } + + #[test] + fn sqrt_in_range(x in any::()) { + // we use x = x' * x' so x has a square root + let x = &x * &x; + let p = Felt252::prime(); + + let sqrt = x.sqrt().to_biguint(); + prop_assert!(sqrt < p, "{}", sqrt); + } + + #[test] + fn sqrt_is_inv_square(x in any::()) { + // we use x = x' * x' so x has a square root + let x = &x * &x; + 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::(), ref felt in any::()) { + let sum = (felt + x).to_u64(); + prop_assert_eq!(x + felt, sum); + } + + #[test] + fn add_to_u64_extremes(x in any::()) { + 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::(), y in any::()) { + 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::(), y in any::()) { + 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::(), y in any::()) { + 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::(), y in any::()) { + prop_assert_eq!(x.clone() - y + y, x) + } + + #[test] + fn sub_usize_in_range(x in any::(), y in any::()) { + 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::(), y in any::()) { + prop_assert_eq!(x.clone() - y + y, x) + } + + #[test] + fn add_in_range(x in any::(), y in any::()) { + 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::(), ref y in any::()) { + let expected_y = x + y - x; + prop_assert_eq!(&expected_y, y, "{}", y); + } + + #[test] + fn add_assign_in_range(mut x in any::(), y in any::()) { + 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); + + // test reference variant + x += &y; + let as_uint = &x.to_biguint(); + prop_assert!(as_uint < p, "{}", as_uint); + } + + #[test] + fn felt_to_str(x in any::(), radix in 2_u32..37) { + let str_x = x.to_str_radix(radix); + let int_x = x.to_biguint(); + let expected = int_x.to_str_radix(radix); + prop_assert_eq!(str_x, expected); + } + + #[test] + fn bigint_from_felt(x in any::()) { + prop_assert_eq!(BigInt::from(x.clone()), x.to_bigint()); + } + + #[test] + fn to_signed_felt_is_negative(x in any::()) { + let int = BigInt::from(x); + let felt = Felt252::from(x); + prop_assert_eq!(felt.to_signed_felt(), int); + } + } + + #[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)] + // Checks that the result of adding two zeroes is zero + fn sum_zeros_in_range() { + let x = Felt252::zero(); + let y = Felt252::zero(); + let z = Felt252::zero(); + assert_eq!(x + y, z) + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + // Checks that the result of multiplying two zeroes is zero + fn mul_zeros_in_range() { + let x = Felt252::zero(); + let y = Felt252::zero(); + let z = Felt252::zero(); + assert_eq!(x * y, z) + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + // Checks that the result of performing a bit and operation between zeroes is zero + fn bit_and_zeros_in_range() { + let x = Felt252::zero(); + let y = Felt252::zero(); + let z = Felt252::zero(); + assert_eq!(&x & &y, z) + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + // Checks that the result of perfforming a bit or operation between zeroes is zero + fn bit_or_zeros_in_range() { + let x = Felt252::zero(); + let y = Felt252::zero(); + let z = Felt252::zero(); + assert_eq!(&x | &y, z) + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + // Checks that the result of perfforming a bit xor operation between zeroes is zero + fn bit_xor_zeros_in_range() { + let x = Felt252::zero(); + let y = Felt252::zero(); + let z = Felt252::zero(); + assert_eq!(&x ^ &y, z) + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + // Tests that the maximum value a Felt252 can take is equal to (prime - 1) + 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)] + // Tests that the minimum value a Felt252 can take is equal to zero. + 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_u32); + 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_u32); + 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 felt_from_str_radix_failed() { + let x = Felt252::from_str_radix("abcdefghijk", 16); + assert!(x.is_err()); + let res = x.unwrap_err().to_string(); + let expected = "ParseFeltError"; + assert_eq!(res, expected) + } + + #[test] + fn default_is_zero() { + assert_eq!(Felt252::default(), Felt252::zero()) + } +} diff --git a/vm/Cargo.toml b/vm/Cargo.toml index 37ea8ccc2d..46981f4d46 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -21,7 +21,9 @@ std = [ "dep:num-prime", ] cairo-1-hints = ["dep:cairo-lang-starknet", "dep:cairo-lang-casm", "dep:ark-ff", "dep:ark-std"] - +lambdaworks-felt = [ + "felt/lambdaworks-felt" +] # Note that these features are not retro-compatible with the cairo Python VM. test_utils = [ diff --git a/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs b/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs index c9ca674f7a..4d639e03e3 100644 --- a/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs +++ b/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs @@ -54,10 +54,8 @@ use crate::{ unsafe_keccak_finalize, }, math_utils::*, - memcpy_hint_utils::{ - add_segment, enter_scope, exit_scope, memcpy_continue_copying, memcpy_enter_scope, - }, - memset_utils::{memset_continue_loop, memset_enter_scope}, + memcpy_hint_utils::{add_segment, enter_scope, exit_scope, memcpy_enter_scope}, + memset_utils::{memset_enter_scope, memset_step_loop}, poseidon_utils::{n_greater_than_10, n_greater_than_2}, pow_utils::pow, secp::{ @@ -241,15 +239,20 @@ impl HintProcessorLogic for BuiltinHintProcessor { hint_code::MEMSET_ENTER_SCOPE => { memset_enter_scope(vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking) } - hint_code::MEMCPY_CONTINUE_COPYING => memcpy_continue_copying( + hint_code::MEMCPY_CONTINUE_COPYING => memset_step_loop( vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking, + "continue_copying", + ), + hint_code::MEMSET_CONTINUE_LOOP => memset_step_loop( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + "continue_loop", ), - hint_code::MEMSET_CONTINUE_LOOP => { - memset_continue_loop(vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking) - } hint_code::SPLIT_FELT => split_felt(vm, &hint_data.ids_data, &hint_data.ap_tracking), hint_code::UNSIGNED_DIV_REM => { unsigned_div_rem(vm, &hint_data.ids_data, &hint_data.ap_tracking) diff --git a/vm/src/hint_processor/builtin_hint_processor/cairo_keccak/keccak_hints.rs b/vm/src/hint_processor/builtin_hint_processor/cairo_keccak/keccak_hints.rs index 94317ec8ad..a73a85c8b4 100644 --- a/vm/src/hint_processor/builtin_hint_processor/cairo_keccak/keccak_hints.rs +++ b/vm/src/hint_processor/builtin_hint_processor/cairo_keccak/keccak_hints.rs @@ -56,8 +56,8 @@ pub fn keccak_write_args( let low = low.as_ref(); let high = high.as_ref(); - let low_args = [low & Felt252::new(u64::MAX), low >> 64]; - let high_args = [high & Felt252::new(u64::MAX), high >> 64]; + let low_args = [low & Felt252::new(u64::MAX), low >> 64_u32]; + let high_args = [high & Felt252::new(u64::MAX), high >> 64_u32]; let low_args: Vec<_> = low_args.into_iter().map(MaybeRelocatable::from).collect(); vm.write_arg(inputs_ptr, &low_args) diff --git a/vm/src/hint_processor/builtin_hint_processor/ec_utils.rs b/vm/src/hint_processor/builtin_hint_processor/ec_utils.rs index a36cc82da3..5ea381e0f9 100644 --- a/vm/src/hint_processor/builtin_hint_processor/ec_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/ec_utils.rs @@ -65,7 +65,7 @@ pub fn random_ec_point_hint( let m = get_integer_from_var_name("m", vm, ids_data, ap_tracking)?; let bytes: Vec = [p.x, p.y, m, q.x, q.y] .iter() - .flat_map(|x| to_padded_bytes(x)) + .flat_map(|x| x.to_be_bytes()) .collect(); let (x, y) = random_ec_point_seeded(bytes)?; let s_addr = get_relocatable_from_var_name("s", vm, ids_data, ap_tracking)?; @@ -121,7 +121,7 @@ pub fn chained_ec_op_random_ec_point_hint( .iter() .chain(m_range.iter()) .chain(q_range.iter()) - .flat_map(|x| to_padded_bytes(x)) + .flat_map(|x| x.to_be_bytes()) .collect(); let (x, y) = random_ec_point_seeded(bytes)?; let s_addr = get_relocatable_from_var_name("s", vm, ids_data, ap_tracking)?; @@ -152,14 +152,6 @@ pub fn recover_y_hint( Ok(()) } -// Returns the Felt252 as a vec of bytes of len 32, pads left with zeros -fn to_padded_bytes(n: &Felt252) -> Vec { - let felt_to_bytes = n.to_bytes_be(); - let mut bytes: Vec = vec![0; 32 - felt_to_bytes.len()]; - bytes.extend(felt_to_bytes); - bytes -} - // Returns a random non-zero point on the elliptic curve // y^2 = x^3 + alpha * x + beta (mod field_prime). // The point is created deterministically from the seed. diff --git a/vm/src/hint_processor/builtin_hint_processor/keccak_utils.rs b/vm/src/hint_processor/builtin_hint_processor/keccak_utils.rs index e0aa586ee4..36c4b63679 100644 --- a/vm/src/hint_processor/builtin_hint_processor/keccak_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/keccak_utils.rs @@ -1,4 +1,4 @@ -use crate::stdlib::{boxed::Box, cmp, collections::HashMap, ops::Shl, prelude::*}; +use crate::stdlib::{boxed::Box, cmp, collections::HashMap, prelude::*}; use crate::types::errors::math_errors::MathError; use crate::{ @@ -82,17 +82,12 @@ pub fn unsafe_keccak( let word = vm.get_integer(word_addr)?; let n_bytes = cmp::min(16, u64_length - byte_i); - if word.is_negative() || word.as_ref() >= &Felt252::one().shl(8 * (n_bytes as u32)) { + if word.is_negative() || word.as_ref() >= &(Felt252::one() << (8 * (n_bytes as u32))) { return Err(HintError::InvalidWordSize(Box::new(word.into_owned()))); } - let mut bytes = word.to_bytes_be(); - let mut bytes = { - let n_word_bytes = &bytes.len(); - left_pad(&mut bytes, n_bytes as usize - n_word_bytes) - }; - - keccak_input.append(&mut bytes); + let start = 32 - n_bytes as usize; + keccak_input.extend_from_slice(&word.to_be_bytes()[start..]); } let mut hasher = Keccak256::new(); @@ -157,12 +152,7 @@ pub fn unsafe_keccak_finalize( let range = vm.get_integer_range(start_ptr, n_elems)?; for word in range.into_iter() { - let mut bytes = word.to_bytes_be(); - let mut bytes = { - let n_word_bytes = &bytes.len(); - left_pad(&mut bytes, 16 - n_word_bytes) - }; - keccak_input.append(&mut bytes); + keccak_input.extend_from_slice(&word.to_be_bytes()[16..]); } let mut hasher = Keccak256::new(); @@ -181,13 +171,6 @@ pub fn unsafe_keccak_finalize( Ok(()) } -fn left_pad(bytes_vector: &mut [u8], n_zeros: usize) -> Vec { - let mut res: Vec = vec![0; n_zeros]; - res.extend(bytes_vector.iter()); - - res -} - // Implements hints of type : ids.output{num}_low = ids.output{num} & ((1 << 128) - 1) // ids.output{num}_high = ids.output{num} >> 128 pub fn split_output( @@ -200,7 +183,7 @@ pub fn split_output( let output_cow = get_integer_from_var_name(&output_name, vm, ids_data, ap_tracking)?; let output = output_cow.as_ref(); let low = output & Felt252::from(u128::MAX); - let high = output >> 128; + let high = output >> 128_u32; insert_value_from_var_name( &format!("output{}_high", num), high, @@ -286,8 +269,8 @@ pub fn split_output_mid_low_high( let binding = get_integer_from_var_name("output1", vm, ids_data, ap_tracking)?; let output1 = binding.as_ref(); let output1_low = output1 & Felt252::from((1u64 << (8 * 7)) - 1u64); - let tmp = output1 >> (8 * 7); - let output1_high = &tmp >> 128; + let tmp = output1 >> (8_u32 * 7); + let output1_high = &tmp >> 128_u32; let output1_mid = tmp & &Felt252::from(u128::MAX); insert_value_from_var_name("output1_high", output1_high, vm, ids_data, ap_tracking)?; insert_value_from_var_name("output1_mid", output1_mid, vm, ids_data, ap_tracking)?; diff --git a/vm/src/hint_processor/builtin_hint_processor/math_utils.rs b/vm/src/hint_processor/builtin_hint_processor/math_utils.rs index be27605888..086374df13 100644 --- a/vm/src/hint_processor/builtin_hint_processor/math_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/math_utils.rs @@ -30,7 +30,7 @@ use crate::{ }, }; use felt::Felt252; -use num_bigint::BigUint; +use num_bigint::{BigUint, Sign}; use num_integer::Integer; use num_traits::One; use num_traits::{Signed, Zero}; @@ -115,43 +115,45 @@ pub fn assert_le_felt( let prime_over_2_high = constants .get(PRIME_OVER_2_HIGH) .ok_or_else(|| HintError::MissingConstant(Box::new(PRIME_OVER_2_HIGH)))?; - let a = &get_integer_from_var_name("a", vm, ids_data, ap_tracking)? - .clone() - .into_owned(); - let b = &get_integer_from_var_name("b", vm, ids_data, ap_tracking)? - .clone() - .into_owned(); + let a = get_integer_from_var_name("a", vm, ids_data, ap_tracking)?.to_biguint(); + let b = get_integer_from_var_name("b", vm, ids_data, ap_tracking)?.to_biguint(); let range_check_ptr = get_ptr_from_var_name("range_check_ptr", vm, ids_data, ap_tracking)?; + // TODO: use UnsignedInteger for this + let prime_div2 = prime_div_constant(2)?; + let prime_div3 = prime_div_constant(3)?; + if a > b { - return Err(HintError::NonLeFelt252(Box::new((a.clone(), b.clone())))); + return Err(HintError::NonLeFelt252(Box::new(( + Felt252::from(a), + Felt252::from(b), + )))); } - let arc1 = b - a; - let arc2 = Felt252::zero() - Felt252::one() - b; - let mut lengths_and_indices = vec![(a, 0_i32), (&arc1, 1_i32), (&arc2, 2_i32)]; + let arc1 = &b - &a; + let arc2 = Felt252::prime() - 1_u32 - &b; + let mut lengths_and_indices = [(&a, 0_i32), (&arc1, 1_i32), (&arc2, 2_i32)]; lengths_and_indices.sort(); - if lengths_and_indices[0].0 > &div_prime_by_bound(Felt252::new(3_i32))? - || lengths_and_indices[1].0 > &div_prime_by_bound(Felt252::new(2_i32))? - { + // TODO: I believe this check can be removed + if lengths_and_indices[0].0 > &prime_div3 || lengths_and_indices[1].0 > &prime_div2 { return Err(HintError::ArcTooBig(Box::new(( - lengths_and_indices[0].0.clone(), - div_prime_by_bound(Felt252::new(3_i32))?, - lengths_and_indices[1].0.clone(), - div_prime_by_bound(Felt252::new(3_i32))?, + Felt252::from(lengths_and_indices[0].0.clone()), + Felt252::from(prime_div2), + Felt252::from(lengths_and_indices[1].0.clone()), + Felt252::from(prime_div3), )))); } let excluded = lengths_and_indices[2].1; exec_scopes.assign_or_update_variable("excluded", any_box!(Felt252::new(excluded))); - let (q_0, r_0) = (lengths_and_indices[0].0).div_mod_floor(prime_over_3_high); - let (q_1, r_1) = (lengths_and_indices[1].0).div_mod_floor(prime_over_2_high); + let (q_0, r_0) = (lengths_and_indices[0].0).div_mod_floor(&prime_over_3_high.to_biguint()); + let (q_1, r_1) = (lengths_and_indices[1].0).div_mod_floor(&prime_over_2_high.to_biguint()); - vm.insert_value((range_check_ptr + 1_i32)?, q_0)?; - vm.insert_value(range_check_ptr, r_0)?; - vm.insert_value((range_check_ptr + 3_i32)?, q_1)?; - vm.insert_value((range_check_ptr + 2_i32)?, r_1)?; + vm.insert_value(range_check_ptr, Felt252::from(r_0))?; + vm.insert_value((range_check_ptr + 1_i32)?, Felt252::from(q_0))?; + vm.insert_value((range_check_ptr + 2_i32)?, Felt252::from(r_1))?; + vm.insert_value((range_check_ptr + 3_i32)?, Felt252::from(q_1))?; Ok(()) } @@ -380,9 +382,12 @@ pub fn is_positive( let value = get_integer_from_var_name("value", vm, ids_data, ap_tracking)?; let value_as_int = value.to_signed_felt(); let range_check_builtin = vm.get_range_check_builtin()?; + + // Avoid using abs so we don't allocate a new BigInt + let (sign, abs_value) = value_as_int.into_parts(); //Main logic (assert a is positive) match &range_check_builtin._bound { - Some(bound) if value_as_int.abs() > bound.to_bigint() => { + Some(bound) if abs_value > bound.to_biguint() => { return Err(HintError::ValueOutsideValidRange(Box::new( value.into_owned(), ))) @@ -390,7 +395,7 @@ pub fn is_positive( _ => {} }; - let result = Felt252::from(value_as_int.is_positive() as u8); + let result = Felt252::from((sign == Sign::Plus) as u8); insert_value_from_var_name("is_positive", result, vm, ids_data, ap_tracking) } @@ -468,10 +473,10 @@ pub fn signed_div_rem( builtin_bound.clone(), )))); } - Some(builtin_bound) if bound.as_ref() > &builtin_bound.shr(1) => { + Some(builtin_bound) if bound.as_ref() > &(builtin_bound >> 1_u32) => { return Err(HintError::OutOfValidRange(Box::new(( bound.into_owned(), - builtin_bound.shr(1), + builtin_bound >> 1_u32, )))); } None if div.is_zero() => { @@ -682,7 +687,7 @@ pub fn is_quad_residue( if x.is_zero() || x.is_one() { insert_value_from_var_name("y", x.as_ref().clone(), vm, ids_data, ap_tracking) - } else if Pow::pow(x.as_ref(), &(Felt252::max_value() >> 1)).is_one() { + } else if Pow::pow(x.as_ref(), &(Felt252::max_value() >> 1_u32)).is_one() { insert_value_from_var_name("y", &x.sqrt(), vm, ids_data, ap_tracking) } else { insert_value_from_var_name( @@ -702,6 +707,12 @@ fn div_prime_by_bound(bound: Felt252) -> Result { Ok(Felt252::new(limit)) } +fn prime_div_constant(bound: u32) -> Result { + let prime: &BigUint = &CAIRO_PRIME; + let limit = prime / bound; + Ok(limit) +} + /* Implements hint: %{ ids.a_lsb = ids.a & 1 @@ -1800,6 +1811,29 @@ mod tests { ) } + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn signed_div_rem_out_of_range_bound() { + let hint_code = "from starkware.cairo.common.math_utils import as_int, assert_integer\n\nassert_integer(ids.div)\nassert 0 < ids.div <= PRIME // range_check_builtin.bound, \\\n f'div={hex(ids.div)} is out of the valid range.'\n\nassert_integer(ids.bound)\nassert ids.bound <= range_check_builtin.bound // 2, \\\n f'bound={hex(ids.bound)} is out of the valid range.'\n\nint_value = as_int(ids.value, PRIME)\nq, ids.r = divmod(int_value, ids.div)\n\nassert -ids.bound <= q < ids.bound, \\\n f'{int_value} / {ids.div} = {q} is out of the range [{-ids.bound}, {ids.bound}).'\n\nids.biased_q = q + ids.bound"; + let mut vm = vm_with_range_check!(); + //Initialize fp + vm.run_context.fp = 6; + //Insert ids into memory + let bound = vm.get_range_check_builtin().unwrap()._bound.clone(); + vm.segments = segments![((1, 3), (5)), ((1, 4), 10)]; + vm.insert_value((1, 5).into(), bound.clone().unwrap()) + .unwrap(); + //Create ids + let ids_data = ids_data!["r", "biased_q", "range_check_ptr", "div", "value", "bound"]; + //Execute the hint + let builtin_bound = felt_str!("340282366920938463463374607431768211456"); + assert_matches!( + run_hint!(vm, ids_data, hint_code), + Err(HintError::OutOfValidRange(bx)) + if *bx == (bound.unwrap(), builtin_bound >> 1_u32) + ) + } + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn signed_div_rem_no_range_check_builtin() { @@ -2328,7 +2362,7 @@ mod tests { if x.is_zero() || x.is_one() { assert_eq!(vm.get_integer(Relocatable::from((1, 0))).unwrap().as_ref(), x); - } else if x.pow(&(Felt252::max_value() >> 1)).is_one() { + } else if x.pow(&(Felt252::max_value() >> 1_u32)).is_one() { assert_eq!(vm.get_integer(Relocatable::from((1, 0))).unwrap().into_owned(), x.sqrt()); } else { assert_eq!(vm.get_integer(Relocatable::from((1, 0))).unwrap().into_owned(), (x / Felt252::new(3)).sqrt()); diff --git a/vm/src/hint_processor/builtin_hint_processor/memcpy_hint_utils.rs b/vm/src/hint_processor/builtin_hint_processor/memcpy_hint_utils.rs index 83127d7412..150b712242 100644 --- a/vm/src/hint_processor/builtin_hint_processor/memcpy_hint_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/memcpy_hint_utils.rs @@ -2,17 +2,13 @@ use crate::stdlib::{any::Any, collections::HashMap, prelude::*}; use crate::{ hint_processor::{ - builtin_hint_processor::hint_utils::{ - get_integer_from_var_name, insert_value_from_var_name, insert_value_into_ap, - }, + builtin_hint_processor::hint_utils::{get_integer_from_var_name, insert_value_into_ap}, hint_processor_definition::HintReference, }, serde::deserialize_program::ApTracking, types::exec_scope::ExecutionScopes, vm::{errors::hint_errors::HintError, vm_core::VirtualMachine}, }; -use felt::Felt252; -use num_traits::{One, Zero}; //Implements hint: memory[ap] = segments.add() pub fn add_segment(vm: &mut VirtualMachine) -> Result<(), HintError> { @@ -46,38 +42,6 @@ pub fn memcpy_enter_scope( Ok(()) } -// Implements hint: -// %{ -// n -= 1 -// ids.continue_copying = 1 if n > 0 else 0 -// %} -pub fn memcpy_continue_copying( - vm: &mut VirtualMachine, - exec_scopes: &mut ExecutionScopes, - ids_data: &HashMap, - ap_tracking: &ApTracking, -) -> Result<(), HintError> { - // get `n` variable from vm scope - let n = exec_scopes.get_ref::("n")?; - // this variable will hold the value of `n - 1` - let new_n = n - 1; - // if it is positive, insert 1 in the address of `continue_copying` - // else, insert 0 - if new_n.is_zero() { - insert_value_from_var_name("continue_copying", &new_n, vm, ids_data, ap_tracking)?; - } else { - insert_value_from_var_name( - "continue_copying", - Felt252::one(), - vm, - ids_data, - ap_tracking, - )?; - } - exec_scopes.insert_value("n", new_n); - Ok(()) -} - #[cfg(test)] mod tests { use super::*; @@ -85,6 +49,7 @@ mod tests { use crate::utils::test_utils::*; use assert_matches::assert_matches; + use felt::Felt252; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; diff --git a/vm/src/hint_processor/builtin_hint_processor/memset_utils.rs b/vm/src/hint_processor/builtin_hint_processor/memset_utils.rs index 3673a49d05..b5182522e9 100644 --- a/vm/src/hint_processor/builtin_hint_processor/memset_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/memset_utils.rs @@ -12,7 +12,7 @@ use crate::{ vm::{errors::hint_errors::HintError, vm_core::VirtualMachine}, }; use felt::Felt252; -use num_traits::Signed; +use num_traits::{One, Signed}; // Implements hint: // %{ vm_enter_scope({'n': ids.n}) %} @@ -31,26 +31,26 @@ pub fn memset_enter_scope( /* Implements hint: %{ n -= 1 - ids.continue_loop = 1 if n > 0 else 0 + ids.`i_name` = 1 if n > 0 else 0 %} */ -pub fn memset_continue_loop( +pub fn memset_step_loop( vm: &mut VirtualMachine, exec_scopes: &mut ExecutionScopes, ids_data: &HashMap, ap_tracking: &ApTracking, + i_name: &'static str, ) -> Result<(), HintError> { // get `n` variable from vm scope - let n = exec_scopes.get_ref::("n")?; + let n = exec_scopes.get_mut_ref::("n")?; // this variable will hold the value of `n - 1` - let new_n = n - 1; + *n -= Felt252::one(); // if `new_n` is positive, insert 1 in the address of `continue_loop` // else, insert 0 - let should_continue = Felt252::new(new_n.is_positive() as i32); - insert_value_from_var_name("continue_loop", should_continue, vm, ids_data, ap_tracking)?; + let flag = Felt252::new(n.is_positive()); + insert_value_from_var_name(i_name, flag, vm, ids_data, ap_tracking)?; // Reassign `n` with `n - 1` // we do it at the end of the function so that the borrow checker doesn't complain - exec_scopes.insert_value("n", new_n); Ok(()) } diff --git a/vm/src/hint_processor/builtin_hint_processor/uint256_utils.rs b/vm/src/hint_processor/builtin_hint_processor/uint256_utils.rs index f1a6f71eb1..3c4b9352cc 100644 --- a/vm/src/hint_processor/builtin_hint_processor/uint256_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/uint256_utils.rs @@ -95,7 +95,7 @@ impl<'a> From<&BigUint> for Uint256<'a> { impl<'a> From for Uint256<'a> { fn from(value: Felt252) -> Self { let low = Felt252::new(u128::MAX) & &value; - let high = value >> 128; + let high = value >> 128_u32; Self::from_values(low, high) } } @@ -231,11 +231,7 @@ pub fn split_64( let a = get_integer_from_var_name("a", vm, ids_data, ap_tracking)?; let mut digits = a.iter_u64_digits(); let low = Felt252::new(digits.next().unwrap_or(0u64)); - let high = if digits.len() <= 1 { - Felt252::new(digits.next().unwrap_or(0u64)) - } else { - a.as_ref().shr(64_u32) - }; + let high = a.as_ref() >> 64_u32; insert_value_from_var_name("high", high, vm, ids_data, ap_tracking)?; insert_value_from_var_name("low", low, vm, ids_data, ap_tracking) } diff --git a/vm/src/hint_processor/cairo_1_hint_processor/hint_processor_utils.rs b/vm/src/hint_processor/cairo_1_hint_processor/hint_processor_utils.rs index e0b89b1d56..185ceb69b1 100644 --- a/vm/src/hint_processor/cairo_1_hint_processor/hint_processor_utils.rs +++ b/vm/src/hint_processor/cairo_1_hint_processor/hint_processor_utils.rs @@ -4,6 +4,7 @@ use crate::vm::errors::{hint_errors::HintError, vm_errors::VirtualMachineError}; use crate::vm::vm_core::VirtualMachine; use cairo_lang_casm::operand::{CellRef, DerefOrImmediate, Operation, Register, ResOperand}; use felt::Felt252; +use num_traits::Zero; /// Extracts a parameter assumed to be a buffer. pub(crate) fn extract_buffer(buffer: &ResOperand) -> Result<(&CellRef, Felt252), HintError> { let (cell, base_offset) = match buffer { @@ -99,7 +100,7 @@ pub(crate) fn res_operand_get_val( pub(crate) fn as_cairo_short_string(value: &Felt252) -> Option { let mut as_string = String::default(); let mut is_end = false; - for byte in value.to_bytes_be() { + for byte in value.to_be_bytes().into_iter().skip_while(Zero::is_zero) { if byte == 0 { is_end = true; } else if is_end || !byte.is_ascii() { @@ -110,3 +111,17 @@ pub(crate) fn as_cairo_short_string(value: &Felt252) -> Option { } Some(as_string) } + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn simple_as_cairo_short_string() { + // Values extracted from cairo book example + let s = "Hello, Scarb!"; + let x = Felt252::new(5735816763073854913753904210465_u128); + assert!(s.is_ascii()); + let cairo_string = as_cairo_short_string(&x).expect("call to as_cairo_short_string failed"); + assert_eq!(cairo_string, s); + } +} diff --git a/vm/src/types/program.rs b/vm/src/types/program.rs index 321983dc4d..b353acdc97 100644 --- a/vm/src/types/program.rs +++ b/vm/src/types/program.rs @@ -185,7 +185,7 @@ impl TryFrom for Program { let data = value .bytecode .iter() - .map(|x| MaybeRelocatable::from(Felt252::from(&x.value))) + .map(|x| MaybeRelocatable::from(Felt252::from(x.value.clone()))) .collect(); //Hint data is going to be hosted processor-side, hints field will only store the pc where hints are located. // Only one pc will be stored, so the hint processor will be responsible for executing all hints for a given pc diff --git a/vm/src/vm/runners/builtin_runner/range_check.rs b/vm/src/vm/runners/builtin_runner/range_check.rs index b20b060088..6188a3b05d 100644 --- a/vm/src/vm/runners/builtin_runner/range_check.rs +++ b/vm/src/vm/runners/builtin_runner/range_check.rs @@ -96,7 +96,7 @@ impl RangeCheckBuiltinRunner { } else { Err(MemoryError::RangeCheckNumOutOfBounds(Box::new(( num.into_owned(), - Felt252::one().shl((N_PARTS * INNER_RC_BOUND_SHIFT) as u32), + Felt252::one() << ((N_PARTS * INNER_RC_BOUND_SHIFT) as u32), )))) } },