From 523322812ab37ca8091d16734371f60cf13fb979 Mon Sep 17 00:00:00 2001 From: Marcos Villagra <71292159+mdvillagra@users.noreply.github.com> Date: Mon, 27 Nov 2023 10:49:31 -0300 Subject: [PATCH 01/29] updated readme (#699) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 981fabc6f5..aeaf745d82 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,8 @@ List of symbols: | BLS12-381 | :heavy_check_mark: | :heavy_check_mark: | | | | | BLS12-377 | πŸ—οΈ | :heavy_check_mark: | | :heavy_check_mark: | | | BN-254 | :x: | :heavy_check_mark: | | | | -| Pallas | :x: | :heavy_check_mark: | | | | -| Vesta | :x: | :heavy_check_mark: | | | | +| Pallas | πŸ—οΈ | :heavy_check_mark: | | | | +| Vesta | πŸ—οΈ | :heavy_check_mark: | | | | | Bandersnatch | πŸ—οΈ | :heavy_check_mark: | | | | | **STARKs** | **Lambdaworks** | **Arkworks** | **Halo2** | **gnark** | **Constantine** | | STARK Prover | :heavy_check_mark: | :x: | | :x: | | From 1530d4beaa9c6e2a4ad14f34e8cbad9a8740c2f4 Mon Sep 17 00:00:00 2001 From: PatStiles <33334338+PatStiles@users.noreply.github.com> Date: Mon, 27 Nov 2023 12:00:37 -0600 Subject: [PATCH 02/29] feat(ecc): Add subgroup check BLS12-381 using untwist frobenius endomorphism (#649) * add naive subgroup check to pairing * add naive test * fmt * move subgroup check to IsGroup trait * nits * add jacobian point file * refactor to use endomorphism * nit * Add untwist tests * nit * add out of subgroup checks * add doc comments * fix fmt * add unwraps to groth16 * make imports compatible with std * fmt * nits * ci --------- Co-authored-by: Mauro Toscano <12560266+MauroToscano@users.noreply.github.com> --- crypto/src/commitments/kzg.rs | 2 +- .../curves/bls12_381/compression.rs | 33 ++-- .../curves/bls12_381/curve.rs | 177 +++++++++++++++++- .../curves/bls12_381/field_extension.rs | 5 + .../curves/bls12_381/pairing.rs | 56 ++++-- math/src/elliptic_curve/traits.rs | 11 +- math/src/errors.rs | 5 + provers/groth16/src/setup.rs | 2 +- provers/groth16/src/verifier.rs | 6 +- 9 files changed, 250 insertions(+), 47 deletions(-) diff --git a/crypto/src/commitments/kzg.rs b/crypto/src/commitments/kzg.rs index 7abc65efba..fb33cb0ffd 100644 --- a/crypto/src/commitments/kzg.rs +++ b/crypto/src/commitments/kzg.rs @@ -200,7 +200,7 @@ impl>, P &(alpha_g2.operate_with(&(g2.operate_with_self(x.representative())).neg())), ), ]); - e == FieldElement::one() + e == Ok(FieldElement::one()) } fn open_batch( diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs index a822c3d4dd..effa080bc1 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs @@ -1,27 +1,21 @@ use super::field_extension::BLS12381PrimeField; -use crate::cyclic_group::IsGroup; -use crate::elliptic_curve::short_weierstrass::curves::bls12_381::curve::BLS12381Curve; -use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint; -use crate::field::element::FieldElement; -use crate::unsigned_integer::element::U256; - -#[cfg(feature = "std")] use crate::{ - elliptic_curve::traits::FromAffine, errors::ByteConversionError, traits::ByteConversion, + elliptic_curve::short_weierstrass::{ + curves::bls12_381::curve::BLS12381Curve, point::ShortWeierstrassProjectivePoint, + }, + field::element::FieldElement, }; #[cfg(feature = "std")] use std::{cmp::Ordering, ops::Neg}; +#[cfg(feature = "std")] +use crate::{ + cyclic_group::IsGroup, elliptic_curve::traits::FromAffine, errors::ByteConversionError, + traits::ByteConversion, +}; + pub type G1Point = ShortWeierstrassProjectivePoint; pub type BLS12381FieldElement = FieldElement; -const MODULUS: U256 = - U256::from_hex_unchecked("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"); - -pub fn check_point_is_in_subgroup(point: &G1Point) -> bool { - let inf = G1Point::neutral_element(); - let aux_point = point.operate_with_self(MODULUS); - inf == aux_point -} #[cfg(feature = "std")] pub fn decompress_g1_point(input_bytes: &mut [u8; 48]) -> Result { @@ -68,7 +62,8 @@ pub fn decompress_g1_point(input_bytes: &mut [u8; 48]) -> Result; pub type BLS12381TwistCurveFieldElement = FieldElement; @@ -35,18 +43,111 @@ impl IsShortWeierstrass for BLS12381Curve { } } +/// This is equal to the frobenius trace of the BLS12 381 curve minus one or seed value z. +pub const MILLER_LOOP_CONSTANT: u64 = 0xd201000000010000; + +/// 𝛽 : primitive cube root of unity of πΉβ‚š that Β§satisfies the minimal equation +/// 𝛽² + 𝛽 + 1 = 0 mod 𝑝 +pub const CUBE_ROOT_OF_UNITY_G1: BLS12381FieldElement = FieldElement::from_hex_unchecked( + "5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe", +); + +/// x-coordinate of 𝜁 ∘ πœ‹_q ∘ 𝜁⁻¹, where 𝜁 is the isomorphism u:E'(π”½β‚šβ‚†) βˆ’> E(π”½β‚šβ‚β‚‚) from the twist to E +pub const ENDO_U: BLS12381TwistCurveFieldElement = +BLS12381TwistCurveFieldElement::const_from_raw([ + FieldElement::from_hex_unchecked("0"), + FieldElement::from_hex_unchecked("1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaad") +]); + +/// y-coordinate of 𝜁 ∘ πœ‹_q ∘ 𝜁⁻¹, where 𝜁 is the isomorphism u:E'(π”½β‚šβ‚†) βˆ’> E(π”½β‚šβ‚β‚‚) from the twist to E +pub const ENDO_V: BLS12381TwistCurveFieldElement = +BLS12381TwistCurveFieldElement::const_from_raw([ + FieldElement::from_hex_unchecked("135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2"), + FieldElement::from_hex_unchecked("6af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09") +]); + +impl ShortWeierstrassProjectivePoint { + /// Returns πœ™(P) = (π‘₯, 𝑦) β‡’ (𝛽π‘₯, 𝑦), where 𝛽 is the Cube Root of Unity in the base prime field + /// https://eprint.iacr.org/2022/352.pdf 2 Preliminaries + fn phi(&self) -> Self { + // This clone is unsightly + let mut a = self.clone(); + a.0.value[0] = a.x() * CUBE_ROOT_OF_UNITY_G1; + a + } + + /// πœ™(P) = βˆ’π‘’Β²P + /// https://eprint.iacr.org/2022/352.pdf 4.3 Prop. 4 + pub fn is_in_subgroup(&self) -> bool { + self.operate_with_self(MILLER_LOOP_CONSTANT) + .operate_with_self(MILLER_LOOP_CONSTANT) + .neg() + == self.phi() + } +} + +impl ShortWeierstrassProjectivePoint { + /// πœ“(P) = 𝜁 ∘ πœ‹β‚š ∘ 𝜁⁻¹, where 𝜁 is the isomorphism u:E'(π”½β‚šβ‚†) βˆ’> E(π”½β‚šβ‚β‚‚) from the twist to E,, πœ‹β‚š is the p-power frobenius endomorphism + /// and πœ“ satisifies minmal equation 𝑋² + 𝑑𝑋 + π‘ž = 𝑂 + /// https://eprint.iacr.org/2022/352.pdf 4.2 (7) + fn psi(&self) -> Self { + let [x, y, z] = self.coordinates(); + Self::new([ + x.conjugate() * ENDO_U, + y.conjugate() * ENDO_V, + z.conjugate(), + ]) + } + + /// πœ“(P) = 𝑒P, where 𝑒 = SEED of the curve + /// https://eprint.iacr.org/2022/352.pdf 4.2 + pub fn is_in_subgroup(&self) -> bool { + self.psi() == self.operate_with_self(MILLER_LOOP_CONSTANT).neg() + } +} + #[cfg(test)] mod tests { use super::*; use crate::{ - cyclic_group::IsGroup, elliptic_curve::traits::EllipticCurveError, + cyclic_group::IsGroup, + elliptic_curve::{ + short_weierstrass::curves::bls12_381::field_extension::BLS12381_PRIME_FIELD_ORDER, + traits::EllipticCurveError, + }, field::element::FieldElement, + unsigned_integer::element::U384, }; - use super::BLS12381Curve; + // -15132376222941642751 = MILLER_LOOP_CONSTANT + 1 = -d20100000000ffff + // we want the positive of this coordinate based on x^2 - tx + q + pub const TRACE_OF_FROBENIUS: U256 = U256::from_u64(15132376222941642751); + + const ENDO_U_2: BLS12381TwistCurveFieldElement = + BLS12381TwistCurveFieldElement::const_from_raw([ + FieldElement::from_hex_unchecked("1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac"), + FieldElement::from_hex_unchecked("0") + ]); + + const ENDO_V_2: BLS12381TwistCurveFieldElement = + BLS12381TwistCurveFieldElement::const_from_raw([ + FieldElement::from_hex_unchecked("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa"), + FieldElement::from_hex_unchecked("0") + ]); + + // Cmoputes the psi^2() 'Untwist Frobenius Endomorphism' + fn psi_square( + p: &ShortWeierstrassProjectivePoint, + ) -> ShortWeierstrassProjectivePoint { + let [x, y, z] = p.coordinates(); + // Since power of frobenius map is 2 we apply once as applying twice is inverse + ShortWeierstrassProjectivePoint::new([x * ENDO_U_2, y * ENDO_V_2, z.clone()]) + } #[allow(clippy::upper_case_acronyms)] type FEE = FieldElement; + #[allow(clippy::upper_case_acronyms)] + type FTE = FieldElement; fn point_1() -> ShortWeierstrassProjectivePoint { let x = FEE::new_base("36bb494facde72d0da5c770c4b16d9b2d45cfdc27604a25a1a80b020798e5b0dbd4c6d939a8f8820f042a29ce552ee5"); @@ -117,4 +218,74 @@ mod tests { g.operate_with_self(3_u16) ); } + + #[test] + fn generator_g1_is_in_subgroup() { + let g = BLS12381Curve::generator(); + assert!(g.is_in_subgroup()) + } + + #[test] + fn arbitrary_g1_point_is_in_subgroup() { + let g = BLS12381Curve::generator().operate_with_self(32u64); + assert!(g.is_in_subgroup()) + } + + //TODO + #[test] + fn arbitrary_g1_point_not_in_subgroup() { + let x = FEE::new_base("178212cbe4a3026c051d4f867364b3ea84af623f93233b347ffcd3d6b16f16e0a7aedbe1c78d33c6beca76b2b75c8486"); + let y = FEE::new_base("13a8b1347e5b43bc4051754b2a29928b5df78cf03ca3b1f73d0424b09fccdef116c9f0ecbec7420a99b2dd785209e9d"); + let p = BLS12381Curve::create_point_from_affine(x, y).unwrap(); + assert!(!p.is_in_subgroup()) + } + + #[test] + fn generator_g2_is_in_subgroup() { + let g = BLS12381TwistCurve::generator(); + assert!(g.is_in_subgroup()) + } + + #[test] + fn arbitrary_g2_point_is_in_subgroup() { + let g = BLS12381TwistCurve::generator().operate_with_self(32u64); + assert!(g.is_in_subgroup()) + } + + //`TODO` + #[test] + fn arbitrary_g2_point_not_in_subgroup() { + let x = FTE::new([ + FEE::new(U384::from_hex_unchecked("97798b4a61ac301bbee71e36b5174e2f4adfe3e1729bdae1fcc9965ae84181be373aa80414823eed694f1270014012d")), + FEE::new(U384::from_hex_unchecked("c9852cc6e61868966249aec153b50b29b3c22409f4c7880fd13121981c103c8ef84d9ea29b552431360e82cf69219fa")) + ]); + let y = FTE::new([ + FEE::new(U384::from_hex_unchecked("16cb3a60f3fa52c8273aceeb94c4c7303e8074aa9eedec7355bbb1e8cceedd4ec1497f573f62822140377b8e339619ed")), + FEE::new(U384::from_hex_unchecked("1cd919b08afe06bebe9adf6223a55868a6fd8b77efc5c67b60fff39be36e9b44b7f10db16827c83b43ad2dad1947778")) + ]); + + let p = BLS12381TwistCurve::create_point_from_affine(x, y).unwrap(); + assert!(!p.is_in_subgroup()) + } + + #[test] + fn g2_conjugate_works() { + let a = FTE::zero(); + let mut expected = a.conjugate(); + expected = expected.conjugate(); + + assert_eq!(a, expected); + } + + #[test] + fn untwist_morphism_has_minimal_poly() { + // generator + let p = BLS12381TwistCurve::generator(); + let psi_square = psi_square(&p); + let tx = p.psi().operate_with_self(TRACE_OF_FROBENIUS).neg(); + let q = p.operate_with_self(BLS12381_PRIME_FIELD_ORDER); + // Minimal Polynomial of Untwist Frobenius Endomorphism: X^2 + tX + q, where X = psh(P) -> psi(p)^2 - t * psi(p) + q * p = 0 + let min_poly = psi_square.operate_with(&tx.neg()).operate_with(&q); + assert!(min_poly.is_neutral_element()) + } } diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs index 2c1246bcfa..e8cff61963 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs @@ -181,6 +181,11 @@ impl FieldElement { pub fn new_base(a_hex: &str) -> Self { Self::new([FieldElement::new(U384::from(a_hex)), FieldElement::zero()]) } + + pub fn conjugate(&self) -> Self { + let [a, b] = self.value(); + Self::new([a.clone(), -b]) + } } impl FieldElement { diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs index 376434bd7d..25bfb579e1 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs @@ -1,18 +1,26 @@ -use super::field_extension::{Degree12ExtensionField, Degree2ExtensionField}; -use crate::{ - elliptic_curve::short_weierstrass::curves::bls12_381::field_extension::Degree6ExtensionField, - field::element::FieldElement, unsigned_integer::element::UnsignedInteger, +use super::{ + curve::{BLS12381Curve, MILLER_LOOP_CONSTANT}, + field_extension::{Degree12ExtensionField, Degree2ExtensionField}, + twist::BLS12381TwistCurve, }; - -use super::{curve::BLS12381Curve, twist::BLS12381TwistCurve}; use crate::{ cyclic_group::IsGroup, - elliptic_curve::short_weierstrass::curves::bls12_381::field_extension::LevelTwoResidue, - elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint, - elliptic_curve::short_weierstrass::traits::IsShortWeierstrass, - elliptic_curve::traits::IsPairing, field::extensions::cubic::HasCubicNonResidue, + elliptic_curve::{ + short_weierstrass::{ + curves::bls12_381::field_extension::{Degree6ExtensionField, LevelTwoResidue}, + point::ShortWeierstrassProjectivePoint, + traits::IsShortWeierstrass, + }, + traits::IsPairing, + }, + errors::PairingError, + field::{element::FieldElement, extensions::cubic::HasCubicNonResidue}, + unsigned_integer::element::{UnsignedInteger, U256}, }; +pub const SUBGROUP_ORDER: U256 = + U256::from_hex_unchecked("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"); + #[derive(Clone)] pub struct BLS12381AtePairing; impl IsPairing for BLS12381AtePairing { @@ -23,21 +31,22 @@ impl IsPairing for BLS12381AtePairing { /// Compute the product of the ate pairings for a list of point pairs. fn compute_batch( pairs: &[(&Self::G1Point, &Self::G2Point)], - ) -> FieldElement { + ) -> Result, PairingError> { let mut result = FieldElement::one(); for (p, q) in pairs { + if !p.is_in_subgroup() || !q.is_in_subgroup() { + return Err(PairingError::PointNotInSubgroup); + } if !p.is_neutral_element() && !q.is_neutral_element() { let p = p.to_affine(); let q = q.to_affine(); result = result * miller(&q, &p); } } - final_exponentiation(&result) + Ok(final_exponentiation(&result)) } } -/// This is equal to the frobenius trace of the BLS12 381 curve minus one. -const MILLER_LOOP_CONSTANT: u64 = 0xd201000000010000; fn double_accumulate_line( t: &mut ShortWeierstrassProjectivePoint, p: &ShortWeierstrassProjectivePoint, @@ -255,7 +264,8 @@ mod tests { &p.operate_with_self(a * b).to_affine(), &q.neg().to_affine(), ), - ]); + ]) + .unwrap(); assert_eq!(result, FieldElement::one()); } @@ -263,12 +273,24 @@ mod tests { fn ate_pairing_returns_one_when_one_element_is_the_neutral_element() { let p = BLS12381Curve::generator().to_affine(); let q = ShortWeierstrassProjectivePoint::neutral_element(); - let result = BLS12381AtePairing::compute_batch(&[(&p.to_affine(), &q)]); + let result = BLS12381AtePairing::compute_batch(&[(&p.to_affine(), &q)]).unwrap(); assert_eq!(result, FieldElement::one()); let p = ShortWeierstrassProjectivePoint::neutral_element(); let q = BLS12381TwistCurve::generator(); - let result = BLS12381AtePairing::compute_batch(&[(&p, &q.to_affine())]); + let result = BLS12381AtePairing::compute_batch(&[(&p, &q.to_affine())]).unwrap(); assert_eq!(result, FieldElement::one()); } + + #[test] + fn ate_pairing_errors_when_one_element_is_not_in_subgroup() { + let p = ShortWeierstrassProjectivePoint::new([ + FieldElement::one(), + FieldElement::one(), + FieldElement::one(), + ]); + let q = ShortWeierstrassProjectivePoint::neutral_element(); + let result = BLS12381AtePairing::compute_batch(&[(&p.to_affine(), &q)]); + assert!(result.is_err()) + } } diff --git a/math/src/elliptic_curve/traits.rs b/math/src/elliptic_curve/traits.rs index 675c2ff491..1e3355b48c 100644 --- a/math/src/elliptic_curve/traits.rs +++ b/math/src/elliptic_curve/traits.rs @@ -1,5 +1,6 @@ use crate::{ cyclic_group::IsGroup, + errors::PairingError, field::{element::FieldElement, traits::IsField}, }; use core::fmt::Debug; @@ -41,11 +42,15 @@ pub trait IsPairing { type OutputField: IsField; /// Compute the product of the pairings for a list of point pairs. - fn compute_batch(pairs: &[(&Self::G1Point, &Self::G2Point)]) - -> FieldElement; + fn compute_batch( + pairs: &[(&Self::G1Point, &Self::G2Point)], + ) -> Result, PairingError>; /// Compute the ate pairing between point `p` in G1 and `q` in G2. - fn compute(p: &Self::G1Point, q: &Self::G2Point) -> FieldElement { + fn compute( + p: &Self::G1Point, + q: &Self::G2Point, + ) -> Result, PairingError> { Self::compute_batch(&[(p, q)]) } } diff --git a/math/src/errors.rs b/math/src/errors.rs index 4a33326305..d0ceb22ecb 100644 --- a/math/src/errors.rs +++ b/math/src/errors.rs @@ -22,6 +22,11 @@ pub enum DeserializationError { InvalidValue, } +#[derive(Debug, PartialEq, Eq)] +pub enum PairingError { + PointNotInSubgroup, +} + impl From for DeserializationError { fn from(error: ByteConversionError) -> Self { match error { diff --git a/provers/groth16/src/setup.rs b/provers/groth16/src/setup.rs index 4847d89221..f1611cc3cb 100644 --- a/provers/groth16/src/setup.rs +++ b/provers/groth16/src/setup.rs @@ -88,7 +88,7 @@ pub fn setup(qap: &QuadraticArithmeticProgram) -> (ProvingKey, VerifyingKey) { let alpha_g1 = g1.operate_with_self(tw.alpha.representative()); let beta_g2 = g2.operate_with_self(tw.beta.representative()); - let alpha_g1_times_beta_g2 = Pairing::compute(&alpha_g1, &beta_g2); + let alpha_g1_times_beta_g2 = Pairing::compute(&alpha_g1, &beta_g2).unwrap(); let delta_g2 = g2.operate_with_self(tw.delta.representative()); diff --git a/provers/groth16/src/verifier.rs b/provers/groth16/src/verifier.rs index e5619907fd..b83c4b215e 100644 --- a/provers/groth16/src/verifier.rs +++ b/provers/groth16/src/verifier.rs @@ -15,8 +15,8 @@ pub fn verify(vk: &VerifyingKey, proof: &Proof, pub_inputs: &[FrElement]) -> boo ) .unwrap(); - Pairing::compute(&proof.pi3, &vk.delta_g2) + Pairing::compute(&proof.pi3, &vk.delta_g2).unwrap() * vk.alpha_g1_times_beta_g2.clone() - * Pairing::compute(&k_tau_assigned_verifier_g1, &vk.gamma_g2) - == Pairing::compute(&proof.pi1, &proof.pi2) + * Pairing::compute(&k_tau_assigned_verifier_g1, &vk.gamma_g2).unwrap() + == Pairing::compute(&proof.pi1, &proof.pi2).unwrap() } From f5208fff54c78038a465109e2e8d0701d8ff0431 Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Wed, 29 Nov 2023 13:37:46 -0300 Subject: [PATCH 03/29] chore: remove legacy Poseidon hash implementation (#702) * chore: remove legacy Poseidon hash implementation * cargo fmt * clippy * Use Stark field * cargo fmt --- .../hash/poseidon/bls12381/t2/mds_matrix.csv | 3 - .../poseidon/bls12381/t2/round_constants.csv | 1 - .../hash/poseidon/bls12381/t3/mds_matrix.csv | 3 - .../poseidon/bls12381/t3/round_constants.csv | 1 - crypto/src/hash/poseidon/mod.rs | 309 ------------------ crypto/src/hash/poseidon/parameters.rs | 82 ----- crypto/src/hash/poseidon/s128b/mds_matrix.csv | 3 - .../hash/poseidon/s128b/round_constants.csv | 1 - examples/merkle-tree-cli/src/main.rs | 18 +- 9 files changed, 8 insertions(+), 413 deletions(-) delete mode 100644 crypto/src/hash/poseidon/bls12381/t2/mds_matrix.csv delete mode 100644 crypto/src/hash/poseidon/bls12381/t2/round_constants.csv delete mode 100644 crypto/src/hash/poseidon/bls12381/t3/mds_matrix.csv delete mode 100644 crypto/src/hash/poseidon/bls12381/t3/round_constants.csv delete mode 100644 crypto/src/hash/poseidon/parameters.rs delete mode 100644 crypto/src/hash/poseidon/s128b/mds_matrix.csv delete mode 100644 crypto/src/hash/poseidon/s128b/round_constants.csv diff --git a/crypto/src/hash/poseidon/bls12381/t2/mds_matrix.csv b/crypto/src/hash/poseidon/bls12381/t2/mds_matrix.csv deleted file mode 100644 index ae5dd836bc..0000000000 --- a/crypto/src/hash/poseidon/bls12381/t2/mds_matrix.csv +++ /dev/null @@ -1,3 +0,0 @@ -1054aa308cbc55d67054b451780938637384c4516c1efde724244827b4a3f517da7dafc71176d4d0d8a389ad443d87bc,193dd99b435bdca5e54d9628f98cf64b4ef15b4e2932f4aaced0306790038fe2a24a129e873b775603e9bcb62d2eb572,197d34c530289f1ee0c78c2549e7b588c17a92540bc14097515c84ca3a1cc5c11c0f28d8dc5a1c45023c79a753fdfe7f -028eb098a4742d219569a8e6a5aa351492f8f10167c20ecb7d772463dcf7235320bcd6c3f573b4886354bd6dc6d142bd,02acacfd528c4a6581b88f99d319114aa570e09304469f24c922e11d53a233b91ede1db5d56bcf351b0c7273d236dc2a,09739ef37c6cd264756f56cc854698c909e36260dab6ba99c0f9f966e6e620c0f3af8c9d9d39259a4772c0ddb846caf0 -0e04c931c0ca356d46e3ead73b18c96b3d0b18d22ed1f65c6083be79a489c4bd37304725f8ee11007cb37f2eaf1ccee1,17f594a2ad8bf7a10bd730eaf750db78dc067b8d79cdf61d0218a9c2dc72986795f6dab8f3bf4512d3cf419146b31266,09605fc485a538747637ede019ed67c220d6e6773430033ca6ab7d7e2df4587ebce624cf90da513633af537ef47fdffa diff --git a/crypto/src/hash/poseidon/bls12381/t2/round_constants.csv b/crypto/src/hash/poseidon/bls12381/t2/round_constants.csv deleted file mode 100644 index b609c7420a..0000000000 --- a/crypto/src/hash/poseidon/bls12381/t2/round_constants.csv +++ /dev/null @@ -1 +0,0 @@ -154f2457ef0af5d8e80201b00fb2e7651c26e75c552c2bc71f25de7436423909b6431e4c87276409028bdb84cfea977b,17330c2341e58e3255d9f0bbb7d3fee3bb30b80ebebae1f456f6e8b4229c09fffd7052dbc85863015d27a43217f11e3f,05d4688ba8f620e38eeb1f3a1461c89611aadfbff11be1bfa469f461b3c10831d1ca2c237f8a93b0adc90ec14c6df22b,0664a69aa7c2864176cf758dcd71fb255eb5320e93cdabfb32cbcc78d61e913e93ce180bc31041da2930b27c6c91be78,080035cc109d5aab327d8177ed36b620f6e62b598df5c326f87a6bddb9fc301bbfc6f63330037e1a3933b2c68b8d742c,0e46a18007cff8a758623662dfaa6a5907508ca3fdc262b16de7f1021af3471f18073fffa8b62e4d2d65f88d6de82d08,087aa16a5b69dcf5e5dade5483b470ac1fa205c9859c0e7c73fe330cf1f253e3a079470ebd8235cb7945a7f4dd4bda87,0eaa1a2c10012c78d3cfa5449b77b16ad30a2c86b17b95fd7ec0a16041148d3a4404e242dec8ffe1545b0e51ad5aa7ba,0e3b6a0616900c3af65bb6ea9a8b2561b1593ba66d8c1df6dcbb5ff61ddc7a6901e813835804ad93f620f85244186bde,055502e89fb98360ba746aa8f1848539984677f7a92ef1dc69eb9aec78632d2fdffbef65a16e48cb141bfcfeb68b8ff9,1823c8032ea6d122aabb6907adfa011f369b3fe2101022edbc139efbd8dc393c1fd72b4956e3749bd4a05cee0e1bf2e3,16094945fa5310014f8f536673456948fae2965ad3f44cd66680f9c619cb0ab42a46e335ea39f9fd5d0e5434b22d0bae,02c5cce0e00a04d0af53f30c459e60e752f0edee8141702b402d0b04111926d8e8048c9d6d19a4178f5f39d276b6636d,0a0e9eb9c29ab145709137b90d7b784406adcf03be4d5e5cb22168d665c704eae0c59c33d7b39870d7d41ebdae3dba63,14810d3625b41d7d2915009d70f6ff79a360aeaeb4309e3a8c15c31869bfe9281778e98894dfdd3ae3676791fef00d8f,0ac64e116b047832b2d4dde1d59ca7abebd7e165729a9f92da3764c796a3722851a526ff4dd730dbd89aa024d6b90c4f,082810320ae521f905c70d58ab9a9d116025906953aa1ad2b33670e6aee784e97306dff4469aff5030238be5300c4125,06fdddeca6d088b675e48c8edbbe3c668df3486eda775851bfa7d3b30482d9996fb2558589ed56fd81881ef8aa35e463,0d2c62d0f1a1cc18cd3c0b4695da388c5fe4a5f6fa23b7e0f1adad95aa17178b64010ce2ad27952e9de13bdd0f2238f1,05980e4285f89623b4923e10bae8de36eb0790d517a1a13ee2d1e60dd7af4cf77df27202ace93f3cc2d2cd790a359f2f,0651a7a9ef748bab7fb382380615fbec324ec63b8841a762490feae653a22368255845324133fa4d712b908cf23862fe,16aa775e2376ab82ea54406cd3e86b2ae9f3f1940d09419828a47d8792872af6d319f571f8ac52b690d0edb47385adb9,0d2bf7f7d1a3ef3a28f4fd7fb0799b711f0aa0b569e0e39b35f0682041c6bf4313571fc268126803c1cd455926b9f31c,133665f56a0462062926c8758395d53e5f55c8e49b4be0ba504847a9d92da919125eae37ddda7e4f9e54e6030d37f2e6,057d3388086600f182601a3aaf874ad6c9af2e1b6e31fd907735c2a0d2f36d280fe720f7ab68f1619919a59f8034750d,06b8202bd67f29635882c73a2075d20f523f8ca3ef3e5031b749766ac28aadd0e9b42c515f539b6543972950e641c7b7,100f28254f5a526902976d9a0e04aa7c8281ef241b65fa7753609f99e2a65d4ddcf9a6f203f52eca9d8a67eaceab0368,025f8e66fdde4c9b366efb32ac27e8e6929dca3b9221ae63ec3dcc8c1bc0d6fb3faece3d63ff4ae05d93b7d58f2275cd,0e60c2cc4aefb5f9795bbddfa437c39984e7c24c85405c3bd10930fa6cbac400aa3a07de6b3eab999a706e0a7417e7f8,10149037150d9ecb855842788053b978936f376f3faf2688433c69a9bb9a59679e669c510da19b4d7835e9556b128e6c,02e1fe43ed9084dcc74865ae47d59ab7577361ef9d858e183a58081c213a21f36680d051b496ef253ffd90bf93d0b1ef,0b2ffe1b4c9498b46728e35820023a2981f0173ec0c94eb8662a9d93a74ee971a4da683d3e0333e6f0331c299eaf49ae,0365ef61b7d375f28463a1fef5a0fb6ced29471c52245569853fa806b863a0f07ec392377ac459a3554759971da01f80,0828bd548085ad65df11653813e3c7270ab11dc6605071fd1c52905842296b6eac7174c1a9efb80d7829ba6517769acf,01bc698937294df70dc04c1f0c13a191bf2d7f41edbd29a6b6198de6b128066a24663d67e6ad23525d2586602cb0aa2e,18daf786f64570af1e44d387c1819f318e31bd02b1b09eb0eef36e4273bd3346cd12f43ceeb3aa9e6e3fb7104a1acc3a,016fa665700b72fe0d9d4b588f87553cf289e0fc98f3b12aa1429c2c4f5a0f18c46f54186f2b89524e858eec1263cb66,0b67c11fc4788bb2f1517678f71e21e58a0af7cdf9cf821eb36e7fcd7fb6bba9bb57f91bcade43b11865b7147dd4a03f,144aebfa271a685a9e05ba4b98e3fba70493eda456f45c748f3cab0e671caf407e86d1ac129ffe2a7413ce794db30ed2,1247ae67946b588d139e52dcf673c67a41f7be84c2368d6e5e748f9acef42407c7f537abdc0f33d8c4e675f41d38b910,0780000fa29beb94fd07312400d57c40e55fe346c2c68400a1ed5c4c7108859150a7af8f7000d21199cb7fc292c95521,0b2b79069d11eb45b83fd1407ae8e33593844ded02dc62a79117eee92eeae7687ea9ca08eadc8778b24ae6c14582d6ed,099fc7451e0211a1d92b82fbe5b290b370b4c8627cbe9a8ff2e1480d2c520caef7ddc494ca93952d8ba6a57768214b3b,15d38e4a3eb3beec5d21c94b90c98c0c15db24b3910f26286346de7a0a59a262d75436f8b4f45d5759d3bc6284313da7,0df6e591d48b8fe773f1aa940868fede16b46339e656d462a266ab23d75874d55845751942769870ad193ce692d7e122,0733a678e5e6916419c2bd5e8b539ead4187b2af724003182b45de8dcd2e29e888d6af424c8c6ccd520fcc3d19b47c2e,12c6e68e20d7245cf6f95224cc86c1b2eba4ae9a158602d588e73adab495b48b1177798a7eac56179a72dcdb9b948ef4,0e2bf51d224aaff9b09f19e2618dd7a3483af0b1f3593b59fec160c81e51a372f38d3c1fa3668f8e031b30f50ffc6e1a,0d189eafc13611b4d52ed0fece5a1121cc71261c898bfbd0944683ea2fbf93179d6e19fee858c88532a7c0ca0fe48ce0,106ca751523f7746402b0047c4e3c22e6078d5fc846a625d1cdd6f95ab4d75d6f6effd9dfb37508e5a56127e4e5f440e,0e3d7b0b5b18a2d9757a5c537fa1bfc07ebb64e73b3fafac804bdf775030d6b3552d522210538572522021f12204436b,0ca015d9a41111738830bfc8acaec1afbf12a26da30130824d6e30d72934f2bc741324db5308ec300118f6e777d51540,091c3d10ae2deff9699870018f4475adfb1c4bb9a4760487093fef27ac91c691f3062f79593e6758004028144c55cf90,085a8ff1fb23ad3a534df006ce209df11841c0388ead4f874e30ec77c518f57f3fd80c76f91e0d5364d34c0d2c91a226,03b260e0a1428a0afb0880cb00686ea7e7774c419babd0131485b33a19419e9628b2436dc9604a5aa9a48dec14149b37,06e17d6cc8341743f331e8030b5e35acf0f31454d6c46be1834f2db3b2fb5b9d0177d3f437ddbd8d92b60c32fa41ae9f,13458b0422d3a90c0c48c7f8be74c5f6bf43c65eef8a55ed3e4a3150dd14d6e1370a13c63a7bcd9e1c9f5c89d3febb40,009307ba2dea6e150d33935a81b632d36b8a0b45cf638e2c081acf367c4f8316b1c1b5ea9057479f68f17e82c466f954,096d8122739e2ffd19ddbe7c1a7a4584bb7abd8caebc25e476068ee4e757287c4d759c50c62f380add701bb8ab10c20f,19bb476b8629da8924327afd809588d75f7800c6ef6cd7fc8709cee65ab2683ba9cbd2746a20e56af82d89d9b1ed66c5,0c4a08deb88627f5db30d42b5ed83a10180a62cf991258b9bccd142cda9c0168e3b1d5cd848d2a6a7adab709159a5133,0a107ad615c80dc81e38024de9298d60f1dc709cc8b1acecd5e0aa09dc6e48ace7ec3f3327a860ad38f1d8901933849f,146cb92f3254d4fb6ac2f1545e25a295970a0b3d05f8842fcec6f943787b9939e30eb33cf6d754511893a81e94adad7b,0f65ba85a62513668ab9e9ab5b2c5d4f0989d4e2599b5252a60dfc56105987600eb3a64bc10c75b63f3c4f9dae55810f,01efd65006fb91a94748f46dc2297d1e19519a41c75695160e6ca5e71e580486c504907b5161d3d6c61652134f7bcdca,0d43ffd4f33bde32c7630ef4674b07ed12c85aba1229f431cff5d78d68d817b5cc976d69b469ac6cb93177d1486ab199,0f707eaddc3ff387b1457fb20531d5fffe1a25875d3c5e4edbda1343b9e5758ae3114209b14f55d2abfd23a73aa6333b,05db174b4fba7e68d28a642eac6e4909cb8dfb658e4408147dfb70c09243fe5c32129792fd6cd336f1824524a8e37999,0e8334f7b12ba167b0782d1dd7dfa28d51cc83dda20e2bd44548d0c2fc4940689cda542ad609906c4e295d8a1e9f64f6,0e569b7f4fda9c27239da626a37aae1f1f393940f7ffa0238f07e367d0b040cbb3bfd06f86261adde52d52f142711232,0d917e17cbe2994a9f48c754dfcb43824dfc196c4eeaad1ec2c902978d6670b895ca25016db9a720f450fb055b150a70,0211d95a855cc02131117c08419c8a8ea906a00cbc8fdb79c755acd6ec0aa4bd450fca6953fd4b6b0b0009d982c85b5e,026e137be92785d890a8d8d8a4bde4cfa07f29e2b6b148be5f8014ed13034a60801bb003a36971467c9bf91e7a97142e,190d39ddb4050295544700a76b23d3f109c092a80a804e13b66ea795db6fc156986d556f206026a49344e0e5951d98d7,0f1d859aba9fa151727cac9a8567ee9c032c2d8ccf5d8f2724464828b1f673212809e9fa096b2ecdf95b2b2e4fa38a75,091c654a512b8ea08bbab7b6b9fdd78a49b19423345a1465528a0b584e85d23c765bb9f3f1985e6f89ac3009ebf3a99e,0f57fec764855ea7d1624c91f16c4d099166506f304a79e1e0c40bfc57f41c8f361ab15c74ef76eac14edd5c6d5bbceb,0acb488fd42ad53b12c9703361fa00eef33606e804e7a91a2d6c96a5774df2a6bf94aab5973c3aa0b88221aa1a0ecb8f,0a9ba0419b6cc23aeafe149f6d737ddd8aa00e75834dfc1e73901bb9ae25010d85977dcdbf417f82396aa061273f3a5f,016b0bf68515efd0574f287d03010c8fcf8d523102e0654ee51c4132d750ef64ca1b1093503e1c671f382fc1b140a6be,0819566899cba98f5f43cb9dafb062e635ec4193abfcc1aaa8aa34d53efb392b48cca39665d4960e2ca61b8e0df68a42,04c13c6ada21527399abc94e4fdbdb8ffb14b5bb3f774880ff33cb38aa8981319bbfbf6e4941ae7504ab7aeabb0bac34,180ded2033afdbd8f88963e1d1ad4b640875e17a7e720a056a0e61ea1a6f5990cfdd9e8572934743a6cc8e999e784985,091896bbb40a69fc15c9d75556fa08c59450fedcd480cc9e8def435dfb68041ce38b5a800da26e82e67573283fcca177,03f59112fb3048a6a468feb79664872b58f898d9ad36c922858111f42e204aec44997f5a6da233fec053f33b45f4c835,195a78a6b417d24382b40185a29901b956d54c95c9764097420e66e6daa4eff3fba9155c85fea9479611e322f49c9522,06648f2749a55fefb4492e8a4453bea878c65f740665d01084a62deb11bcbdac48e357c9a6f68076cce028faf6865146,07b49d61c947fdcdc7961f981adbcb55924d3e7b68214068b87da133060e7f95ea00e99d70036cd4bbd6749c6932169f,0a3881cd9dd7d8f1163ac6fa761f75df4e0847c9f5da5197dbd3336a995a1dce7106d0b1434a356e248ccd2f105d1a4f,0af02e7b4ba459116765fddd8f761db3cd909446c5afa49e46d4a4bb83ad181a99569e418a5ceccaf00ad29f2d445135,19b5191fdcd2b9d9a7d2fffc1c4187f14b70d6838c0dad3c9b2da8415c19ec97ec3798eb25f32a7226003d8333a422ae,0b388939fcca198ec39c0bd0288b768816f842abc6985e2af35b43bf0db1659b699942873c843072f6d0fdac5b83117e,162dfb8b05e1bdaf3cde4f7cc80dbafa48489c290bc09ec187f98f7cddf220c85ab0885d7cccbfcbae38000c3c870e00,00c2ac6722de06149432bb0c2e5012f1831ea313fa29ed5c93519e8506a1ed32adce2b4a85f07474781b5bbe802a3898,189f182cb112caece3612aef52bde9aba388f7bc4bf4de158ef6540c3923b01eb64671f60cfb8396a56b63c78ee1b081,0434613a56a2a3546baf0459bf1b5cea2edf822bfc5c89ae7cbdb317d5f8d38eeea9dce5c65c49b38aff83206ffe35d5,0dc34d1d79b41e0df0005ff90665357a7314e72b8c89e6c67cd98ac1fd01cd1c0bb57aadeaf2b77b75fdf92f9530caa5,14f38ec06a49dd2a850cf5cc0378f087001ffeb12f12ce075d3d2c7d84a77fc115e7c218c110cdf25d45593b6cdec378,001e794021eb02572e0ace832ea4c863038433ddf439a3d26d2d39bae07e80c84652e23a652858a647698a43737cc253,0cc12818fcec8de2ff6688b8eed64a4e03e3b0d3d1ed5da397ae9cd398329c17579ac9cfe9301e2c119dbc52c286e5b8,08882c9c79b23d992c50156933658218e44a7f27be637947600cf6d132212dacae30c2f42902fbf4e04f21321a10d4bf,0dba2c8dd75397e5094d6cabdf0f430428f0ca20c05a0ae19438fb0cdb3d7475ca278a64262631a2bc2dbf35ffcdf216,1803e2c96c87d7c0fe49bdfc811b32ae4c9ebfcd7d2f02542a7185197c2c3e0bb0744f76bcfea488499654d6a1aa1afa,0eb92031745ae4b6b1e096a9f2a142c40f4fe5995a574d968a5adf8c66bd987297ea7d2e2129aaaf8756ec9ce364e3b3,128bac41ff7a71d26308e5190ed5ea80e80b64d994a7e7c57037007ba178a9983e9d3fdd5e5d59e9abdf1e524187ac6a,00bdf9f6a53be1828b46689987f20baf7a2757853dd80b9da616c749c35e54fe683debd0df062dfbe9c2f1d68365f91c,08a70deb1baad04bf134642ff43baed6b5a0c86e6cec7d4e5f969b85beed1e93bb8d0266c4b23463b7ef72bc26375dca,18fd4a63c62018804a2375a24c4f381ce3f94abf2ebf7340da75f759c78ae91646dbe79bcf8409ee537205765f585582,0398cbf405c66b09bc3bb2df8809a82ad432b66d544ef466fa04d6f2dcc48f12571b1fab34a0286699d923ecbf65676f,15bf34bd747a732510bc10cf2bdd98804e3e5a30bd6884d1f3195dc4398aa0f7fc35e777b20ab8219ec11b4c77f4eddd,0b5f02b361dec84aa3dd7240e410d8b4b2c0768ab9335f71f7f3fedc3e08a9c27c35aa4cf7416af83ef5e2c5f03f4547,1344510bddd17c7b873df49848d445bc8dcf49b03be49dbdc68495f79388b33f521c6f48f137762be2eca8f5dfc7151a,01525dcbfbb85405e0af83b391560fd504652d369ebafd64f1ed4a44e8ff7c4260b561d58a180b5c127faeed06ca48e5,0c2d15aa7b437da7472473a34620850d346a4e52257db90acd823499eeb7008016e63e0f51752f14b336e50dbd9dfee1,0855aad17f483c75a542bbea0e8a9273fe815d1ba3d20b3706dfb4c894d0c7c0c78813dc20f1988bbd2bf1273919f7f2,0406f4ac57671b676e9c2119aad5cc4310b1a07ffb82b11ac494d51c09e41e501547deb1a3f1daf4f51af237ba337c4c,12eb56e33d4f24c74682853c235445de6d2b8a67d8cb9bfd1231ddfc9c1ad30bf09bac2ca348a1b88b997acab6c18404,18dc440193684f025c3633850bf0193f484da7d6d7d967094b51202dff171e18d868e1ac94421e722c166a82c7d62d41,188dedb851df0d743e693a2cc09b2fc3786ca2def266b49b4c95dc266209c47a59121d20afe2fecd6edf3be9cc164028,0579a1e5aaae3f6e02ac1deb913ca82477cbde72b7e6d58b7cc6a91a9319bc9834d550126d18931884ac769c7d821b42,14a9d80e9010b4676e3ced9da437370385daa0874e4636ea63983e766fe96c1a062a715ce03064319c1fb5e2f37d48b4,19f24af0d7dfab02a118e0c873422626c76fd70370eb41af469e0e82b78bcd5255c814f2e7c320418ea8cc55183c51e9,19cef3bcc7428c678d54d3083e6b57e897c273b4472a5301d2498fde11f948f6d9c9f17c8feb4836cdec0a897222719e,0dd4cb77abccccf182405a74ade52dc9efcce0e170e720d510ffd10239621a4502850e3bef113a81bb2252bfb5652268,06d9f3e1dab3438a43e493a7b665b81186bbbd733ab812cf10c563d6b07dbfc62efe2f3c3ffddebc31ec54e5406493d1,13e1fff7de97f3b1df8217108215b8eb41afd06c0f8763025a7ce928afcab6c965a0ebb47a4d572c333f5b4487efa39c,12d303e32afc0b1a430b36e79e1d2ce1a4b41b65e9614b5c73464c77ab705d8162ef31efb15c2363be099c8d8a6938f4,02f89adefb33ef694bf89c794977155c1248662d9493c1bdaef8929e2d9ce97af464ef782a1c55d21f8a419efc9d9bcf diff --git a/crypto/src/hash/poseidon/bls12381/t3/mds_matrix.csv b/crypto/src/hash/poseidon/bls12381/t3/mds_matrix.csv deleted file mode 100644 index ae5dd836bc..0000000000 --- a/crypto/src/hash/poseidon/bls12381/t3/mds_matrix.csv +++ /dev/null @@ -1,3 +0,0 @@ -1054aa308cbc55d67054b451780938637384c4516c1efde724244827b4a3f517da7dafc71176d4d0d8a389ad443d87bc,193dd99b435bdca5e54d9628f98cf64b4ef15b4e2932f4aaced0306790038fe2a24a129e873b775603e9bcb62d2eb572,197d34c530289f1ee0c78c2549e7b588c17a92540bc14097515c84ca3a1cc5c11c0f28d8dc5a1c45023c79a753fdfe7f -028eb098a4742d219569a8e6a5aa351492f8f10167c20ecb7d772463dcf7235320bcd6c3f573b4886354bd6dc6d142bd,02acacfd528c4a6581b88f99d319114aa570e09304469f24c922e11d53a233b91ede1db5d56bcf351b0c7273d236dc2a,09739ef37c6cd264756f56cc854698c909e36260dab6ba99c0f9f966e6e620c0f3af8c9d9d39259a4772c0ddb846caf0 -0e04c931c0ca356d46e3ead73b18c96b3d0b18d22ed1f65c6083be79a489c4bd37304725f8ee11007cb37f2eaf1ccee1,17f594a2ad8bf7a10bd730eaf750db78dc067b8d79cdf61d0218a9c2dc72986795f6dab8f3bf4512d3cf419146b31266,09605fc485a538747637ede019ed67c220d6e6773430033ca6ab7d7e2df4587ebce624cf90da513633af537ef47fdffa diff --git a/crypto/src/hash/poseidon/bls12381/t3/round_constants.csv b/crypto/src/hash/poseidon/bls12381/t3/round_constants.csv deleted file mode 100644 index 3349235495..0000000000 --- a/crypto/src/hash/poseidon/bls12381/t3/round_constants.csv +++ /dev/null @@ -1 +0,0 @@ -05ce359a7f1c1482b1f51709cadd64f5a26af15a23f514ddd3f99c1cb19d70b5e3c89c25e6c66c03b24fb5cf098171dd,0cbe7632efbacf28e284698e45f9564427171e953365ce0cea873a09cd5858899109272afc22d0f71632040fef6066e5,06ec4522f086f379aedd419486800c2331968b246ea4fe3c505621f4e8bc4e43126c217b178a2ce037c5d3c62c83cfc1,044d247198740ea72421cdfc31a8cc2b75bb91103d8c8c962bc21374299a2971bc8cc83a8047fbd8ef7f5e9d09a258ff,0420e80c9b9e0bf1732967ee0fd10235ce6095f420b91ec9e05331a563290ed9bade74ad84576e86436f63bb8188327a,0ab070a0f6fb9da6ecb8fc33528002a8460a0ece92b8e43849ebfc6bd1066283f6a3c2b01eef74c36df368a1bff4425a,0acaf97590428902e3c0b935ef0f502b62c7813e1abad1f70bbe0418ee59d97d0038450669d0b8f990d9dbb890bab9d6,02f9455e08ec7eb0a6e298a8d7920dffa93b7c70456e620af26bd2fc3f73f78e7e0d43e94e38a3719d312f10a30ca727,0380838a13c281081d6eb090e6a22e81a80b6ea6999e9cee02b47c466eed788da3f98ed9e4bc766aa8a74510ca933267,0ff56d99a2ac8b53fcb7a73a3b6c3027b3aded2ca607a84d9b21762890a60c0506b3e9674644207aae65dafe97831db9,142c240946c27c075ca9c8185658a20b62b50af30464654b940171e5dc3e80ef890899261fb806fe20eeeaae0295f3de,025afb38bbad1053670447d397a1bcc6f6206eb88b57a992673ef3505523575840119427396ab82fe2bae7188efd12e5,15dcad4ebb0f84e3ddb75c5b26b7777b64c80a00ceedaf69b8aabcea714c4050e589bc68ff17a334b025ae7ff7b565ae,19e9ced254a572f210a23d4bdc969893e21e66dfcea0295a3a9e4932e6aaf10e687d85af3fd1f8018f8f6ee3377398cf,140335a3c89cfe2a895eb0a1f0976f0084d4e7f9742d29359efa35dab5d53b9ca846d09b8ce14744e62456d98fa2b255,03a27061337bf8a6137a3c431114fe68fd10dd67a94c185f227cf3c83169c93a96cc6c2f68e9cedfffc1f7e9b4153ce5,0e9157746dfc2fa80982e8e0a532d96f107780b5531113f9a3bff986bb056d6537ef35ee58260b204df0309cfb09f725,18456e050b97e7bf6e4e5faba129cd0e219148f04a6bf424a98b8526760171217193cfb79cfbbfd0787275ce7f494c25,0f47ea2a57120fec3ba8ff68de76eb961c75678d90b3e167556c0ff90fc924fcf1954c2e6e825deeb7b44634e26e2a5a,0529c46e1812c17588bdf6bb2808723ab13f044ac3611c4904815f4deca65d5de22e58bf27d0a2973df3f9a24d6d7f04,06f7d85299cef8bf7cc0b8400cfaa53da9e2441b688b617953226e484b5b51aa68ecc953a422dbee26bb5628b2dd22a6,02c98c03b1f86a41b940307f49294856fcbeb8adcd203f346846c3ccc804ba9b53a9baf8c7c1139cfe836146f91fde5d,1436ec6bd04bfac09b195dc71c6ec105b7430586d6959ddb2b484f6769b27095651bc56c296cbc7e0b66dec3b347e37b,18714cf905f3f453ddeee24d7458d234ac2db60c25eb32604a7635a373f83d44b10001ef8d554d7ef40db37a68afb6c3,090b7696b7e0fa8d23841f04274971fecb81b57f56bd169f9b0becaec57013dae47948d45ce2800029de77e029dabccb,04cdb2625703e7155cca895075ef92ed738e1c7dd162c91da5584ae970a575a1eb377a4dea47c0bf72f3b2ab0ac4834c,1144cfac1dc4c2d16681d3459db3eb289fe3248677b519b429e813857c6e807e9f7ee2c55fbf66667950f2b78448e21a,09568d718e369742310b1a23e14a5a162c71c0e32e3eaacf6e2e12aa5cef9530cd28fd760ff4d313d44d7afc40a3c7f1,0688822f3bfd78ad70d0e84609e56f5058f8660a3411e9fa73c5d6b8c82dc25cedb79bc1bbe7a68a17c95aa67974e018,15e7b1b72818c77a9ca26eaac3fedf48b57948f8628b1a9516c6d7ae34e61799021ca4891a4a5797c23a2a97b1b3e100,0137553bf56f0b217a6a5ccd16abdc96f1ace6fc82c2fbde179ca3c13f4f633f7f6bb54581ccc5159228950d9d00a781,1781f5f50a747bb7b48c50ff7476a4bf32cb65457ea0af6cc0ef3773dc610acea687048b5d55e7842471e2058331c0a8,03f4c4558ceecffce8f234cf001ff89dca987ea20833751b4ed3753f5f0004c7eb3d1b3cc9982c009a1e47a69060ccc6,18a8dbbd87cb89704250d42245e586055fd978ea444e70fcc9101f341dbdcd88ead49b151225840f2ca3e7e1feba4b4d,0724e331e71ca7b17b4e7d633349acd5d285a582a52013092692d5b3e48395441f2483207b032edcf89b42a419eeb1ac,16adb125d97e06622b91f650a7a7183878e7dfab2a2a123186e5cbb41f07b1f9c8eff6b15491f7db7a910fd8d0cfaf0c,12d84fe1d5fca468d5816a0f2008346252aa2f6cee3eaf5d030a321fa5219889e5ea5a5072a231cd0cba25a7ce6ac48a,098a829e2f358afc1034ede5157c3056acbfbdfb60638335d2fda25c3f26c5341a704cf142579bb1b213595dd59d4428,16489677f6a6bbfba47ac7d04a88d6236e337fac0a73e9cf2d1e5dfc6eae10a91406b157c0c71b6bd1cdd551b9ea800c,07a424aae1b55e5b5f8e3f9a1db819fa5e266e391c7eb3a6c68dad0dea7fda692f075ebeea39c1022c4c113ff7cd14fe,1331a382d571cca0e791eabb10370f5981efbef3db48115cfcd9337bf8eb1ea77851dd705972d4428fcdb80adc3ef993,168a3aece4331b8ac5ba9eb50ff558f94633f2e027dde1f6d4e28bbd5a5e336ba5224c8f2a03ccfe5ed155911e9509e9,01e24ea996889992c1f60510e602c2346a216df585319e01561ab40eadcb83f9dded6138cccfb50bfec6618f6e0678aa,0a913b3c9398f07d7ae4ebfd371be9529874c239ac28a9526b2fcb7aaa5a806af217f239a444c7fd7afc80bcd4125db4,18d58a9abd4588144055b222f75b19a4d04d144056d31a66035e7e039b92dfd38b01dc29a1124481e5a9645ba20eecbb,14a998b9fc42b4c16d84ac55754c45884502854ac8c28fd57d652fab0a9e19316a164d6f741272dfcd77c146cd8b999e,0ef45e044f060a44e83391157a17e0272dcc4b6ba4bcb844b0a554229d18faa4f2dd9526356acb2d6480de3df6e41d20,047ff51bc66d5bdbe62c6d0dbc0b605d8de234acff2976df33d05e66b3d922694caddb59fe72f21a24652853ef46dd00,011bc23a609888f723178e35d7a13b2f21950ee6213890d5f40a6b8926de9b835dd9acf73a8b1d1c3669862fd8c7c590,01dce326b98812b2855e819c34a7c4f16ba9db156e3a83e513cd61556a3ce608e9146abd2a228b104be6c2c6bfb90bd2,1150ef5003c9a54b1b128d7725eba4d7b1436526f834eb073eba8fccd845e107b3c996746443f93165ebf0ec4a8222e1,0cf349aec12bbc5268063e99754741e2d344f31bd0e943893b19efa98fa289674d1d7ac5cdf9626515c264e84b9ddde2,027822348cbff16ec1e397418d3ba95b0204dcd7b22696785581f4b6fbc9ae693133ccbcd022b2499408296559d76d72,1098a88df50b338300498ad27ab9815e63522af879eceda54cedcaad069cc0c206db7d18acb7da340e279569077ae1bf,0540cbf7b242a46c8b2133c287697bc2c67c13699ec4949a789cfa883d0d2c3e792f5757bdc079473ced142f5e158d35,0b2e85a5c568235613b8a3d0f454b6a3371b535aa3a470887c32bb0dcbac7aa8287c625c267da78bf44e54ffedee67ec,0976b0e5e3c2707762844ce0cc4ceb7efbcb7f5d7dbce76b99bb9ea5c4f181ec5cc4b5fb9a0ff4725e775bb6ce2b92ed,0af4715edc994b4c751e1620a7263e0d459addabd7a71f54180abd713a727e507b2653d77c6d8249fb8bdc4e15a2d3c8,0b750e94e19e26998795fb67a4984bb98ddf8f4016eb65f7b1b7856301ef05d030c5e247262241bf96e0e985907a9477,16472b1e2c7b5919d87a39831ef730eacb015b97bfc0e3b7752f4b56c53504187f6b8705a3180b0868d82571260eadf0,037e1a36cedbfc2a72baeee7e012b7176647c23c0422bdf00fcb0cca4f3f8c1af95f65513cc330371b291cf6b2f2bb9c,0729d0cf85334fca0a6714b9a38b34c4969ce5247c6bffde1637cb7d922dd07c16ff430deb7b8de025f39845f74eb64a,030b67b9c66743215b1e15caa3750694de366b6663296fd307d142da9b9027a2e4b334503a26dac8e9173e24f79cbb48,01189e869a4fbabfb8daa5fc56a6da604e775307e22ce093abe44966986e2a0241f35c23114cc44a961b8bd6a1deb32e,049793cdd584aec84b560124989879f6157629a1c717c4873bf45130c6889c7deb77ac9495ec69664a487efed216e3e6,0e38794bed871a15c84833c3e5d42b6d82d70c15da0101c2e7ede9fddc7eae9d7bc979706d07e72aea52005d3226f7ba,10ddf61a15f49cd2b8046cf887da2b6fcac37be7c082c967a8f2fec04b84fa6ba86c8b561ae281b6ed638b445b408df4,09aebc19964ee60b7aefb2c251a4a094196f7034dd9d0ed187da0533a7e1f1ab97b28307346e35072f69b67350f0ee1a,0f320be802ad88035a89faf94d270437014388134a75436d8b3af5e25c5d12d1cdd0edcb8aea0712857246bf834a4502,105cd93cb9fd0be6bcbdc07d961d2cdb349ef76b1e16a9d44d7bfadcf05b47f4d31d7d15bf666d8a6a8087b0b2ab29cc,0df20a9ae9e01da18a21b35680972b90910a3e304db3db50f8cf83d4d6922afa2e55682c1dc3be3a860f0200d4b4cf4e,04e24a87315876e95619ac8322319953b7f84323bad52509ffe05eb4a5667b06a166f18b365a8f4cdfd858674c12c40a,0b8875acdb187fa263f2dac90dea8dff052a325450a4367c219cd731a577b92428107a53f5bc4a2b154624556bb5949f,0b4635d1a1cd2f1b96d32bdb651f15dcbce50502464f7a3c7311a20822a909c46ae3a3ec92a31ea13d68a9f973681bdb,0f19b257050abf57d718f651451eea67f97a84f05272411cc2afc97410c12d0989cafa47937b4153c6129e1564312e57,077a54e3d0e43947a52707ab3221dffc0de858cd5413b5c8f750ce02bd79ee754b32f40bd88f9bd62e9a13fe79b1d5b4,184664091abd54bf15ffb03364be946daf37e354a2a9962570f79b64d8670470b84240761e558f343ea7649254cbd013,145d1651d59327f204514a21a711183710f1686f1a990ce2d44b4a65ed7a3285985cbc6de91a56b691612e5e2ee02a57,0007d55524a36b12d267e95a962fde3bd5ba34314fa37810ded7a8626cf98672d7bd4b48f3218ce5e86edf0289088ea3,10b0097cfef9ce88b164db30ac2ac77f597c9fc1822cdae7014a6789a4716e25f595c2ed0516fcadd4ef20e29bdf31c6,066ad87f0efb7c7a307bba84459d5316a11920642c42d992b592c3d517d29095a2cd07600f676d750cbbc4b04edfbc16,00fb9b5f9e70c20e78a020d555465fb232e696dd9f38d82d968f164bd2d98d092dc1d5320dfa9a5b3d490066c85dba45,024953f8badbf9211686c44a673d69f85f6158ac34eca9ce2dba1455d837a2de9642d36d8a2f8854a0b364aa3bd79d01,18e11bcc334b279a3ce4ba62a7f2980a7064fdc67f77429412440ca13c0ff0312f5a08eac1310dce3a42ab1c73833533,04e5a1e6d15868d4e4f0c6c442e9c52d9fbf96f565242b66d8e66e152d3607566259ba63ede6d6fa8d6973e022835d27,147bd4798b157ed4d3e736743faa1d103ceb6989447e9248d452f91b2043de17160383ea97c135009c47cc15624d671a,118befecbd3b48fb946cc1a4dfc8f52929b98f395676095daf690ef9386a6fdefe3c8a4aa3f8eaa9e7585f00aeebe697,09a73b5eb2ace9b5f62b8affe9a79143bb265d07b95142263a5fc980c1146d235f6a89f8af94911741d891e4cd721d27,171c08e7e2709c99f886df082e1e0cd4118719239dbbdb18d228dc988eb85926b2c6adb9472e7122aed118ef5135dfd0,09f272c7d746ef7f598dbdd118b9b06d5361d4dcfa8086f928018e531219571a370843bd577c634cf5de6faebcd3126e,0d128a38bc3d48a31e11195332b8c5a8a204e3e59034b9681a4c3d9514f27af90e92004b37c530101298f34a6466bffb,18382300aee2678a4519f4b17a7092a7f76b4b2af42b13523245579b0aa20e0653efe994864e38412e82fb487a663ebb,0d68d7d180ddf70c7fa2469f4e7851c493d48af1ffedc7f4a6d4a02b63ce2949fc878ea9758f74943b101e9e794d5448,078c2d00f1bbbabf2b58e4d8a49709cc5996d0a176789f9f92754706950facb57c0cff1dd784d2ce887d9f2e3262114b,0909ccd7e72c7952bd434a11d4246535cb3aae2fadb6cff08afed4a32d973bdb32e418ba754861de12f7c3636792522e,0b6ec5064386034726d53e63d3a1d9bff848d4e008c265205e2815af9e2ed50deda6fe04f64c388f94876eb7fd3445c9,185f4aa3b9050e21671ca2960f025ec50d4bc1fe8d3ad4f569deb98fe99c5cd3319cc066d9093674428126c6750a6cec,00e7e3595109c6563e01fd54867800943b3e38152e6d0a32115d3422e5d80ba612a5fe3d800a273531d6274578516c13,166530b6115e4d43c758ee5a687e39c358426f8159a41e92e748b80a2d8cd9c0d351b05dd98f84122f4ef20d94171749,07eb14319e034abda996289dea0c891d64cc3fdde9e9e2b6676d2fe96f00ffba9668c5d856ef07530567fb3aef17aaa5,0b95d8d4de0bbdad8b7f458f9da28b830910c5624e29aa83460ffc8498f7830d0b255e5e1525d5564bed8ae17d583afd,03a47d2d2ec8d2fa7837147495c4a6143be8e58f91c91e6412748a32dd1a33bda48b5c9ed69216c57effa187b0c8aa9f,077f7bc717b2560b8773d5c3fc03525a01a941ff72a929b286e317b7495de2ce43d653d40ce31e994b080cd8183a0515,0be1b0962fe93fe7946bf1df923fd12cf9cdc7b07fa99d0553ffbe1ce32b9de9144ff6b4cc7c62a3d64679ec32dc9f0c,01dd6422670a60d2e916294d5dea985d46ea96a63b27737121410e322e1847d4e7b9e3dd6aa8e04ee245d7e1a7f13241,0f21590653671d78043e9c6d372c41865f679415b366554879241718bc95a2c7a2e04b1b61c0e9c112985c6e10f4fdf1,0422fd1be4147d4ec040770fd1aa4e70ba92943e10659272d771e4fb37d9536e1dc3996cdb45bc4fa523d34b58ef08d2,00b1467dbb2645c2ef8e16e8b5cbd179b1580b475f1d32bfd400a4e99f23c172c5676cfc8e4f9adad1232f489a340c1b,08ec6ea940732f09b5e8de3a6fd9958fddef903d8a3ff2dece0d264bc1ea5ec6472e17951eab3103023ab50b4f52194a,160aa6c14a31c79c1b1915ff1addebe23dfcdacbd95117bce04ed9d99dea33e9de2d9e97c1cc1a5f1fb91ffd5aa0023c,164a82fd9ed676818d029d5fe2596d384b85f3fa5086f23007dcd0f023b16dcf2c0b3ae43e32f1d91334363c2629ec96,0f0efea1d3bffbe1cc4ea82b15f41b2f28eb7b53e7ce7e944b456f4fe1669a2c938c7eec5d7ceeaa53c03dcd81ce767c,149354064804e1cea886906f2b3fe96ee00e0fc0c2603f82a56649a9c864b3c41c81b0f2c49a07f5a3c852e447b9c2fb,10d1564cb621de7d0604671236207c9ac39cbb193f9a725d22cb4c963968aa00d8174bdab906e03a1d5740be06f0fb89,128a6d9c3bc0f94309faefed95bb0d6b52b0ca9f9bce23eecb6774a732df16aca5e670d9d7e565c3cb152cbff1606e83,094776371d549361ec26a30cbf335bcce648ea666b2ac471608be1d1beaca9f07c0839cbb7aa8a00a24c5259f76d610b,0fff2c4159ba172de6f30cf0de74a485a38a0cc96a62e11a26fc82d99d401247ee8e1e3c7e907c633d28589f35231861,057efd21c274e1a15400b8f10a12948a3a8bf9a65a23446774ece8e8c8c7618e8032e4ea03ad3fa333ce1405b8394478,0e04cfe3451c08b9dc5238d2bf6c762711ccc09b40eb02a875f04882af5f4e48a7ba43cd54b22c5a740cf5e902fe5364,19a96116dde9c7eb3a84604bb5bd793e469c4415792a968f40d92bf41c7b477a8f3c9629054f150acad0cd65148daca5,0bd81f5e316b869464fe73c0a45202be3ba6e1b4254c7679c6789b4a0dc25c84dbbf3b05db21923fd83109479f888a23,00c464080153a13a83bea0c2a695a3c6467e2a03e2c0cc0bf16bd959db9804b2e42ccc517bce1c157771aa780161e7ac,178c7d0319260818f645a76b4815fcaf51f6531bdccd2ba552b68b30d79cbab01b52a0613f21972fecd9c4785c011c48,011393ba528a7067b330eaa8207770fc08bd34ffd26a112f74000f33b2e006d870407df2a625fc36be7e23a658a105d0,136877999cdcb25ae6d76b70b66557d5bbc74fe30668dec3cceb6dcb5b70ca9536baa618319e917c082dbf073fe35021,185ba3019fb2636c7beedf173badc49671b4b4e65b8baf141d16a49c74644050623e3fd2749a64a2721fc216284288c9,0c615df42da5777100ac4ab6075e634177426a7a2e406def9a879c63a170bbb84228df9e54ee4925f56bd40a20c5def6,0c47d568e12d8bbe0b34017d377ad40a89e2718a41176c0f1a99a6b6519c3a1780ea0c3574aba2b10547866f39c72c40,03c69a7ccbc473c3d2b8e35733c2567b04528c05a71ef19096e2e7cc9084b10f3aade165ef193232f4d1ac785bfdcddd,163f55581d9f16a5207bb93fb636877a260ff5c45acca10c0fb7a0f61c977a8637ffdc404e3630ea8475bcf8ddee3e3d,0d28e185da6408d9f3dcc88854bed1543de7d0dd3089a835b93a79b022dc9b2f51a97b3b0611d8a0bcb6236b0015567a,00303e9dcbb98689b6e887d6845a5fcefcb5adc23e5407a877bdd41a365e2e81030479e5ab1bf47203c279745539d5e7,07cac622a80947ec9ce438dd8436ded7f877bdd395e6ee37ed0fa680e6c72fc36bfece086fd67d5b30707e760b7e15c0,16e80d030d026fd9f22bb0d590386ee5cd0694d0bb359c646006d60a6ed27f1b2a164f669a3fc92d7b5b160216baa5fd,167b0dede70310c5c65cd2974413dabf46359d51b9d5ed5ed30d4ef96b4618a0b36cb972cd4711854549e87e8ad9da68,08c0723287967b2a67079556ca108d9b9eba5799509f4f1a4141d272487399bc25d0c8ba3cffedc223f68a89e7405cce,164e1e59ad8df72a45c656818e54571e710755f0501d7a07842df612e959228e0fb0343f6c41eefd290c14530e51902e,01d21d2438240516072aef2dc5a41f8e0a8fcf10d8a5e99c5b73983a9c7ed8e6b8b4268c9f083713d73b8d2bd08a55c4,07f18232bc05c1e17761f47e32ddf4aa7c1668f4450477e96620f5fd9466a022b670d778da0fff772501d2f94646c1bb,051d52281e61ce46473de7d0b81c0730a65201aab9162a37f8828b693c7ae9b68cc0a3331810792b6be004852bcbbf3d,15d888836f26821caecc48881cead8ecaf161772cbccaa7660f38a73abe81c3f1090522a8948d42237883904bfce2841,119f6aa29426a19848984997573acbf1cded87be680ecce8ccfc39e7e58bad54942151f00a3370c64e5a7bfcd79a7585,00ee43e73d460496b140ddea7c269d9e5f9c7667c097bf294cc12029e48adad5a951f3db1ec99f5536a825fb8e4ae18a,1744de6f243aad17f7956e73f10f05e311a5073ddb7e086d795a09d648d9967d1ac149edb7d9b894f2b809ce281597f8,0964c43fde732ba6d2e7e504c30d1e5bca4b7fae88154c7714b500cc9ec2515c79d18c2f1841b5679ce4024b1dfb8120,0a0e278a3a14f3a4abf74a50df67d94259997461a3f04456bec24bcf8f5e766eedf4c5cec98bb3e5c8aae70d677f915b,0a519f934edf38e363f63d5cb7d7ef42d79b0c40bedf5ee476ba59c2982a448f4ff5b2e85155344ce537c4068fe20eae,0d7e48d6a4d0b760fd348b80522bc44099ffd4c113c9dfae7dd1965254fd6349cfe367a13a03e9c59b845a6a182be3f9,14a92ff29e69681828ab2349f07b282efeaf9041617ef528446da683632f543aef7c1adf6ef3a86762ef5207f2167566,0b402bda56cfbacc5df30977f2abe5f2c124707517a87e1be1581daced1dc76656dc7dd7c1444f30d6d231152304f458,0859044cfcbd98680091b4273c8bad57680ad0d8ec94d15d47e56b3a4d0556fbc1e9c202528e3d1eb381073154c75466,0d936bb51d78851f14c7f59aa40c1393513739175654a237289487f0c7769a920cf95d7f7b7f33f01db8c9c5a7152808,083eefdf26479e4ab22eb29f63c8e7a0396f8d68cf2490727869fcfef4a28c0cc60a10af6f3ebe94e36b3f27c9445f15,192fa334e28c89f0497f0efe3852710679846219f31350aab9878a9564fab8ec6b0f80e7059185eb1e21c95fde7acc0d,1284b780a63069963fa52dcad439221b7a989275191e5ccd51ac6f7cdd5afea693d8b80d667ba1faa0ff153ff8945907,120a4ceca1b79d90bc87937ce162bf5afb526c933af83c002897bb714ec76e104d50bf5f075435fe9e4d1df7768ec976,14f969981137edcae8c51ec84fecd9854d38b2d7ab36b90d9bfd2b1939f0d8e1711189b7bc0a0786d3a1d7f5a82808c1,1574e558dd57b7bb88e8947b2873e9a0b1e2ac91ffa36a43c2a7ad447a473d090e6c31562916ec75f9ae6e7f23e1d183,055bbd9217e7c4219eb23ef4f41ea00988c536238747574ff6130c852f8abfec73af0429dcea5cd9a3309179186f829f,00c9bdf50f0d1d4aad99c034a62221ae790c9c3d8d9e6393accf2dbf7191561e8fa8a03d0eba83b1fe84d4e72183215b,0748f478ef452544d34388175c0e37969515bd3c2f198028aa4d3a6b294f5cb11e5d6c4442379475eed8a44eba5ce779,1891fd758f4122852f4856e9c4177570fe8205218402aaf5398ff255852b8438af1d07d8d5f0472a4827291ff32ca9d0,10a71032a784fc7e89734b3f66f5bb640c5377cfa762b8d71a418739f2a6f7e6dbf750f2177138f099bbf866dd2527d5,04c19b663c45ff653d582e6694c1332b4feada37b28910714a834f2f6c2bc6ac238d0a80e18905e9c5e8a069cb79e202,01addecfb6ae315271349cbc54eec9788caea8e42b25ab5f5a9dc3e7001fc01184be5daac997fea2757cfe618d30a766,17e29412f7863adf73ed5e02424d84f6d8b50e2e3e4cbc67d82c40fec6cdcaa1f4e6dcab2b7e353f672b99e1a066587c,06a4526cbd242edfb340bbba5ca7c834343237d9a3558f7d8d7a5aa515f9ff209239178f9c7dc5dcf487300ec3a2ae0c,0029bc8185dbd1843263e0f4eba5c30b6faec72895f62d2f8365ab446fe001b4f826bfa1640ae82029e0563f0eda59b8,0ee70e93a57b6644d75fe29187c2599beb930f9ca9c69d6474f04d0a1fcda71a02ae9e8916655fa042c515aa4ceb6655,00500e8c34d680ae0d43a3e84a2a4d6a1dad05d7fc7a6db8fceb2febe2f9d899937ee3d7b5dfa2af7248dc6920809e37,05e2496094e498bdfa05aa676973b7a7fcd9f70cfd859ad342e1ac3480998e150c5ffba8d750b0ca51f88b1d1c34706c,1196e116c4f6ae092a3308122ccd09e8fee95277a6d5cd4c33ed084895d4b1e40dd650fb7db009b69a86a33b725cc39a,0b8745eee6cec3513b8e142d7d122813c4ef51a856d533093bb6d74ed6073264dc9f4bcd889b7dc034ed0b578d3658c0,110815b3ed5cfcccc993eda5e6272573b7b73a62bb27a85b3d5edf6f41a186cdd3e0bbd52b92c1ecba182cc120eabd54,080d6fd19269b333a206819d21bac42bafca7acb748363112098d357bd863eac44d694aa20bbfc88dc4430e292e4a01b,02d355830be701ae5dc0e04149e258f16208c5e5a771ca5a3827baf837d8ebdb6497db7fe13e14b2bf368380119b66c4,0057973cff03522b3031fda563891aa942125320be89b1226bc646e69e219a16274ae48396aa8b45be38e3c691247361,04c15f4475385a4c8da5e854ee22047d557dc0e5a902d9cf1b7a15f675e494a5279ceca134a291fa0c90bdd669269a4f,0adef7e30f15446e0d0308b19760dfa878fada2bbbf847a360b8333a7f488a49d1f61b11209139d9dfd18238b3f2e7bb,03e9674ca3e94c1493106228dd36dc08e3f1054b045cc8ce0c2b441fded4d512e81ebb082a83810d9d5a97ea213a158b,00a840ddebd2809998cd943ecf635e944116135576cf781014bc7ff02e0cd13e6a88bbb9082cd0f70eb0d1f58afa4cb3,172e5019a2315b706dbe6c391f0d652b7f8dad9bca83e08adf20e20577c6d6fa67c3939d809337bb29f5ae1796e959e4,0436f10695b5d8484e67eb3260c5a4b721c9ece0a72239cf081d1c6fce80c3940b1e3f65791529bbe71b55ba94b4de1f,05cc3e4936c915ff2c4c28b2de8097719af68b3d5eae1f7e991ec722e98b3a6c9b0acd1fb5f6ce4a3267c01bdbb419ca,16e6b6a08e681348604162bc3ae75a17a0305f3707672375380b70d19d0af7c06425887492f8ffc486c437da6de36104,101fc082e1e4eb3b500a781ef2de5fc54e349d643e9bed315a22c169a11d0df3deee571312839966147e28395e287cc4,07052111a043a47127fb3e71018fbb62e4b0fdde669f7e0fbe28533121c7123bdb201b506a3d8a20c0b0f5623a2af612,044f2c9171fb093f42a2a5cf2764e88aa245d6327e6294ad41d72971a86f00e83af2d03d9887b855cc5bddb5182352b0,023bc737af1675138e33c7d70651a78e49c5d639c2ec98e5c96144edb4298f9f9ddc16e949f187daa802334cc467f777,1656d48494cc233d331bebf1f7e6896573b298c6fe1ffb215a7b071e6a776951d1f6461b7efe9a2d9324234a1f0e47c4,1031229bccd53f2b33fa7567acb6f4e192fb04847c20ae8ed0502a72177857178ef9a71533e8a1e782978bebf35daa7b,0cb412e1f0533ef873afde5ff9ea56a03ec8d9316400a299532d54af94f71245a8df168682cb0219db57d6eeaca403d4 diff --git a/crypto/src/hash/poseidon/mod.rs b/crypto/src/hash/poseidon/mod.rs index d628fea3e7..a8dc8cd369 100644 --- a/crypto/src/hash/poseidon/mod.rs +++ b/crypto/src/hash/poseidon/mod.rs @@ -1,310 +1 @@ -use crate::merkle_tree::traits::IsMerkleTreeBackend; - -/// Poseidon implementation for curve BLS12381 -use self::parameters::Parameters; - -use lambdaworks_math::{ - elliptic_curve::short_weierstrass::curves::bls12_381::field_extension::BLS12381PrimeField, - field::{element::FieldElement, traits::IsField}, -}; -use std::ops::{Add, Mul}; -mod parameters; pub mod starknet; - -pub struct Poseidon { - params: Parameters, -} - -impl Poseidon { - pub fn new() -> Self { - Self { - params: Parameters::with_t2() - .expect("Error loading parameters for Posedon BLS12381 hasher"), - } - } -} - -impl Default for Poseidon { - fn default() -> Self { - Self::new() - } -} - -impl IsMerkleTreeBackend for Poseidon { - type Node = FieldElement; - type Data = Self::Node; - - fn hash_data( - &self, - input: &FieldElement, - ) -> FieldElement { - // return first element of the state (unwraps to be removed after trait changes to return Result<>) - // This clone could be removed - self.hash(&[input.clone()]) - .unwrap() - .first() - .unwrap() - .clone() - } - - fn hash_new_parent( - &self, - left: &FieldElement, - right: &FieldElement, - ) -> FieldElement { - // return first element of the state (unwraps to be removed after trait changes to return Result<>) - self.hash(&[left.clone(), right.clone()]) - .unwrap() - .first() - .unwrap() - .clone() - } -} - -impl Poseidon -where - F: IsField, -{ - pub fn new_with_params(params: Parameters) -> Self { - Poseidon { params } - } - - pub fn ark(&self, state: &mut [FieldElement], round_number: usize) { - let state_size = state.len(); - for (i, state) in state.iter_mut().enumerate() { - *state += self.params.round_constants[round_number * state_size + i].clone(); - } - } - - pub fn sbox(&self, state: &mut [FieldElement], round_number: usize) { - let is_full_round = round_number < self.params.n_full_rounds / 2 - || round_number >= self.params.n_full_rounds / 2 + self.params.n_partial_rounds; - - if is_full_round { - // full s-box - for current_state in state.iter_mut() { - *current_state = current_state.pow(self.params.alpha); - } - } else { - // partial s-box - let last_state_index = state.len() - 1; - state[last_state_index] = state[last_state_index].pow(self.params.alpha); - } - } - - pub fn mix(&self, state: &mut [FieldElement]) { - let mut new_state: Vec> = Vec::with_capacity(state.len()); - for i in 0..state.len() { - new_state.push(FieldElement::zero()); - for (j, current_state) in state.iter().enumerate() { - let mut mij = self.params.mds_matrix[i][j].clone(); - mij = mij.mul(current_state); - new_state[i] = new_state[i].clone().add(&mij); - } - } - state.clone_from_slice(&new_state[0..state.len()]); - } - - fn permute(&self, state: &mut [FieldElement]) { - for i in 0..(self.params.n_full_rounds + self.params.n_partial_rounds) { - self.ark(state, i); - self.sbox(state, i); - self.mix(state); - } - } - - fn ensure_permuted(&self, state: &mut [FieldElement], offset: &mut usize) { - // offset should be <= rate, so really testing for equality - if *offset >= self.params.rate { - self.permute(state); - *offset = 0; - } - } - - pub fn hash(&self, inputs: &[FieldElement]) -> Result>, String> - where - F: IsField, - { - let t = self.params.rate + self.params.capacity; - if inputs.is_empty() || inputs.len() >= self.params.n_partial_rounds - 1 { - return Err("Wrong input length".to_string()); - } - - let mut state = vec![FieldElement::zero(); t]; - let mut offset: usize = 0; - - let n_remaining = inputs.len() % self.params.rate; - if n_remaining != 0 { - return Err(format!( - "Input length {} must be a multiple of the hash rate {}", - inputs.len(), - self.params.rate - )); - } - - // absorb - for input in inputs { - self.ensure_permuted(&mut state, &mut offset); - state[offset] += input.clone(); - offset += 1; - } - - // squeeze - let mut result = vec![FieldElement::zero(); self.params.rate]; - for result_element in result.iter_mut().take(self.params.rate) { - self.ensure_permuted(&mut state, &mut offset); - *result_element = state[offset].clone(); - offset += 1; - } - - Ok(result) - } -} - -// Test values and parameters are taken from https://github.com/keep-starknet-strange/poseidon-rs/blob/f01ff35ab4dca63a9d6feb7ff3f46c9b04b28b04/src/permutation.rs#L136 -// (values are parsed from decimals and have been converted to hex in our mod) -// The field that these tests use is defined below, and parameters are stored under /s128b -#[cfg(test)] -mod tests { - use lambdaworks_math::{ - field::fields::montgomery_backed_prime_fields::{IsModulus, U384PrimeField}, - unsigned_integer::element::U384, - }; - - use super::*; - - #[derive(Clone, Debug)] - pub struct TestFieldModulus; - impl IsModulus for TestFieldModulus { - const MODULUS: U384 = U384::from_hex_unchecked( - "2000000000000080000000000000000000000000000000000000000000000001", - ); - } - - pub type PoseidonTestField = U384PrimeField; - type TestFieldElement = FieldElement; - - pub fn load_test_parameters() -> Result, String> { - let round_constants_csv = include_str!("s128b/round_constants.csv"); - let mds_constants_csv = include_str!("s128b/mds_matrix.csv"); - - let round_constants = round_constants_csv - .split(',') - .map(|c| TestFieldElement::new(U384::from_hex_unchecked(c.trim()))) - .collect(); - - let mut mds_matrix = vec![]; - - for line in mds_constants_csv.lines() { - let matrix_line = line - .split(',') - .map(|c| TestFieldElement::new(U384::from_hex_unchecked(c.trim()))) - .collect(); - - mds_matrix.push(matrix_line); - } - - Ok(Parameters { - rate: 2, - capacity: 1, - alpha: 3, - n_full_rounds: 8, - n_partial_rounds: 83, - round_constants, - mds_matrix, - }) - } - - #[test] - fn test_poseidon_s128b_t() { - let mut state = [ - TestFieldElement::new(U384::from_u64(7)), - TestFieldElement::new(U384::from_u64(98)), - TestFieldElement::new(U384::from_u64(0)), - ]; - let poseidon = Poseidon::new_with_params(load_test_parameters().unwrap()); - - poseidon.ark(&mut state, 0); - let expected = [ - TestFieldElement::new(U384::from_hex_unchecked( - "16861759ea5568dd39dd92f9562a30b9e58e2ad98109ae4780b7fd8eac77fe8a", - )), - TestFieldElement::new(U384::from_hex_unchecked( - "13827681995D5ADFFFC8397A3D00425A3DA43F76ABF28A64E4AB1A22F275092B", - )), - TestFieldElement::new(U384::from_hex_unchecked( - "BA3956D2FAD4469E7F760A2277DC7CB2CAC75DC279B2D687A0DBE17704A8310", - )), - ]; - assert_eq!(state, expected); - } - - #[test] - fn test_mix() { - let mut state = [ - TestFieldElement::new(U384::from_hex_unchecked( - "13f891b043b3b740cc3e1b3051127d335f08e488322f360a776b3810b7dc690a", - )), - TestFieldElement::new(U384::from_hex_unchecked( - "1bd24b7cb99acf0dbea719ff4007bd60105bcefef21ec509d2f8d4f9bb6a3a1a", - )), - TestFieldElement::new(U384::from_hex_unchecked( - "110853eb2ebee0d940454fe420229a2a0974e666d16c92bab9f36cbd1a0eded", - )), - ]; - - let poseidon = Poseidon::new_with_params(load_test_parameters().unwrap()); - - poseidon.mix(&mut state); - - let expected = [ - TestFieldElement::new(U384::from_hex_unchecked( - "1d30b34b465f8cddc8dc468f137891659c7e32b510cf41cec3aac0b26741681d", - )), - TestFieldElement::new(U384::from_hex_unchecked( - "c445fa4dd2af583994272bede589b06b98fe9cd6d868bf718f6748ba6165620", - )), - TestFieldElement::new(U384::from_hex_unchecked( - "1ed95ae0ea03bb892691f5200fb5902957ac17b3466afa62be808682801f97f9", - )), - ]; - assert_eq!(state, expected); - } - - #[test] - fn test_hash() { - let poseidon: Poseidon = Poseidon::new(); - - let a = FieldElement::one(); - let b = FieldElement::zero(); - - poseidon.hash_new_parent(&a, &b); - } - - #[test] - fn test_permutation() { - let poseidon = Poseidon::new_with_params(load_test_parameters().unwrap()); - - let mut state = [ - TestFieldElement::new(U384::from_u64(7)), - TestFieldElement::new(U384::from_u64(98)), - TestFieldElement::new(U384::from_u64(0)), - ]; - - poseidon.permute(&mut state); - - let expected = [ - TestFieldElement::new(U384::from_hex_unchecked( - "18700783647721BB9AD092B176BBEB5348401C21132CCF83C30134DFAB5A2DEB", - )), - TestFieldElement::new(U384::from_hex_unchecked( - "1CC8856652601B3C81139AD5EC13E4A3A8F4A5DB242555521A09E002E7A10B2B", - )), - TestFieldElement::new(U384::from_hex_unchecked( - "3DCB1CEC811FC2D7401CA7B9B084D167F33B6983D4428C8E0534C9C3CECF46D", - )), - ]; - - assert_eq!(state, expected); - } -} diff --git a/crypto/src/hash/poseidon/parameters.rs b/crypto/src/hash/poseidon/parameters.rs deleted file mode 100644 index a09a150e4d..0000000000 --- a/crypto/src/hash/poseidon/parameters.rs +++ /dev/null @@ -1,82 +0,0 @@ -use lambdaworks_math::{ - elliptic_curve::short_weierstrass::curves::bls12_381::field_extension::BLS12381PrimeField, - field::{element::FieldElement, traits::IsField}, -}; - -type PoseidonConstants = (Vec>, Vec>>); - -pub struct Parameters { - pub rate: usize, - pub capacity: usize, - pub alpha: u32, - pub n_full_rounds: usize, - pub n_partial_rounds: usize, - pub round_constants: Vec>, - pub mds_matrix: Vec>>, -} - -/// Implements hashing for BLS 12381's field. -/// Alpha = 5 and parameters are predefined for secure implementations -impl Parameters { - // t = 3 means width of input is 2 - // sage generate_params_poseidon.sage 1 0 381 3 5 128 - // Params: n=381, t=3, alpha=5, M=128, R_F=8, R_P=56 - pub fn with_t3() -> Result { - let round_constants_csv = include_str!("bls12381/t3/round_constants.csv"); - let mds_constants_csv = include_str!("bls12381/t3/mds_matrix.csv"); - - let (round_constants, mds_matrix) = Self::parse(round_constants_csv, mds_constants_csv)?; - Ok(Parameters { - rate: 2, - capacity: 1, - alpha: 5, - n_full_rounds: 8, - n_partial_rounds: 56, - round_constants, - mds_matrix, - }) - } - - // t = 2 means width of input size is 1 - // sage generate_params_poseidon.sage 1 0 381 2 5 128 - // Params: n=381, t=2, alpha=5, M=128, R_F=8, R_P=56 - pub fn with_t2() -> Result, String> { - let round_constants_csv = include_str!("bls12381/t2/round_constants.csv"); - let mds_constants_csv = include_str!("bls12381/t2/mds_matrix.csv"); - - let (round_constants, mds_matrix) = Self::parse(round_constants_csv, mds_constants_csv)?; - - Ok(Parameters { - rate: 1, - capacity: 1, - alpha: 5, - n_full_rounds: 8, - n_partial_rounds: 56, - round_constants, - mds_matrix, - }) - } - - pub fn parse( - round_constants_csv: &str, - mds_constants_csv: &str, - ) -> Result, String> { - let round_constants = round_constants_csv - .split(',') - .map(|c| FieldElement::::new_base(c.trim())) - .collect(); - - let mut mds_matrix = vec![]; - - for line in mds_constants_csv.lines() { - let matrix_line = line - .split(',') - .map(|c| FieldElement::::new_base(c.trim())) - .collect(); - - mds_matrix.push(matrix_line); - } - - Ok((round_constants, mds_matrix)) - } -} diff --git a/crypto/src/hash/poseidon/s128b/mds_matrix.csv b/crypto/src/hash/poseidon/s128b/mds_matrix.csv deleted file mode 100644 index d3c6af6e3d..0000000000 --- a/crypto/src/hash/poseidon/s128b/mds_matrix.csv +++ /dev/null @@ -1,3 +0,0 @@ -cf637e74647c1797112d9a892ab349f752a41d0786fe3ccfe8aa8b19100e8c2,1126899aecd525518a149bd72873b63a2e63ddce2beeb008b174602d5627e37a,12c89bb904f498c2b93b27b25de1ecb92fceed11ac0edc9e4fa579ab59dbbb68, -5755e4439812879f5de90df763ea97b39ec65d17d51029fa43766692a018caf,e40fb0bb50b31f195cd880c9fa733623e9ade8ba440998a89648cfa4986d1fe,58516197c95d98f783f2e5f2cfb295278c521f10e79022aed0a4e95234400f5, -4d1d1386e0cf786cbe76072195538fde6ad46f5d2a56130a3eebb204210c046,173b460d8dcb542a860598f41981a1be0a33473a94dc67168c5a4ab66d5d29eb,13c5fbb65e8c090e2395fb03b71da4ad138e18486882a863f9a090fbe53c063d, diff --git a/crypto/src/hash/poseidon/s128b/round_constants.csv b/crypto/src/hash/poseidon/s128b/round_constants.csv deleted file mode 100644 index ffa5cbc400..0000000000 --- a/crypto/src/hash/poseidon/s128b/round_constants.csv +++ /dev/null @@ -1 +0,0 @@ -16861759ea5568dd39dd92f9562a30b9e58e2ad98109ae4780b7fd8eac77fe83,13827681995d5adfffc8397a3d00425a3da43f76abf28a64e4ab1a22f27508c9,ba3956d2fad4469e7f760a2277dc7cb2cac75dc279b2d687a0dbe17704a8310,1e26c47a7d421f24f13c4282214aa759291c78f926a2d1c6882031afe67ef4dc,f8985f8e16505145bd6df5518cfd41f2d327fcc948d772cadfe17baca05d6a7,1d427f108675136e204c659875341243c6e26a68b456dc1d142dcf3434169714,15af083f36e4c70f454361733f0883c5847cd2c5d9d4cb8b0465e60edce699dc,fd71701bde3cf8e54fa3f74f7b352a52d3975f92ff84b1ac77e709bfd38888f,603da06882017d49c26f8a6320a1c5eac1b64f699ffea44e39584467a6b1d4d,1c332a6f6bde2f1f8e79ce13f47ad1cdeebd8870fd13a36b613b9721f6453a63,1d3d0ebf61664c9b5310a04c4dec2e7e4b9a813aaeff60d6c9e8caeb5cba78ea,d346a688948442c5ae5ebcb88028d2a6c82f99f928494ee1bfc2d15eaabfecf,1d50a9e24176501da7631ccaecb7a4ab8694ab61f238797098147e69dd91e5af,1a19dcccb783b05aaa62773fedd3570e0f48ad3ed77c8b262b5794daa2687018,1cb085eb1df424933453cc97445954bf3433b6ab9dd5a99592864c00f54a3fac,d3e8a8e8a404bad3af2bf3c03e420ea5a465939d04b6c72e2da084e5aabb797,15ca045c1312c00b1bd14d2537fe5c19fb4049cb137faf5df4f9ada962be8cb3,7c74922a4567ec444997e959f27a5b06820b1ed97596a969939c46c16251806,8c0bba6880d2e3d6bf5088614b9684ff2526a20f91670435dc6f519bb7ab843,4526bcaec43e837d708dd07234c1b2dc1a6203741decd72843849cd0f879353,9cc9a17b00d3564d81efaea5a75a434bef44d92edc6d5b0bfe1ec7f01d613f7,828b1e269b84bd912aa8cdbead0bc1ce1eb7284e2b28ed90bc7b4a4fde8f026,1e2af2f41d76c3f01d9a2482fbdaf6590c19656bcb945b58bb724dc7a994499c,1dcfd7e44946dad9b2618213b0d1bf4a2269bed2dc0d4dbf59e285eee627df1d,7ff2afb40f32e6456fdd1b94da8d3bbcf0312ab9f16ac9bc31955dc8386a75c,1dcd236bdc15b4fc83e90bab8ae37f8aab40efae6fa9cd919b3248ee326e92a5,5463841390e22d60c946418bf0e5822bd999084e30688e741a90bbd53a698a,1a4c940fff3fe757b2021f13eb4d71747efd44a4e51890ae8226e7406144f81d,4e50cb07b3872728dc88f05393d9d03153ca4c02172dd1d7fc77d45e1b0455e,e2ca053e4da0f257b430e53238d2bab1d9b499c35f375d7d0b32e1189b6dcbf,f19f20ac59d1da1aaf37fe0b851bc2419cd89100adff965951bff3d3d7e11a1,17645ca5e87a9f776a82fe5bb90807f44050ac92ca52f5c798935cf47d55a902,95b8aeaca96aab0200eed38d248ecda23d4b71d17133438015391ca63663771,1853d94dbbca7bf5aa8252f106292ac3b98799e908f928c196c1b658bf10b2f1,1a8f90b403e24034c6f4c0a3b70edbb3942b447c615c0f033913831c34de2d2d,2485167dc233a02e1161c4d0bf025159699dd2feb36e3e5b70ae6e770e22096,19c8b08a90d6ed89ff7de548541dd26988f7fdaacdd58698e938607a5feca6f7,105c3bf5cba23fc66b75e79d146f9880c7c4df5ecdad643ce05b16901c48830,a38019787f4cc1c627a65a21bef2106d5015b85dfbd77b2965418b02dbc6bd8,15e624d7698fd09b73dce29a5f24c465c15b52dec8172923a6ebc99a6ddc5ed,5d3688ba56f34fdf56bc056ad8bf740ca0c2efef23b04a479f612fde5800a0a,a29abdef3fef7839e67ed336e82dc6c2e26d872d98b3cce811c69ae363b4451,1be8096ecfcbce15ee400801a56f236db2c43d1e33c92b57ac58daf2d3fc44de,1bad5fec670d6ec8108d605aae834c7ce6a7cd4e1b47bf6a02265352c57db9d5,fcf4598c0cf131d75877afdbb4df6794ef597fff1f98557adca32046aeaef1a,58aecc0081b545f4a4d1c4c8f27932e4170c37841fef49aca0ec7a123c00adf,f57b4b7ee98dffe5460b71995790396e4ef3c859db5b714ec09308d65d2ca6b,16b82800937f87fff3cd974f43322169963d2b54fd2b7ed348dc6cc226718b74,103a915b1814709473427e34ab8fbb7ca044f14088fedae9606b34a60b1e9c66,d4afbf1bd98ff28f9bc01028ff44195c0bb609d367b76269a627689547bfbff,1de1ceb846fe12b1b9524c7d014931072c3852df2d991470b08375edf6e762d3,ff751f98968213fbe5dff3ce06e8cb916709e0c48e3020c6b2b01c1bec0814c,36f6b64463f7afdfc3180616e340536bea7f01d226b68b6d45cd6dfbff811f3,161135c9846fadf3b4511d74fe8de8b48dd4d0e469d6703d7ed4fe4fe8e0dbc0,8b58921a3fbdb4e59b78f6acfca9a21a4ba83cc6e0ae3527fbaad907fc912bf,22a4f8a5cdc7438b9d16b61c2973847211d84eb2fb27b816e52821c2e2b1b21,c1cf6db5d6145fefeccbbc9a50b2ceedeb1765c61516ffcb112f810ad670370,10be44689973d9e51cfc05fa8f4aec6fac6a0ff2fdfab744ade9de11416b6845,1b9bf209c4e117606489cda45128096d6d148a237142dc4951df0b8239be1497,1a09cf541e5f74f32b93310b8ce37b092a58282643860b5707c7eb980ea03a0c,6b562e6005f334a0bdc218ba681b6ba7232e122287036d18c22dd5afa953282,180e8103a23902b55dc6d5f59253a627a2a39c8aca11a914670e7a35dea38c95,6a3725548c6648506bdc1b4d5f9bed83ef8ca7468d68f4fbbf345de2d552f78,e7fcd6997472d37605d0f01a8eccc5f11a45c0aa21eb4ebb447b4af006a4a4a,a6144c95c8de1d04075784d28c06c162a44366f77792d4064c95db6ecb5d006,15b173c8b0eb7ebe4b3a874eb6307cda6fd875e3725061df895dc1466f35023b,17e1c2d6fde8ac8587bae06ad491d391c448f877e53298b6370f2165c3d54de0,1cdb779f3e5b7367996f451b156fe4e28f74d61e7771f9e3fa433b57ca6627b8,bb930d8a6c6493713435ec06b6fed7825c3f71114acb93e240eed6970993e9,c472d73b28304fed708467e9296fb5599d3a08814c31c4189e9579c046e8796,1fba9c303dfee1679e10e3c883ca5ce5614d23739b7cb2052cc23612b11170fa,121c0e3319ede3390425dc9b2c1ed30e6356cb133e97579b822548eb9c4dc4cb,acfd61139e50c4a37b09933816e2a0932e53b7dc4f4947565c1d41e877eb1a7,dabea18941a47e3844544d92ee0eca65bdd10b3f170b0dc2f30acd37e26d8fd,1f7088fdb015c7137a6265e44fef6f724ea28ae28b26e6eee5a751b7ce6bcc2d,babdc9d677230535b3e3c43cfd443076b4ce33cddbc8446120dce84e6122b80,a250f430b7fe72e2e5d00b6b83e52a52ca94879ccfab81a7a602662c2d62c57,1dc92ef479c11a801fb24ef76d57912b12660e7bd156d6cabbb1efb79a258630,1235ec59739163f9510f616fa8b87900fd08fd4208a785cffcf784a63a0fd5d1,1ced4e872eb7e72d207be77e9d11e38f396b5c0ba3376e855523c00b372cc66e,15f9406febca3823b756ef3f6331890b3d46afa705908f68fb7d861c4f275a23,19d9c501d9ff1efd621a9f61b68873c05f17b0384661f06d97edf441abdaa4ac,14b0de22bbd0a43f34982c8e28d2f6e169e37ba694774c4dfa530f41c5359542,9b4d48bd38a3e6b02186aabb291eca0d319f0e3648b2574c49d6fd1b033d913,f558bbea55584d01725d8aa67ddba626b6596bbd2f4e65719702cefcead4bac,11108f1a9500a3e9561ea174600e266a70b157d56ece95b60a44cf7a3eef17d2,8913d96a4f35c12becb92b4b6ae3f8c209fb90caab6668567289b67087bf6c,1e502262c51ad8ed16926346857dec8cca2e99f5742b6bf223f4d8a6f32867ac,fcb5fcdc00891662889280505c915bde962ea034378b343cd3a5931d2ec0e62,12eb919524a898a4f90be9781a1515145baea3bc96b8cd1f01b221c4d2a1ce9e,58efb6272921ad5eada46635e3567dced0662c0161223e3c1c63e8de3ec3d7f,1e2fcd49ca9c7452b436d205ffc2a39594254a1ac34acd46d6955e7844d4f8a3,e3589533083872be62d9acce0b625f885e5941e54bd3a2106fcf837aef5314b,7da445b81e9b31f6d47a5f4d23b92a378a17f119d5e6e70629f8b41fefb12ec,2b22dab62f0817e9fc5737e189d5096a9027882bef1738943b7016256118343,19af01472348f224bacdfed1d27664d0d5bdea769be8fcb8fbef432b790e50ed,76b172dbbeec4b31de313b9390f79ec9284163c8e4986bc5b682e5ac6360315,1070efaeae36f6590f362f6cb423d2009b30ddb4178d46def0bdb2905b3e086a,186cb99b36e5203b0a39872686b84ee1d28c4942b8036a1c25a0e4117ccaeef7,1a9fd44305a5a99e0bbf9674e544bda0fb3d0fe5bb3aa743fd1b8a4fc1dc605b,106b447ded1045663629b184d8c36db3a11a6778d8848142aa6363d6619f977b,642a8b4be4ba722cbfcf55a77339b5d357cceb6946fdc51c14b58f5b8989b65,1c89e0a26f65a0f5cc6cc6aa5b6e775cbc51a73700bd794a7acd79ae1d95883c,bb19d4ef195967cbf78ab5dc2fd1d24816428f45a06293c1b9d57b9a02e920d,1fd2dd994756ea86a576b74790b2194971596f9cd59e55ad2884c52039013dfe,1922810cc08f493f300df869823b9f18b3327e29e9e765002970ef0f2e8c5f9,52f3afaf7c90f3f1d46e1d79a70745b39c04376aafff05771cbd4a88ed418b8,17ccfc88e44a03fda95260f44203086e89552bbe53dcc46b376c5bcab6ea789f,a949125939e69464100228beff83823f5157dd8e067bc8819e40a1ab008ddb2,16cb64e3a0d37a504273ce4ee6929ba372d6811dde135af4078ba6e1912e1019,10d63b53707acf3362f05f688129bf30ad43714257949cd9ded4bf5953837fb6,18bcb1549c9cab70d13bb968b4ea22d0bb7d7460a6965702942092b32ef152dd,13d1c5233657cddbf5ead698fe76f6492792a7205ba0531a0ca25b8d8fe798c9,12240b9755182e57066c2808b1e16ea448e26a83074558d9279f450b79f97521,8cc203d8b0f90400fe8e54f343cef59fe8d70882137de70c9b43ab6615a6476,310c6cc475d91a2e061bacdc175ea9e119e937dea9d2100fa68e03c1f779120,ff84b639f52e45920bc947defced0d8cbdbe033f578699397b83667049106d7,1584ca7f01262b51d89c4562f57139f47e9f038cb32ec35abe4e1da8de3e165b,1135eefaf69b540af7d02f562868be3e02fdc72e01e9510531f9afa78abbbf3,372082b8a6c07100a50a3d33805827ad350c88b56f62c6d36a0d876856a99e8,17c3c12b819a8acf87499bac1a143fc59674f132e33898f0c119e3d12462dfe8,4f1354c51e8f5a05b84157cfeff6822c056ce9e29d602eb46bd9b75a23836db,ada9f26a82714c6075739ba206507a08ac360150e849950ef3973548fbd2fe0,1287173956a2bd2f11b5ec29195e38cc3f6a65ff50801aa75fd78dd55070285a,7273101c190fe38212420095a51c8411c7f3227f6a7a4a64ae6ba7f9201e135,2dbf2a6b56b26d23ebeb61e500687de749b03d3d349169699258ee4c98005fc,885b6cbb29739b7808e67f00ab89b52ab89ef8d92530394e4b910efd706c7fc,bd55b5f1171ef731dacbcbadfd5b910b493fa9589fd937e3e06ce26b08925aa,10aaedaa6ef2f9667d16b3b295410c0e44f7a2f8135c207824f6ae2a9b16e91d,6aca6ebf70b1c006c6331e9f1a5c4cc89b80f8adc5d18915c1cd0d496ccf5ea,1678602af36a84abb010f831d403d94d5e90003e6d37c677e9dd157fb27776,2022036bdf687b441b547fefdf36d4c2cd3f4b0526a88aafe60a0a8f508bad5,87bfc350957c979ca664397414bdfb8f9b8dfe49fb63e32353d4e2e8d1d4af7,12d639cbd418ca95c24ea29ccd1d15ab81f43a499b27a06d3c5e2176f7ad79c0,ecdea7f959a3e488403d5b39687a1fe0dee3369e5fbc0f4779569f64506e18,bf656bdc4fefda3b70658e2f1992ef9f22e5f2d28c490e21d4e34357154b559,10d1b8cb1561ee7d2319638ccab9033dfec47596f8a6f4ce6594e19fddd5925c,1758ffc77c62e312f86ef6ea01545ad76f281ec2941da7222d1e8b4e2ec1f1a0,1a0315ca07956f6e995386e96aeaa1b4596aacd28f83c32f29a591c95e6fcadd,3e55cf341e7c280cb05f3d6ff9c8d9f2cfe76b84a9d1b0f54884b316b740d8d,14d56feb32cde735eede9749739be452e92c029007a06f6e67c81203bf650c6d,1cee807aa678a8ab33b6171eaa6a2544497f7599fb8145d7e8089f465403c8ad,1a5d2bacc8f1ed4048cb5f394de2cb6e1f365e56a1bc579d0f9a8ad2ef2b3836,1df573de597ce1679fc20051f6501268cd4b278811924af1f237d15feb17bd4f,b0297c3c54a4ecc5826a280e053cf7a3c1e84a1dcf8b33c682cf85ddac86e01,1af5e9c47c9a862343c7526a59783f03c6bc79b69b8709fe6a052b93a8339af7,19bf75c7a739da4829f9c23065ff8ccb1da7deec83e130bcd4a27a416c72b854,e0563d5f852ad6c5989017bd5c4cfdc29cd27fc4e91eeabdb8e864df3c3c685,fa4b1d70885a92b0969635468daec94f8156c20e3131bd71005be1cd16ccfb1,1b47bb025695e416f1e201cd62aa4600b8b85cf718cd1d400f39c10e59cc585e,e783ab1e1ef97189e7f9381eb6ab0de2c4c9c2de413691ba8aa666292e9e221,933e0280c6de7b77b3870a07823c081fd9c4cb99d534debd6a7bfb4e5b0dd59,8865d450ce29d212fb5db72460b3560a2f093695573dff94fd0216eb925bef6,11de023f840e053035526dabacf0dee948efba06bcbb414ecd81a6b301664e5c,855fc1e341bfcdc805015a96f724c5ac7cc7b892a292d38190631ab1a5388d4,12df6557bfd4a4cde7b27bf51552d2b5162706a3e624faca01a307ef8d53285d,913a8a66962cdddd92a6bd3e9c1d55ef8f226da95e4d629046d73d0507f6275,1a71577d6ee9f9027f2c889874ba5b44ca1076033db5c2de4f3367b08c008e68,b396b33911218d7b0365c09348a561ef1ccb956fc673bc5291d311866538581,19e1392f2da08450c8a7d89e899189306170baa3c3436e6a5398f69c8f321648,1e6154508103200adf118e1d6e7c61a333e313b1a9a5b6d69c876bd2e7d694d0,6b14294e71cd7bf776edbd432d20eb8f66d00533574e46573516f0cacdeec8b,f252fbbb06c2769338b1c41df31e4e51fe2a18e2406c671915cab6eb1a1d4ff,13ccf71be7cc2abcbcf5a09807c69679430c03645747621b7f5327cb00ff99dc,a9778dc707503a36a9f7c97b4ceef0a9b39001d034441617757cd816dac91ad,1b9473f6f06bb82d33590d34e3bae36e491f7bbf86a26aa55a8f5b27bb98d4dd,7ba7c32f875b62b895caa0215f996fd4ad92bab187e81417063dde91c08c033,1b7c1367e49cbf0703b22aac82abf83b0ed083148a5f4c92839e5d769bdab6c5,1dc9eb899931d1fbb53ffcf833cdfa05c2068375ff933eb37ae34157c0b2d963,15f6054a4d486938c27772fb50a7d2e5c1557ffdc1ffd07331f2ca26c6e3b669,20e6d62a2fe0f35b0fab83e8c7d1e8bfd0fec827960e40a91df64664dcd777d,16290a56a489abd0120c426fe0e409c2ff17adf51f528cafb0d026d14ffd6ac3,1b703f16f99033332267a6f7ece342705a32ca4c101417286279f6fc315edc8e,5194962daf666e79a0c32b5a9a307ba92e2c630f70e439195b680dd296df406,e8eae20a79a7c1242c34617b01340fb5fd4bea2aa58b98d2400d9b515ee5e2,b69058169d62f3aae28bfb28def7cd8d00dd7c2894fae4ffec65242afa5cd58,418c963bc9717f274077503ee472f22cfdff0973190ab189c7b93103fd78179,168d07a3eefc78865b28b3f4dc93167fb8c97112d14a25b4d4db55972015638e,1d17e892228df1dbf15a3c4241c98ba25ba0b5557375003f8748583a61836384,5cc0f0f6cf9bda4a150116e7932f8fe74ac20ad8100c41dc9c99538792e27a7,53d5d7863434afe29bdb1f8a648e4820883543e821f0f5c1668884c0be41eda,18a158126b89e5f3a600bf53f8101707b072218912dd0d9df2528f67de24fe04,1eb53b80726538b1e582069a698323d44c204bed60672b8d8d073bed2fede506,11097fb448406b606de0877efd58c01be53be83bde9601a9acc9e0ca2091fda5,18cbc0ff7239d2f53902396389d67b3049ce1fefde66333ce37ca441f5a31bf8,1f9a3d91dd8a2f2b632eb43d57b5c5d838ceebd64603f68a8141ebef84280e8a,823fb472fe573bc5300f74e8f6de8fe1185078218eceb938900e7598a368dcc,17ac73134016d2caa4c63a6b9494c0bd7a6ba87cc33e8a8e23ebda18bfb67c2c,199a16068c3eab2b03f1b5c5ee2485ccc163d9ab17bb035d5df6e31c3dcf8f2c,1f24b4356a6ba954d4ef9fd1634752820ee86a925725ac392134d90def073fc,803e44e7f7aed13add59b6b4d11c60a528fb70727f35d817305971592333d4c,df93b02f82672bd14535a511ed3eb4fe85987ae57bc9807cbd94cd7513d3961,8f0a0a88db990b4d71c3d51d4197fa3fd1cc76e670607e35ca2d3bada295250,13432226916d3125acac1e211431fd4cd2b6f2e80626af6564bdde3e77608dbe,1d5625941bfea5838175192845a7ad74b0b82940ef5f393ca3830528d59cf931,8ddf48695b204587dfe4f8cb3ef1b39783e9b92f9276b858e2e585e318e20a5,a60730a657ff88c8851a679ab2a1490434ee50d4953e7c5d3194578b08ae8ea,1ccfd231373aa37496283840bdb79ba6d7132775b398d324bcd206842b961abb,1b203843c41cd396f14fa0bc0b2191a27ebc659e74fd48f981e963de57eff26c,802c2f6ae5624a6fb8435d1c86bf76c260f5e77a54b006293705872e647cc4a,f80225456e63770b3e561384ef2e73a85b0e142b69752381535022014765f1c,7f602ec1a80a051fd21b07f8e2960613082fc954b9a9ff641cc432a75c81887,162561b0a0a720f3b60f6aaf7022b7d323fe77cd7c1ab432f0c8c118ca7e6bde,1604fe5a6a22337ca69b05dea16b1cf22450c186d093754cb9b84a8a03b70bd6,11cf9987a40446c0d3dc140bf5f9b76f6eada5995905189f8682eaf88aef2b83,16bc0b2487c1ed883db47a4bdd60cf69debee233e91b50e9ee42ce22cbfbacd3,af5dbb5055eb66aa11403b93e90338b7620c51356d2c6adcbf87ab7ea0792f3,446328f4dddacc129743c43883d59c45f63b8a623a9cf318489e5fc4a550f76,4ba30c5240cde5bca6c4010fb4b481a25817b43d358399958584d2c48f5af25,df5275f76425982c89209117734ae85708351d2cf19af5fe39a32f89c2c8a9f,d76f3b5156f45d0e18c7f98df3b2f7b993cdda4eb8cb92415e1be8e6af2fc2d,191dc3f15cba92bded5a44b55a5b026df84a61719ed5adbb93c0e8e12d35ef40,44c40e6bd52e8a2d9896403ae4f543ae1c1d9ea047d75f8a6442b8feda04dd0,1836d733a540012bd0ccbf4974e80ac1954bf90fe9ea4e2c914ad01166026e7,bc553be9776b585a8159d306ef084727611df8037761f00f84ca02ce731b3b6,186ce94781c1a1fada1c7b87e0436b1b401ae11a6d757843e342f5017076a062,1381ec71fbdef2480253be9f00f4e6b9e107f457812effb7371cc2daa0acd0fb,9844da9cc0ee9856490d847320d9f3cd4fb574aa687bafdfe0ffa7bf2a8f1b4,7a8bf471f902d5abb27fea5b401483dedf97101047459682acfd7f9b65a812f,1633b6fb004de51a41915fb51ac174456f5a9cdff7aecb6e6b0d063839e56338,1979ee5cec4961c7771200382bfc6d17bbe546ba88fed8b17535fd70fbc50ab9,a806c07861857f3ea9891b42d565256b0312446f07435ac2cae194330bf8c58,c38703d9487079390c7a6b8af194b8b603bb2cdfd26bfa356ac9bb6ee0413a3,1a4446628f5602587153bd3a482b7f6e1c56f4e02225c628a585d58a920035b8,1cc2a76e5ce83177b0685cdeeea3a253ae48f6606790d817bd96025e5435e271,1f8a2332352098c492933c079b148aed57d5e4ce1ab122d370983b8caa0e030c,f9ca6c5e102598e51144ea5937dd07cadce1aa691b19e6db87070ba51ec22d6,16b2e4a46e37ae6ef952d9d34f8d6bd84a442ebfd1ac5d17314e48922af79c6b,305d6cd95cc1beb6805d93d3d8d74e1ca7d443f11e34a18e3529e0d03435d1,1e097b4b8b90dacfb39743ed23f8956cabb7aea70cc624a415c7c17b37fbf9b2,1064e1b3f16c25be845bdb98373e77dad3bdcc90865b0f0af96288707c188950,1e49fafe673f21a123384d841221b73421c56014af2ffdf57f1579ae911fd33e,1fd806dccbf1a1346b294404e849722f2baa2f4d19005a49d1ba288a77fefe45,1d951a37da53e3eec0b3e2db1a9a235d7a03f48f443be6d659119c44aafc7525,ed87fa479fb59274d1912c3554ae3d010496a31bdacb542c816a1607a907735,9451cccd4200f06d473ad73466b4e8c0a712a0b12bb6fc9462a3ac892acc9bc,bca1b6400b3e52107642535f1ca9b03832ca0faa15e1c4ed82dd1efdc0763db,1d2c55735b2f0a2060ad1516a8f13592b0dd024ff4162539f993a99c7a1a4d9e,fe04de60aa8008ff0149d1dee29617de750bd5ce3e9fa5e62951d65f6b924d7,18271784e6920a9be47c4c8fab71c8f8303ef29e26f289223edf63291c0a5498,dc7c19061a84d2e60a04b8f0adaa603c8afe93f17b7f0e56b49514af43d0c6d,1972db5affe7823e419da337cb79061e090943c2959dea1b38e4436f5482eb16,1518b7975a6d8c270eac9fe4082916f021a7ecbadf18809746a9e061a2cb9467,120c5539dc45dc10d4bbc2440a9f5061d74b8ae5e37b34e8755a0315f1e196ef,19ea6f5fb309fa7d08bc7d516e80efc3a977b47208283cf35a9d8bc213b90b17,d0ce323c5128d9cfdd8ddd8ba9cfe2efd424b5de167c7257d1f766541e29df1,401e37d0e2763a3695538b41d3c28215b865f5b7d1b497a8919284c613cb7ed,e45a0de30acc2e67f2893056fc5880255daa12cc61261cc0fab9cf57c57397f,69bc3841eb0a25cd9e988d75f09f698d4fdc9d0d69219f676b66ae7fa3d4964,102684bbe315ad124bdd47c38fe72db47cf0ae0c455cda5484baf523f136bdcb,11e0f83c547c96c68202e8d34e5595a88858c2afa664365e4acb821fd8a13fa,1caf4a7635f8c6585966567ceec34315d0f86ac66c1e5a5ecac945f1097b8301,cfba58cf8aaf4223cb7158908ccc18b1dc48894d2bb46225c72b11f4c74b278,397c4c1691159a28cc90da2e664f8c29a7f89be0ead679a38b0f44c8a2a0e35,6563b9ebb643a5bad397fa5dd13c501f326dd7f32be22e20998f59ec7bad11,b76edb238f7b641ea81d307f4c79f9afec48562076dd09c36cd79e9cb817166,60d4208bb50ea9df29ed22addcd50a1b337504039690eb858584cda96e2e067,1ea37d569d2fbb7adbff1019dc3465ec0f30da46918ab020344a52f1df9a9222,d3b174c7290c6bf412083ff35d23821dc512f1df073c1b429130371ac63b1a,a26ed3d7634762a4b46eb2a5c3b814634d974919689fb489fe55e525b980383,15f3997e7dafcb13e0e7a23d33d2fd9ef06f4d79bd7ffa1930e8b0080d218518,17c5eec716d944ee434df335a10bbac504f886f7f9d3c1648348c3fae8fdf161,53cc30d7fe0f84e7e24fd22c0f9ad68a89da85553f871ef63d2f55f57e1a7c,368821ee335d6dc19b95769f47418569474a24f6e83b268fefa4cd58c4ec8fd,85334f75b052b5f35119816883040da72c6d0a61538bdfff46d6a242bfeb7ab,dd0af4fcbd9dfefc1020cca9d871ae68f80ee4af2ec6547cd49d6dca50aa438,1b0131bce2fba4e84114a19c46d24e00b4699dc00f1d53ba5ab99537901b1e71,5646a95a7c1add2b34c0750ed2e641c538f93f13161be3c4957660f2e78896e,14b9f291d7b430ad9fac36230a11f43e78581f5259692b52c90df47b7d4ec01f,5006d393d347fc81a98f19127072dc83e00becf6ceb4d73d890e74abae01a22,e2c9d42199f3a470e7cb8a115143106acf4f702e6b346fd202dc3b26a679d8d,1d1274d092db5018f180b1a8a13b7f2c7606836eabd8af54bf1d9ac2dc5717b1,1e1fc552b8eb75247ad0fb7aaa4ca528f415e14f0d9cdbed861a8db0bfff0c6a diff --git a/examples/merkle-tree-cli/src/main.rs b/examples/merkle-tree-cli/src/main.rs index 9817bf39d6..661bd73c9e 100644 --- a/examples/merkle-tree-cli/src/main.rs +++ b/examples/merkle-tree-cli/src/main.rs @@ -1,13 +1,11 @@ mod commands; use clap::Parser; use commands::{MerkleArgs, MerkleEntity}; -use lambdaworks_crypto::{ - hash::poseidon::Poseidon, - merkle_tree::{merkle::MerkleTree, proof::Proof}, +use lambdaworks_crypto::merkle_tree::{ + backends::field_element::TreePoseidon, merkle::MerkleTree, proof::Proof, }; -use lambdaworks_math::{ - elliptic_curve::short_weierstrass::curves::bls12_381::field_extension::BLS12381PrimeField, - field::element::FieldElement, +use lambdaworks_math::field::{ + element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, }; use std::io::BufWriter; use std::{ @@ -15,7 +13,7 @@ use std::{ io::{self, Write}, }; -type FE = FieldElement; +type FE = FieldElement; fn load_fe_from_file(file_path: &String) -> Result { FE::from_hex(&fs::read_to_string(file_path)?.replace('\n', "")) @@ -32,7 +30,7 @@ fn load_tree_values(tree_path: &String) -> Result, io::Error> { fn generate_merkle_tree(tree_path: String) -> Result<(), io::Error> { let values: Vec = load_tree_values(&tree_path)?; - let merkle_tree = MerkleTree::>::build(&values); + let merkle_tree = MerkleTree::>::build(&values); let root = merkle_tree.root.representative().to_string(); println!("Generated merkle tree with root: {:?}", root); @@ -46,7 +44,7 @@ fn generate_merkle_tree(tree_path: String) -> Result<(), io::Error> { fn generate_merkle_proof(tree_path: String, pos: usize) -> Result<(), io::Error> { let values: Vec = load_tree_values(&tree_path)?; - let merkle_tree = MerkleTree::>::build(&values); + let merkle_tree = MerkleTree::>::build(&values); let Some(proof) = merkle_tree.get_proof_by_pos(pos) else { return Err(io::Error::new(io::ErrorKind::Other, "Index out of bounds")); @@ -72,7 +70,7 @@ fn verify_merkle_proof( let file_str = fs::read_to_string(proof_path)?; let proof: Proof = serde_json::from_str(&file_str)?; - match proof.verify::>(&root_hash, index, &leaf) { + match proof.verify::>(&root_hash, index, &leaf) { true => println!("\x1b[32mMerkle proof verified succesfully\x1b[0m"), false => println!("\x1b[31mMerkle proof failed verifying\x1b[0m"), } From 347957a3a7af73847b83d5acad8b201dd64f2e83 Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Mon, 4 Dec 2023 11:51:29 -0300 Subject: [PATCH 04/29] feat: turn Poseidon parameters into trait (#706) * feat: turn Poseidon parameters into trait Rather than having the parameters as internal state, we consider them to be compile time constants, and implement them via stateless structures with associated constants, which in turn implement the `PermutationParameters` trait. We use a seal trait for the actual code to avoid overriding methods by mistake. This is the `Poseidon` trait. It's automatically implemented for all implementors of `PermutationParameters`. * Format files --------- Co-authored-by: Mariano Nicolini --- crypto/src/hash/poseidon/mod.rs | 234 +++++++++ crypto/src/hash/poseidon/parameters.rs | 27 + crypto/src/hash/poseidon/starknet/mod.rs | 245 +-------- .../src/hash/poseidon/starknet/parameters.rs | 378 +++++++++++--- .../hash/poseidon/starknet/round_constants.rs | 463 ------------------ .../src/merkle_tree/backends/field_element.rs | 46 +- .../backends/field_element_vector.rs | 46 +- examples/merkle-tree-cli/src/main.rs | 7 +- 8 files changed, 613 insertions(+), 833 deletions(-) create mode 100644 crypto/src/hash/poseidon/parameters.rs delete mode 100644 crypto/src/hash/poseidon/starknet/round_constants.rs diff --git a/crypto/src/hash/poseidon/mod.rs b/crypto/src/hash/poseidon/mod.rs index a8dc8cd369..5954a142f0 100644 --- a/crypto/src/hash/poseidon/mod.rs +++ b/crypto/src/hash/poseidon/mod.rs @@ -1 +1,235 @@ +use lambdaworks_math::field::element::FieldElement as FE; + +pub mod parameters; pub mod starknet; + +use parameters::PermutationParameters; + +mod private { + use super::*; + + pub trait Sealed {} + + impl Sealed for P {} +} + +pub trait Poseidon: PermutationParameters + self::private::Sealed { + fn hades_permutation(state: &mut [FE]); + fn full_round(state: &mut [FE], round_number: usize); + fn partial_round(state: &mut [FE], round_number: usize); + fn mix(state: &mut [FE]); + fn hash(x: &FE, y: &FE) -> FE; + fn hash_single(x: &FE) -> FE; + fn hash_many(inputs: &[FE]) -> FE; +} + +impl Poseidon for P { + fn hades_permutation(state: &mut [FE]) { + let mut round_number = 0; + for _ in 0..P::N_FULL_ROUNDS / 2 { + Self::full_round(state, round_number); + round_number += 1; + } + for _ in 0..P::N_PARTIAL_ROUNDS { + Self::partial_round(state, round_number); + round_number += 1; + } + for _ in 0..P::N_FULL_ROUNDS / 2 { + Self::full_round(state, round_number); + round_number += 1; + } + } + + fn full_round(state: &mut [FE], round_number: usize) { + for (i, value) in state.iter_mut().enumerate() { + *value = &(*value) + &P::ROUND_CONSTANTS[round_number * P::N_ROUND_CONSTANTS_COLS + i]; + *value = value.pow(P::ALPHA); + } + Self::mix(state); + } + + fn partial_round(state: &mut [FE], round_number: usize) { + for (i, value) in state.iter_mut().enumerate() { + *value = &(*value) + &P::ROUND_CONSTANTS[round_number * P::N_ROUND_CONSTANTS_COLS + i]; + } + + state[P::STATE_SIZE - 1] = state[P::STATE_SIZE - 1].pow(P::ALPHA); + + Self::mix(state); + } + + fn mix(state: &mut [FE]) { + let mut new_state: Vec> = Vec::with_capacity(P::STATE_SIZE); + for i in 0..P::STATE_SIZE { + let mut new_e = FE::zero(); + for (j, current_state) in state.iter().enumerate() { + let mut mij = P::MDS_MATRIX[i * P::N_MDS_MATRIX_COLS + j].clone(); + mij = mij * current_state; + new_e += mij; + } + new_state.push(new_e); + } + state.clone_from_slice(&new_state[0..P::STATE_SIZE]); + } + + fn hash(x: &FE, y: &FE) -> FE { + let mut state: Vec> = vec![x.clone(), y.clone(), FE::from(2)]; + Self::hades_permutation(&mut state); + let x = &state[0]; + x.clone() + } + + fn hash_single(x: &FE) -> FE { + let mut state: Vec> = vec![x.clone(), FE::zero(), FE::from(1)]; + Self::hades_permutation(&mut state); + let x = &state[0]; + x.clone() + } + + fn hash_many(inputs: &[FE]) -> FE { + let r = P::RATE; // chunk size + let m = P::STATE_SIZE; // state size + + // Pad input with 1 followed by 0's (if necessary). + let mut values = inputs.to_owned(); + values.push(FE::from(1)); + values.resize(((values.len() + r - 1) / r) * r, FE::zero()); + + assert!(values.len() % r == 0); + let mut state: Vec> = vec![FE::zero(); m]; + + // Process each block + for block in values.chunks(r) { + let mut block_state: Vec> = + state[0..r].iter().zip(block).map(|(s, b)| s + b).collect(); + block_state.extend_from_slice(&state[r..]); + + Self::hades_permutation(&mut block_state); + state = block_state; + } + + state[0].clone() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::hash::poseidon::starknet::PoseidonCairoStark252; + use lambdaworks_math::field::{ + element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, + }; + + #[test] + fn test_hades_permutation() { + // Initialize a state to test. The exact contents will depend on your specific use case. + let mut state: Vec> = vec![ + FieldElement::::from_hex("0x9").unwrap(), + FieldElement::::from_hex("0xb").unwrap(), + FieldElement::::from_hex("0x2").unwrap(), + ]; + + PoseidonCairoStark252::hades_permutation(&mut state); + + // Compare the result to the expected output. You will need to know the expected output for your specific test case. + let expected_state0 = FieldElement::::from_hex( + "0x510f3a3faf4084e3b1e95fd44c30746271b48723f7ea9c8be6a9b6b5408e7e6", + ) + .unwrap(); + let expected_state1 = FieldElement::::from_hex( + "0x4f511749bd4101266904288021211333fb0a514cb15381af087462fa46e6bd9", + ) + .unwrap(); + let expected_state2 = FieldElement::::from_hex( + "0x186f6dd1a6e79cb1b66d505574c349272cd35c07c223351a0990410798bb9d8", + ) + .unwrap(); + + assert_eq!(state[0], expected_state0); + assert_eq!(state[1], expected_state1); + assert_eq!(state[2], expected_state2); + } + #[test] + fn test_hash() { + let x = FieldElement::::from_hex("0x123456").unwrap(); + let y = FieldElement::::from_hex("0x789101").unwrap(); + + let z = PoseidonCairoStark252::hash(&x, &y); + + // Compare the result to the expected output. You will need to know the expected output for your specific test case. + let expected_state0 = FieldElement::::from_hex( + "0x2fb6e1e8838d4b850877944f0a13340dd5810f01f5d4361c54b22b4abda3248", + ) + .unwrap(); + + assert_eq!(z, expected_state0); + } + + #[test] + fn test_hash_single() { + let x = FieldElement::::from_hex("0x9").unwrap(); + + let z = PoseidonCairoStark252::hash_single(&x); + + // Compare the result to the expected output. You will need to know the expected output for your specific test case. + let expected_state0 = FieldElement::::from_hex( + "0x3bb3b91c714cb47003947f36dadc98326176963c434cd0a10320b8146c948b3", + ) + .unwrap(); + + assert_eq!(z, expected_state0); + } + + #[test] + fn test_hash_many() { + let a = FieldElement::::from_hex("0x1").unwrap(); + let b = FieldElement::::from_hex("0x2").unwrap(); + let c = FieldElement::::from_hex("0x3").unwrap(); + let d = FieldElement::::from_hex("0x4").unwrap(); + let e = FieldElement::::from_hex("0x5").unwrap(); + let f = FieldElement::::from_hex("0x6").unwrap(); + + let ins = vec![a, b, c, d, e, f]; + let z = PoseidonCairoStark252::hash_many(&ins); + + // Compare the result to the expected output. You will need to know the expected output for your specific test case. + let expected_state0 = FieldElement::::from_hex( + "0xf50993f0797e4cc05734a47daeb214fde2d444ef6619a7c1f7c8e0924feb0b", + ) + .unwrap(); + assert_eq!(z, expected_state0); + + let ins = vec![a]; + let z = PoseidonCairoStark252::hash_many(&ins); + let expected_state0 = FieldElement::::from_hex( + "0x579e8877c7755365d5ec1ec7d3a94a457eff5d1f40482bbe9729c064cdead2", + ) + .unwrap(); + assert_eq!(z, expected_state0); + + let ins = vec![a, b]; + let z = PoseidonCairoStark252::hash_many(&ins); + let expected_state0 = FieldElement::::from_hex( + "0x371cb6995ea5e7effcd2e174de264b5b407027a75a231a70c2c8d196107f0e7", + ) + .unwrap(); + assert_eq!(z, expected_state0); + + let ins = vec![a, b, c]; + let z = PoseidonCairoStark252::hash_many(&ins); + let expected_state0 = FieldElement::::from_hex( + "0x2f0d8840bcf3bc629598d8a6cc80cb7c0d9e52d93dab244bbf9cd0dca0ad082", + ) + .unwrap(); + assert_eq!(z, expected_state0); + + let ins = vec![a, b, c, d]; + let z = PoseidonCairoStark252::hash_many(&ins); + let expected_state0 = FieldElement::::from_hex( + "0x26e3ad8b876e02bc8a4fc43dad40a8f81a6384083cabffa190bcf40d512ae1d", + ) + .unwrap(); + + assert_eq!(z, expected_state0); + } +} diff --git a/crypto/src/hash/poseidon/parameters.rs b/crypto/src/hash/poseidon/parameters.rs new file mode 100644 index 0000000000..e2eb064588 --- /dev/null +++ b/crypto/src/hash/poseidon/parameters.rs @@ -0,0 +1,27 @@ +use lambdaworks_math::field::{element::FieldElement as FE, traits::IsPrimeField}; + +/// Parameters for Poseidon +/// MDS constants and rounds constants are stored as references to slices +/// representing matrices of `N_MDS_MATRIX_ROWS * N_MDS_MATRIX_COLS` and +/// `N_ROUND_CONSTANTS_ROWS * N_ROUND_CONSTANTS_COLS` respectively. +/// We use this representation rather than an array because we can't use the +/// associated constants for dimension, requiring many generic parameters +/// otherwise. +pub trait PermutationParameters { + type F: IsPrimeField + 'static; + + const RATE: usize; + const CAPACITY: usize; + const ALPHA: u32; + const N_FULL_ROUNDS: usize; + const N_PARTIAL_ROUNDS: usize; + const STATE_SIZE: usize = Self::RATE + Self::CAPACITY; + + const MDS_MATRIX: &'static [FE]; + const N_MDS_MATRIX_ROWS: usize; + const N_MDS_MATRIX_COLS: usize; + + const ROUND_CONSTANTS: &'static [FE]; + const N_ROUND_CONSTANTS_ROWS: usize; + const N_ROUND_CONSTANTS_COLS: usize; +} diff --git a/crypto/src/hash/poseidon/starknet/mod.rs b/crypto/src/hash/poseidon/starknet/mod.rs index 1b3947acbf..0aba345722 100644 --- a/crypto/src/hash/poseidon/starknet/mod.rs +++ b/crypto/src/hash/poseidon/starknet/mod.rs @@ -1,245 +1,2 @@ pub mod parameters; -pub mod round_constants; -// mod starknet_poseidon; -use self::parameters::PermutationParameters; - -use lambdaworks_math::field::{element::FieldElement, traits::IsPrimeField}; -use std::ops::{Add, Mul}; - -#[derive(Clone)] -pub struct Poseidon { - params: PermutationParameters, - // Suggestion: Add the state here -} - -impl Poseidon { - pub fn new_with_params(params: PermutationParameters) -> Self { - Poseidon { params } - } - - pub fn hades_permutation(&self, state: &mut [FieldElement]) { - let mut round_number = 0; - for _ in 0..self.params.n_full_rounds / 2 { - self.full_round(state, round_number); - round_number += 1; - } - for _ in 0..self.params.n_partial_rounds { - self.partial_round(state, round_number); - round_number += 1; - } - for _ in 0..self.params.n_full_rounds / 2 { - self.full_round(state, round_number); - round_number += 1; - } - } - - pub fn full_round(&self, state: &mut [FieldElement], round_number: usize) { - for (i, value) in state.iter_mut().enumerate() { - *value = &(*value) + &self.params.round_constants[round_number][i]; - *value = value.pow(self.params.alpha); - } - self.mix(state); - } - pub fn partial_round(&self, state: &mut [FieldElement], round_number: usize) { - for (i, value) in state.iter_mut().enumerate() { - *value = &(*value) + &self.params.round_constants[round_number][i]; - } - - state[self.params.state_size - 1] = - state[self.params.state_size - 1].pow(self.params.alpha); - - self.mix(state); - } - - pub fn mix(&self, state: &mut [FieldElement]) { - let mut new_state: Vec> = Vec::with_capacity(self.params.state_size); - for i in 0..self.params.state_size { - new_state.push(FieldElement::zero()); - for (j, current_state) in state.iter().enumerate() { - let mut mij = self.params.mds_matrix[i][j].clone(); - mij = mij.mul(current_state); - new_state[i] = new_state[i].clone().add(&mij); - } - } - state.clone_from_slice(&new_state[0..self.params.state_size]); - } - - pub fn hash(&self, x: &FieldElement, y: &FieldElement) -> FieldElement { - let mut state: Vec> = vec![x.clone(), y.clone(), FieldElement::from(2)]; - self.hades_permutation(&mut state); - let x = &state[0]; - x.clone() - } - - pub fn hash_single(&self, x: &FieldElement) -> FieldElement { - let mut state: Vec> = - vec![x.clone(), FieldElement::zero(), FieldElement::from(1)]; - self.hades_permutation(&mut state); - let x = &state[0]; - x.clone() - } - pub fn hash_many(&self, inputs: &[FieldElement]) -> FieldElement { - let r = self.params.rate; // chunk size - let m = self.params.state_size; // state size - - // Pad input with 1 followed by 0's (if necessary). - let mut values = inputs.to_owned(); - values.push(FieldElement::from(1)); - values.resize(((values.len() + r - 1) / r) * r, FieldElement::zero()); - - assert!(values.len() % r == 0); - let mut state: Vec> = vec![FieldElement::zero(); m]; - - // Process each block - for block in values.chunks(r) { - let mut block_state: Vec> = - state[0..r].iter().zip(block).map(|(s, b)| s + b).collect(); - block_state.extend_from_slice(&state[r..]); - - self.hades_permutation(&mut block_state); - state = block_state; - } - - state[0].clone() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::hash::poseidon::starknet::parameters::{ - DefaultPoseidonParams, PermutationParameters, - }; - use lambdaworks_math::field::{ - element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - }; - - #[test] - fn test_hades_permutation() { - let params = PermutationParameters::new_with(DefaultPoseidonParams::CairoStark252); - - let poseidon = Poseidon::new_with_params(params); - - // Initialize a state to test. The exact contents will depend on your specific use case. - let mut state: Vec> = vec![ - FieldElement::::from_hex("0x9").unwrap(), - FieldElement::::from_hex("0xb").unwrap(), - FieldElement::::from_hex("0x2").unwrap(), - ]; - - poseidon.hades_permutation(&mut state); - - // Compare the result to the expected output. You will need to know the expected output for your specific test case. - let expected_state0 = FieldElement::::from_hex( - "0x510f3a3faf4084e3b1e95fd44c30746271b48723f7ea9c8be6a9b6b5408e7e6", - ) - .unwrap(); - let expected_state1 = FieldElement::::from_hex( - "0x4f511749bd4101266904288021211333fb0a514cb15381af087462fa46e6bd9", - ) - .unwrap(); - let expected_state2 = FieldElement::::from_hex( - "0x186f6dd1a6e79cb1b66d505574c349272cd35c07c223351a0990410798bb9d8", - ) - .unwrap(); - - assert_eq!(state[0], expected_state0); - assert_eq!(state[1], expected_state1); - assert_eq!(state[2], expected_state2); - } - #[test] - fn test_hash() { - let params = PermutationParameters::new_with(DefaultPoseidonParams::CairoStark252); - - let poseidon = Poseidon::new_with_params(params); - - let x = FieldElement::::from_hex("0x123456").unwrap(); - let y = FieldElement::::from_hex("0x789101").unwrap(); - - let z = poseidon.hash(&x, &y); - - // Compare the result to the expected output. You will need to know the expected output for your specific test case. - let expected_state0 = FieldElement::::from_hex( - "0x2fb6e1e8838d4b850877944f0a13340dd5810f01f5d4361c54b22b4abda3248", - ) - .unwrap(); - - assert_eq!(z, expected_state0); - } - - #[test] - fn test_hash_single() { - let params = PermutationParameters::new_with(DefaultPoseidonParams::CairoStark252); - - let poseidon = Poseidon::new_with_params(params); - - let x = FieldElement::::from_hex("0x9").unwrap(); - - let z = poseidon.hash_single(&x); - - // Compare the result to the expected output. You will need to know the expected output for your specific test case. - let expected_state0 = FieldElement::::from_hex( - "0x3bb3b91c714cb47003947f36dadc98326176963c434cd0a10320b8146c948b3", - ) - .unwrap(); - - assert_eq!(z, expected_state0); - } - - #[test] - fn test_hash_many() { - let params = PermutationParameters::new_with(DefaultPoseidonParams::CairoStark252); - - let poseidon = Poseidon::new_with_params(params); - - let a = FieldElement::::from_hex("0x1").unwrap(); - let b = FieldElement::::from_hex("0x2").unwrap(); - let c = FieldElement::::from_hex("0x3").unwrap(); - let d = FieldElement::::from_hex("0x4").unwrap(); - let e = FieldElement::::from_hex("0x5").unwrap(); - let f = FieldElement::::from_hex("0x6").unwrap(); - - let ins = vec![a, b, c, d, e, f]; - let z = poseidon.hash_many(&ins); - - // Compare the result to the expected output. You will need to know the expected output for your specific test case. - let expected_state0 = FieldElement::::from_hex( - "0xf50993f0797e4cc05734a47daeb214fde2d444ef6619a7c1f7c8e0924feb0b", - ) - .unwrap(); - assert_eq!(z, expected_state0); - - let ins = vec![a]; - let z = poseidon.hash_many(&ins); - let expected_state0 = FieldElement::::from_hex( - "0x579e8877c7755365d5ec1ec7d3a94a457eff5d1f40482bbe9729c064cdead2", - ) - .unwrap(); - assert_eq!(z, expected_state0); - - let ins = vec![a, b]; - let z = poseidon.hash_many(&ins); - let expected_state0 = FieldElement::::from_hex( - "0x371cb6995ea5e7effcd2e174de264b5b407027a75a231a70c2c8d196107f0e7", - ) - .unwrap(); - assert_eq!(z, expected_state0); - - let ins = vec![a, b, c]; - let z = poseidon.hash_many(&ins); - let expected_state0 = FieldElement::::from_hex( - "0x2f0d8840bcf3bc629598d8a6cc80cb7c0d9e52d93dab244bbf9cd0dca0ad082", - ) - .unwrap(); - assert_eq!(z, expected_state0); - - let ins = vec![a, b, c, d]; - let z = poseidon.hash_many(&ins); - let expected_state0 = FieldElement::::from_hex( - "0x26e3ad8b876e02bc8a4fc43dad40a8f81a6384083cabffa190bcf40d512ae1d", - ) - .unwrap(); - - assert_eq!(z, expected_state0); - } -} +pub use parameters::PoseidonCairoStark252; diff --git a/crypto/src/hash/poseidon/starknet/parameters.rs b/crypto/src/hash/poseidon/starknet/parameters.rs index 2b18a34713..ff73e7e6ba 100644 --- a/crypto/src/hash/poseidon/starknet/parameters.rs +++ b/crypto/src/hash/poseidon/starknet/parameters.rs @@ -1,71 +1,319 @@ -use lambdaworks_math::field::{element::FieldElement as FE, traits::IsPrimeField}; +use crate::hash::poseidon::PermutationParameters; +use lambdaworks_math::field::{ + element::FieldElement as FE, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, +}; -use crate::hash::poseidon::starknet::round_constants::ROUND_CONSTANTS_HEXSTRINGS; +impl PermutationParameters for PoseidonCairoStark252 { + type F = Stark252PrimeField; + const RATE: usize = 2; + const CAPACITY: usize = 1; + const ALPHA: u32 = 3; + const N_FULL_ROUNDS: usize = 8; + const N_PARTIAL_ROUNDS: usize = 83; -#[derive(Clone)] -pub struct PermutationParameters { - /// Exponent for the S box - pub alpha: u32, - pub n_full_rounds: usize, - pub n_partial_rounds: usize, - pub round_constants: Vec>>, - pub mds_matrix: Vec>>, - pub rate: usize, - pub capacity: usize, - pub state_size: usize, -} -pub enum DefaultPoseidonParams { - /// Poseidon as used by Cairo - /// with three inputs - CairoStark252, -} + const MDS_MATRIX: &'static [FE] = &PoseidonCairoStark252::MDS_MATRIX; + const N_MDS_MATRIX_ROWS: usize = 3; + const N_MDS_MATRIX_COLS: usize = 3; -/// Parameters for Poseidon -/// Mds constants and rounds constants should be used for the shared field, even if it technically can work for any field with the same configuration -impl PermutationParameters -where - F: IsPrimeField, -{ - pub fn new_with(params: DefaultPoseidonParams) -> Self { - match params { - DefaultPoseidonParams::CairoStark252 => Self::cairo_stark_params(), - } - } - - fn cairo_stark_params() -> PermutationParameters { - let round_constants: Vec>> = ROUND_CONSTANTS_HEXSTRINGS - .iter() - .map(|[x0, x1, x2]| { - [ - FE::::from_hex(x0).unwrap(), - FE::::from_hex(x1).unwrap(), - FE::::from_hex(x2).unwrap(), - ] - .to_vec() - }) - .collect(); + const ROUND_CONSTANTS: &'static [FE] = + &PoseidonCairoStark252::ROUND_CONSTANTS; + const N_ROUND_CONSTANTS_ROWS: usize = 91; + const N_ROUND_CONSTANTS_COLS: usize = 3; +} - let mds_matrix = [ - [FE::::from(3), FE::::from(1), FE::::from(1)].to_vec(), - [FE::::from(1), -FE::one(), FE::::from(1)].to_vec(), - [FE::::from(1), FE::::from(1), -FE::::from(2)].to_vec(), - ] - .to_vec(); +#[derive(Clone, Default)] +pub struct PoseidonCairoStark252; - const RATE: usize = 2; - const CAPACITY: usize = 1; - const ALPHA: u32 = 3; - const N_FULL_ROUNDS: usize = 8; - const N_PARTIAL_ROUNDS: usize = 83; - Self { - alpha: ALPHA, - n_full_rounds: N_FULL_ROUNDS, - n_partial_rounds: N_PARTIAL_ROUNDS, - round_constants, - mds_matrix, - rate: RATE, - capacity: CAPACITY, - state_size: RATE + CAPACITY, - } - } +impl PoseidonCairoStark252 { + const MDS_MATRIX: [FE; 3 * 3] = [ + FE::from_hex_unchecked("3"), + FE::from_hex_unchecked("1"), + FE::from_hex_unchecked("1"), + FE::from_hex_unchecked("1"), + FE::from_hex_unchecked("800000000000011000000000000000000000000000000000000000000000000"), + FE::from_hex_unchecked("1"), + FE::from_hex_unchecked("1"), + FE::from_hex_unchecked("1"), + FE::from_hex_unchecked("800000000000010ffffffffffffffffffffffffffffffffffffffffffffffff"), + ]; + // These constants can be found both in Jonathan's implementation + // https://github.com/xJonathanLEI/starknet-rs/blob/35c287e1a06e6ab68447f5f0b9df53f910960f57/starknet-crypto-codegen/src/poseidon/params.rs + // And the round 0 ones matches the one used + // in Cairo Lang + // https://github.com/starkware-libs/cairo-lang/blob/c98fc0b50529185b7018208cb3460191eeb53e0d/src/starkware/cairo/stark_verifier/air/layouts/starknet/autogenerated.cairo#L1574-L1596 + const ROUND_CONSTANTS: [FE; 3 * 91] = [ + FE::from_hex_unchecked("6861759ea556a2339dd92f9562a30b9e58e2ad98109ae4780b7fd8eac77fe6f"), + FE::from_hex_unchecked("3827681995d5af9ffc8397a3d00425a3da43f76abf28a64e4ab1a22f27508c4"), + FE::from_hex_unchecked("3a3956d2fad44d0e7f760a2277dc7cb2cac75dc279b2d687a0dbe17704a8309"), + FE::from_hex_unchecked("626c47a7d421fe1f13c4282214aa759291c78f926a2d1c6882031afe67ef4cd"), + FE::from_hex_unchecked("78985f8e16505035bd6df5518cfd41f2d327fcc948d772cadfe17baca05d6a6"), + FE::from_hex_unchecked("5427f10867514a3204c659875341243c6e26a68b456dc1d142dcf34341696ff"), + FE::from_hex_unchecked("5af083f36e4c729454361733f0883c5847cd2c5d9d4cb8b0465e60edce699d7"), + FE::from_hex_unchecked("7d71701bde3d06d54fa3f74f7b352a52d3975f92ff84b1ac77e709bfd388882"), + FE::from_hex_unchecked("603da06882019009c26f8a6320a1c5eac1b64f699ffea44e39584467a6b1d3e"), + FE::from_hex_unchecked("4332a6f6bde2f288e79ce13f47ad1cdeebd8870fd13a36b613b9721f6453a5d"), + FE::from_hex_unchecked("53d0ebf61664c685310a04c4dec2e7e4b9a813aaeff60d6c9e8caeb5cba78e7"), + FE::from_hex_unchecked("5346a68894845835ae5ebcb88028d2a6c82f99f928494ee1bfc2d15eaabfebc"), + FE::from_hex_unchecked("550a9e24176509ea7631ccaecb7a4ab8694ab61f238797098147e69dd91e5a3"), + FE::from_hex_unchecked("219dcccb783b1cbaa62773fedd3570e0f48ad3ed77c8b262b5794daa2687000"), + FE::from_hex_unchecked("4b085eb1df4258c3453cc97445954bf3433b6ab9dd5a99592864c00f54a3f9a"), + FE::from_hex_unchecked("53e8a8e8a404c503af2bf3c03e420ea5a465939d04b6c72e2da084e5aabb78d"), + FE::from_hex_unchecked("5ca045c1312c09d1bd14d2537fe5c19fb4049cb137faf5df4f9ada962be8ca8"), + FE::from_hex_unchecked("7c74922a456802c44997e959f27a5b06820b1ed97596a969939c46c162517f4"), + FE::from_hex_unchecked("c0bba6880d2e686bf5088614b9684ff2526a20f91670435dc6f519bb7ab83f"), + FE::from_hex_unchecked("4526bcaec43e8ebd708dd07234c1b2dc1a6203741decd72843849cd0f87934a"), + FE::from_hex_unchecked("1cc9a17b00d3607d81efaea5a75a434bef44d92edc6d5b0bfe1ec7f01d613ed"), + FE::from_hex_unchecked("28b1e269b84c4012aa8cdbead0bc1ce1eb7284e2b28ed90bc7b4a4fde8f01f"), + FE::from_hex_unchecked("62af2f41d76c4ad1d9a2482fbdaf6590c19656bcb945b58bb724dc7a994498d"), + FE::from_hex_unchecked("5cfd7e44946daa6b2618213b0d1bf4a2269bed2dc0d4dbf59e285eee627df1a"), + FE::from_hex_unchecked("7ff2afb40f3300856fdd1b94da8d3bbcf0312ab9f16ac9bc31955dc8386a747"), + FE::from_hex_unchecked("5cd236bdc15b54183e90bab8ae37f8aab40efae6fa9cd919b3248ee326e929c"), + FE::from_hex_unchecked("5463841390e22d60c946418bf0e5822bd999084e30688e741a90bbd53a698a"), + FE::from_hex_unchecked("24c940fff3fe8c8b2021f13eb4d71747efd44a4e51890ae8226e7406144f805"), + FE::from_hex_unchecked("4e50cb07b3873268dc88f05393d9d03153ca4c02172dd1d7fc77d45e1b04555"), + FE::from_hex_unchecked("62ca053e4da0fc87b430e53238d2bab1d9b499c35f375d7d0b32e1189b6dcb5"), + FE::from_hex_unchecked("719f20ac59d1ebcaaf37fe0b851bc2419cd89100adff965951bff3d3d7e1191"), + FE::from_hex_unchecked("7645ca5e87a9f916a82fe5bb90807f44050ac92ca52f5c798935cf47d55a8fd"), + FE::from_hex_unchecked("15b8aeaca96ab53200eed38d248ecda23d4b71d17133438015391ca63663767"), + FE::from_hex_unchecked("53d94dbbca7cb2aa8252f106292ac3b98799e908f928c196c1b658bf10b2e2"), + FE::from_hex_unchecked("28f90b403e240f1c6f4c0a3b70edbb3942b447c615c0f033913831c34de2d1e"), + FE::from_hex_unchecked("2485167dc233ba6e1161c4d0bf025159699dd2feb36e3e5b70ae6e770e22081"), + FE::from_hex_unchecked("1c8b08a90d6ee46ff7de548541dd26988f7fdaacdd58698e938607a5feca6e8"), + FE::from_hex_unchecked("105c3bf5cba256466b75e79d146f9880c7c4df5ecdad643ce05b16901c4881e"), + FE::from_hex_unchecked("238019787f4cc0b627a65a21bef2106d5015b85dfbd77b2965418b02dbc6bd7"), + FE::from_hex_unchecked("15e624d7698fdf9b73dce29a5f24c465c15b52dec8172923a6ebc99a6ddc5e1"), + FE::from_hex_unchecked("5d3688ba56f34fdf56bc056ad8bf740ca0c2efef23b04a479f612fde5800a0a"), + FE::from_hex_unchecked("229abdef3fef7ae9e67ed336e82dc6c2e26d872d98b3cce811c69ae363b444d"), + FE::from_hex_unchecked("3e8096ecfcbcde2ee400801a56f236db2c43d1e33c92b57ac58daf2d3fc44db"), + FE::from_hex_unchecked("3ad5fec670d7039108d605aae834c7ce6a7cd4e1b47bf6a02265352c57db9bd"), + FE::from_hex_unchecked("7cf4598c0cf143875877afdbb4df6794ef597fff1f98557adca32046aeaef0a"), + FE::from_hex_unchecked("58aecc0081b55134a4d1c4c8f27932e4170c37841fef49aca0ec7a123c00ad6"), + FE::from_hex_unchecked("757b4b7ee98e0a15460b71995790396e4ef3c859db5b714ec09308d65d2ca61"), + FE::from_hex_unchecked("6b82800937f8981f3cd974f43322169963d2b54fd2b7ed348dc6cc226718b5d"), + FE::from_hex_unchecked("3a915b1814707273427e34ab8fbb7ca044f14088fedae9606b34a60b1e9c64"), + FE::from_hex_unchecked("54afbf1bd990043f9bc01028ff44195c0bb609d367b76269a627689547bfbef"), + FE::from_hex_unchecked("5e1ceb846fe1422b9524c7d014931072c3852df2d991470b08375edf6e762bb"), + FE::from_hex_unchecked("7f751f98968212ebe5dff3ce06e8cb916709e0c48e3020c6b2b01c1bec0814b"), + FE::from_hex_unchecked("36f6b64463f7c29fc3180616e340536bea7f01d226b68b6d45cd6dfbff811e4"), + FE::from_hex_unchecked("61135c9846faf39b4511d74fe8de8b48dd4d0e469d6703d7ed4fe4fe8e0dbac"), + FE::from_hex_unchecked("b58921a3fbdbb559b78f6acfca9a21a4ba83cc6e0ae3527fbaad907fc912b8"), + FE::from_hex_unchecked("22a4f8a5cdc7474b9d16b61c2973847211d84eb2fb27b816e52821c2e2b1b1e"), + FE::from_hex_unchecked("41cf6db5d6145edfeccbbc9a50b2ceedeb1765c61516ffcb112f810ad67036f"), + FE::from_hex_unchecked("be44689973db2b1cfc05fa8f4aec6fac6a0ff2fdfab744ade9de11416b6831"), + FE::from_hex_unchecked("39bf209c4e117e16489cda45128096d6d148a237142dc4951df0b8239be148b"), + FE::from_hex_unchecked("209cf541e5f74fc2b93310b8ce37b092a58282643860b5707c7eb980ea03a06"), + FE::from_hex_unchecked("6b562e6005f34ee0bdc218ba681b6ba7232e122287036d18c22dd5afa95326d"), + FE::from_hex_unchecked("e8103a23902be5dc6d5f59253a627a2a39c8aca11a914670e7a35dea38c8f"), + FE::from_hex_unchecked("6a3725548c664fd06bdc1b4d5f9bed83ef8ca7468d68f4fbbf345de2d552f72"), + FE::from_hex_unchecked("67fcd6997472e8e605d0f01a8eccc5f11a45c0aa21eb4ebb447b4af006a4a37"), + FE::from_hex_unchecked("26144c95c8de3634075784d28c06c162a44366f77792d4064c95db6ecb5cff0"), + FE::from_hex_unchecked("5b173c8b0eb7e9c4b3a874eb6307cda6fd875e3725061df895dc1466f350239"), + FE::from_hex_unchecked("7e1c2d6fde8ac9f87bae06ad491d391c448f877e53298b6370f2165c3d54ddb"), + FE::from_hex_unchecked("4db779f3e5b7424996f451b156fe4e28f74d61e7771f9e3fa433b57ca6627a9"), + FE::from_hex_unchecked("bb930d8a6c6583713435ec06b6fed7825c3f71114acb93e240eed6970993dd"), + FE::from_hex_unchecked("4472d73b2830565d708467e9296fb5599d3a08814c31c4189e9579c046e878f"), + FE::from_hex_unchecked("7ba9c303dfee2d89e10e3c883ca5ce5614d23739b7cb2052cc23612b11170e2"), + FE::from_hex_unchecked("21c0e3319ede47f0425dc9b2c1ed30e6356cb133e97579b822548eb9c4dc4b7"), + FE::from_hex_unchecked("2cfd61139e50ddd37b09933816e2a0932e53b7dc4f4947565c1d41e877eb191"), + FE::from_hex_unchecked("5abea18941a4976844544d92ee0eca65bdd10b3f170b0dc2f30acd37e26d8e7"), + FE::from_hex_unchecked("77088fdb015c7947a6265e44fef6f724ea28ae28b26e6eee5a751b7ce6bcc21"), + FE::from_hex_unchecked("3abdc9d677231325b3e3c43cfd443076b4ce33cddbc8446120dce84e6122b73"), + FE::from_hex_unchecked("2250f430b7fe7d12e5d00b6b83e52a52ca94879ccfab81a7a602662c2d62c4d"), + FE::from_hex_unchecked("5c92ef479c11bb51fb24ef76d57912b12660e7bd156d6cabbb1efb79a25861b"), + FE::from_hex_unchecked("235ec597391648b510f616fa8b87900fd08fd4208a785cffcf784a63a0fd5c6"), + FE::from_hex_unchecked("4ed4e872eb7e736207be77e9d11e38f396b5c0ba3376e855523c00b372cc668"), + FE::from_hex_unchecked("5f9406febca3879b756ef3f6331890b3d46afa705908f68fb7d861c4f275a1b"), + FE::from_hex_unchecked("1d9c501d9ff1fba621a9f61b68873c05f17b0384661f06d97edf441abdaa49d"), + FE::from_hex_unchecked("4b0de22bbd0a58534982c8e28d2f6e169e37ba694774c4dfa530f41c535952e"), + FE::from_hex_unchecked("1b4d48bd38a3f8602186aabb291eca0d319f0e3648b2574c49d6fd1b033d903"), + FE::from_hex_unchecked("7558bbea55584bf1725d8aa67ddba626b6596bbd2f4e65719702cefcead4bab"), + FE::from_hex_unchecked("1108f1a9500a52f561ea174600e266a70b157d56ece95b60a44cf7a3eef17be"), + FE::from_hex_unchecked("8913d96a4f36b12becb92b4b6ae3f8c209fb90caab6668567289b67087bf60"), + FE::from_hex_unchecked("6502262c51ad8f616926346857dec8cca2e99f5742b6bf223f4d8a6f32867a6"), + FE::from_hex_unchecked("7cb5fcdc00892812889280505c915bde962ea034378b343cd3a5931d2ec0e52"), + FE::from_hex_unchecked("2eb919524a89a26f90be9781a1515145baea3bc96b8cd1f01b221c4d2a1ce87"), + FE::from_hex_unchecked("58efb6272921bc5eada46635e3567dced0662c0161223e3c1c63e8de3ec3d73"), + FE::from_hex_unchecked("62fcd49ca9c7587b436d205ffc2a39594254a1ac34acd46d6955e7844d4f88e"), + FE::from_hex_unchecked("635895330838846e62d9acce0b625f885e5941e54bd3a2106fcf837aef5313b"), + FE::from_hex_unchecked("7da445b81e9b3d36d47a5f4d23b92a378a17f119d5e6e70629f8b41fefb12e3"), + FE::from_hex_unchecked("2b22dab62f0817e9fc5737e189d5096a9027882bef1738943b7016256118343"), + FE::from_hex_unchecked("1af01472348f395bacdfed1d27664d0d5bdea769be8fcb8fbef432b790e50d5"), + FE::from_hex_unchecked("76b172dbbeec5a31de313b9390f79ec9284163c8e4986bc5b682e5ac6360309"), + FE::from_hex_unchecked("70efaeae36f6af0f362f6cb423d2009b30ddb4178d46def0bdb2905b3e0862"), + FE::from_hex_unchecked("6cb99b36e521ac0a39872686b84ee1d28c4942b8036a1c25a0e4117ccaeedf"), + FE::from_hex_unchecked("29fd44305a5a9a70bbf9674e544bda0fb3d0fe5bb3aa743fd1b8a4fc1dc6055"), + FE::from_hex_unchecked("6b447ded1046e83629b184d8c36db3a11a6778d8848142aa6363d6619f9764"), + FE::from_hex_unchecked("642a8b4be4ba812cbfcf55a77339b5d357cceb6946fdc51c14b58f5b8989b59"), + FE::from_hex_unchecked("489e0a26f65a1eecc6cc6aa5b6e775cbc51a73700bd794a7acd79ae1d95882a"), + FE::from_hex_unchecked("3b19d4ef195975bbf78ab5dc2fd1d24816428f45a06293c1b9d57b9a02e9200"), + FE::from_hex_unchecked("7d2dd994756eacba576b74790b2194971596f9cd59e55ad2884c52039013df5"), + FE::from_hex_unchecked("1922810cc08f50bf300df869823b9f18b3327e29e9e765002970ef0f2e8c5f3"), + FE::from_hex_unchecked("52f3afaf7c9102f1d46e1d79a70745b39c04376aafff05771cbd4a88ed418ac"), + FE::from_hex_unchecked("7ccfc88e44a0507a95260f44203086e89552bbe53dcc46b376c5bcab6ea788e"), + FE::from_hex_unchecked("2949125939e6ad94100228beff83823f5157dd8e067bc8819e40a1ab008dd9c"), + FE::from_hex_unchecked("6cb64e3a0d37a6a4273ce4ee6929ba372d6811dde135af4078ba6e1912e1014"), + FE::from_hex_unchecked("d63b53707acf8962f05f688129bf30ad43714257949cd9ded4bf5953837fae"), + FE::from_hex_unchecked("bcb1549c9cabb5d13bb968b4ea22d0bb7d7460a6965702942092b32ef152d4"), + FE::from_hex_unchecked("3d1c5233657ce31f5ead698fe76f6492792a7205ba0531a0ca25b8d8fe798c1"), + FE::from_hex_unchecked("2240b9755182ee9066c2808b1e16ea448e26a83074558d9279f450b79f97516"), + FE::from_hex_unchecked("cc203d8b0f90e30fe8e54f343cef59fe8d70882137de70c9b43ab6615a646c"), + FE::from_hex_unchecked("310c6cc475d9346e061bacdc175ea9e119e937dea9d2100fa68e03c1f77910b"), + FE::from_hex_unchecked("7f84b639f52e57420bc947defced0d8cbdbe033f578699397b83667049106c7"), + FE::from_hex_unchecked("584ca7f01262c5bd89c4562f57139f47e9f038cb32ec35abe4e1da8de3e164a"), + FE::from_hex_unchecked("1135eefaf69b6e4af7d02f562868be3e02fdc72e01e9510531f9afa78abbbde"), + FE::from_hex_unchecked("372082b8a6c07100a50a3d33805827ad350c88b56f62c6d36a0d876856a99e8"), + FE::from_hex_unchecked("7c3c12b819a8aad87499bac1a143fc59674f132e33898f0c119e3d12462dfe6"), + FE::from_hex_unchecked("4f1354c51e8f6905b84157cfeff6822c056ce9e29d602eb46bd9b75a23836cf"), + FE::from_hex_unchecked("2da9f26a8271659075739ba206507a08ac360150e849950ef3973548fbd2fca"), + FE::from_hex_unchecked("287173956a2beb111b5ec29195e38cc3f6a65ff50801aa75fd78dd550702843"), + FE::from_hex_unchecked("7273101c190ff64212420095a51c8411c7f3227f6a7a4a64ae6ba7f9201e126"), + FE::from_hex_unchecked("2dbf2a6b56b26d23ebeb61e500687de749b03d3d349169699258ee4c98005fc"), + FE::from_hex_unchecked("85b6cbb29739a6808e67f00ab89b52ab89ef8d92530394e4b910efd706c7fb"), + FE::from_hex_unchecked("3d55b5f1171efda1dacbcbadfd5b910b493fa9589fd937e3e06ce26b08925a3"), + FE::from_hex_unchecked("aaedaa6ef2fa707d16b3b295410c0e44f7a2f8135c207824f6ae2a9b16e90c"), + FE::from_hex_unchecked("6aca6ebf70b1cb46c6331e9f1a5c4cc89b80f8adc5d18915c1cd0d496ccf5e1"), + FE::from_hex_unchecked("1678602af36c28abb010f831d403d94d5e90003e6d37c677e9dd157fb27761"), + FE::from_hex_unchecked("2022036bdf687f041b547fefdf36d4c2cd3f4b0526a88aafe60a0a8f508bad2"), + FE::from_hex_unchecked("7bfc350957c968ca664397414bdfb8f9b8dfe49fb63e32353d4e2e8d1d4af6"), + FE::from_hex_unchecked("2d639cbd418cb9fc24ea29ccd1d15ab81f43a499b27a06d3c5e2176f7ad79af"), + FE::from_hex_unchecked("ecdea7f959a4d488403d5b39687a1fe0dee3369e5fbc0f4779569f64506e0c"), + FE::from_hex_unchecked("3f656bdc4fefd92b70658e2f1992ef9f22e5f2d28c490e21d4e34357154b558"), + FE::from_hex_unchecked("d1b8cb1561eed32319638ccab9033dfec47596f8a6f4ce6594e19fddd59254"), + FE::from_hex_unchecked("758ffc77c62e3e0f86ef6ea01545ad76f281ec2941da7222d1e8b4e2ec1f192"), + FE::from_hex_unchecked("20315ca079570df995386e96aeaa1b4596aacd28f83c32f29a591c95e6fcac5"), + FE::from_hex_unchecked("3e55cf341e7c280cb05f3d6ff9c8d9f2cfe76b84a9d1b0f54884b316b740d8d"), + FE::from_hex_unchecked("4d56feb32cde74feede9749739be452e92c029007a06f6e67c81203bf650c68"), + FE::from_hex_unchecked("4ee807aa678a9a433b6171eaa6a2544497f7599fb8145d7e8089f465403c89b"), + FE::from_hex_unchecked("25d2bacc8f1ee7548cb5f394de2cb6e1f365e56a1bc579d0f9a8ad2ef2b3821"), + FE::from_hex_unchecked("5f573de597ce1709fc20051f6501268cd4b278811924af1f237d15feb17bd49"), + FE::from_hex_unchecked("30297c3c54a505f5826a280e053cf7a3c1e84a1dcf8b33c682cf85ddac86deb"), + FE::from_hex_unchecked("2f5e9c47c9a86e043c7526a59783f03c6bc79b69b8709fe6a052b93a8339ae8"), + FE::from_hex_unchecked("1bf75c7a739da8d29f9c23065ff8ccb1da7deec83e130bcd4a27a416c72b84b"), + FE::from_hex_unchecked("60563d5f852ae875989017bd5c4cfdc29cd27fc4e91eeabdb8e864df3c3c675"), + FE::from_hex_unchecked("7a4b1d70885aa820969635468daec94f8156c20e3131bd71005be1cd16ccf9e"), + FE::from_hex_unchecked("347bb025695e497f1e201cd62aa4600b8b85cf718cd1d400f39c10e59cc5852"), + FE::from_hex_unchecked("6783ab1e1ef97bb9e7f9381eb6ab0de2c4c9c2de413691ba8aa666292e9e217"), + FE::from_hex_unchecked("133e0280c6de90e7b3870a07823c081fd9c4cb99d534debd6a7bfb4e5b0dd46"), + FE::from_hex_unchecked("865d450ce29dc42fb5db72460b3560a2f093695573dff94fd0216eb925beec"), + FE::from_hex_unchecked("1de023f840e054a35526dabacf0dee948efba06bcbb414ecd81a6b301664e57"), + FE::from_hex_unchecked("55fc1e341bfdf7805015a96f724c5ac7cc7b892a292d38190631ab1a5388c4"), + FE::from_hex_unchecked("2df6557bfd4a4e7e7b27bf51552d2b5162706a3e624faca01a307ef8d532858"), + FE::from_hex_unchecked("113a8a66962ce08d92a6bd3e9c1d55ef8f226da95e4d629046d73d0507f6271"), + FE::from_hex_unchecked("271577d6ee9fa377f2c889874ba5b44ca1076033db5c2de4f3367b08c008e53"), + FE::from_hex_unchecked("3396b33911219b6b0365c09348a561ef1ccb956fc673bc5291d311866538574"), + FE::from_hex_unchecked("1e1392f2da08549c8a7d89e899189306170baa3c3436e6a5398f69c8f321636"), + FE::from_hex_unchecked("661545081032013df118e1d6e7c61a333e313b1a9a5b6d69c876bd2e7d694ca"), + FE::from_hex_unchecked("6b14294e71cd7fb776edbd432d20eb8f66d00533574e46573516f0cacdeec88"), + FE::from_hex_unchecked("7252fbbb06c2848338b1c41df31e4e51fe2a18e2406c671915cab6eb1a1d4f2"), + FE::from_hex_unchecked("3ccf71be7cc2a9abcf5a09807c69679430c03645747621b7f5327cb00ff99da"), + FE::from_hex_unchecked("29778dc707504fa6a9f7c97b4ceef0a9b39001d034441617757cd816dac919a"), + FE::from_hex_unchecked("39473f6f06bb99e33590d34e3bae36e491f7bbf86a26aa55a8f5b27bb98d4c5"), + FE::from_hex_unchecked("7ba7c32f875b71b895caa0215f996fd4ad92bab187e81417063dde91c08c027"), + FE::from_hex_unchecked("37c1367e49cbfc403b22aac82abf83b0ed083148a5f4c92839e5d769bdab6b6"), + FE::from_hex_unchecked("5c9eb899931d2f4b53ffcf833cdfa05c2068375ff933eb37ae34157c0b2d951"), + FE::from_hex_unchecked("5f6054a4d48698ec27772fb50a7d2e5c1557ffdc1ffd07331f2ca26c6e3b661"), + FE::from_hex_unchecked("20e6d62a2fe0fe9b0fab83e8c7d1e8bfd0fec827960e40a91df64664dcd7774"), + FE::from_hex_unchecked("6290a56a489ad52120c426fe0e409c2ff17adf51f528cafb0d026d14ffd6aac"), + FE::from_hex_unchecked("3703f16f990342c2267a6f7ece342705a32ca4c101417286279f6fc315edc7c"), + FE::from_hex_unchecked("5194962daf6679b9a0c32b5a9a307ba92e2c630f70e439195b680dd296df3fd"), + FE::from_hex_unchecked("e8eae20a79a7c1242c34617b01340fb5fd4bea2aa58b98d2400d9b515ee5e2"), + FE::from_hex_unchecked("369058169d63091ae28bfb28def7cd8d00dd7c2894fae4ffec65242afa5cd45"), + FE::from_hex_unchecked("418c963bc97195a74077503ee472f22cfdff0973190ab189c7b93103fd78167"), + FE::from_hex_unchecked("68d07a3eefc78dc5b28b3f4dc93167fb8c97112d14a25b4d4db559720156386"), + FE::from_hex_unchecked("517e892228df2d4f15a3c4241c98ba25ba0b5557375003f8748583a61836372"), + FE::from_hex_unchecked("5cc0f0f6cf9be94a150116e7932f8fe74ac20ad8100c41dc9c99538792e279b"), + FE::from_hex_unchecked("53d5d7863434c6629bdb1f8a648e4820883543e821f0f5c1668884c0be41ec8"), + FE::from_hex_unchecked("a158126b89e6b0a600bf53f8101707b072218912dd0d9df2528f67de24fdf5"), + FE::from_hex_unchecked("6b53b807265387ee582069a698323d44c204bed60672b8d8d073bed2fede503"), + FE::from_hex_unchecked("1097fb448406b7a6de0877efd58c01be53be83bde9601a9acc9e0ca2091fda0"), + FE::from_hex_unchecked("cbc0ff7239d3763902396389d67b3049ce1fefde66333ce37ca441f5a31bec"), + FE::from_hex_unchecked("79a3d91dd8a309c632eb43d57b5c5d838ceebd64603f68a8141ebef84280e72"), + FE::from_hex_unchecked("23fb472fe575135300f74e8f6de8fe1185078218eceb938900e7598a368db9"), + FE::from_hex_unchecked("7ac73134016d2a8a4c63a6b9494c0bd7a6ba87cc33e8a8e23ebda18bfb67c2a"), + FE::from_hex_unchecked("19a16068c3eac9c03f1b5c5ee2485ccc163d9ab17bb035d5df6e31c3dcf8f14"), + FE::from_hex_unchecked("1f24b4356a6bbfd4d4ef9fd1634752820ee86a925725ac392134d90def073ea"), + FE::from_hex_unchecked("3e44e7f7aeea6add59b6b4d11c60a528fb70727f35d817305971592333d36"), + FE::from_hex_unchecked("5f93b02f826741414535a511ed3eb4fe85987ae57bc9807cbd94cd7513d394e"), + FE::from_hex_unchecked("f0a0a88db99247d71c3d51d4197fa3fd1cc76e670607e35ca2d3bada29523a"), + FE::from_hex_unchecked("3432226916d31f3acac1e211431fd4cd2b6f2e80626af6564bdde3e77608db0"), + FE::from_hex_unchecked("55625941bfea6f48175192845a7ad74b0b82940ef5f393ca3830528d59cf919"), + FE::from_hex_unchecked("ddf48695b204477dfe4f8cb3ef1b39783e9b92f9276b858e2e585e318e20a4"), + FE::from_hex_unchecked("260730a657ff8f38851a679ab2a1490434ee50d4953e7c5d3194578b08ae8e3"), + FE::from_hex_unchecked("4cfd231373aa46d96283840bdb79ba6d7132775b398d324bcd206842b961aa9"), + FE::from_hex_unchecked("3203843c41cd453f14fa0bc0b2191a27ebc659e74fd48f981e963de57eff25d"), + FE::from_hex_unchecked("2c2f6ae5624d1fb8435d1c86bf76c260f5e77a54b006293705872e647cc46"), + FE::from_hex_unchecked("780225456e63903b3e561384ef2e73a85b0e142b69752381535022014765f06"), + FE::from_hex_unchecked("7f602ec1a80a051fd21b07f8e2960613082fc954b9a9ff641cc432a75c81887"), + FE::from_hex_unchecked("62561b0a0a72239b60f6aaf7022b7d323fe77cd7c1ab432f0c8c118ca7e6bca"), + FE::from_hex_unchecked("604fe5a6a22344aa69b05dea16b1cf22450c186d093754cb9b84a8a03b70bc8"), + FE::from_hex_unchecked("1cf9987a4044716d3dc140bf5f9b76f6eada5995905189f8682eaf88aef2b7b"), + FE::from_hex_unchecked("6bc0b2487c1eece3db47a4bdd60cf69debee233e91b50e9ee42ce22cbfbacbf"), + FE::from_hex_unchecked("2f5dbb5055eb749a11403b93e90338b7620c51356d2c6adcbf87ab7ea0792e6"), + FE::from_hex_unchecked("446328f4dddae6529743c43883d59c45f63b8a623a9cf318489e5fc4a550f61"), + FE::from_hex_unchecked("4ba30c5240cde5bca6c4010fb4b481a25817b43d358399958584d2c48f5af25"), + FE::from_hex_unchecked("5f5275f76425b15c89209117734ae85708351d2cf19af5fe39a32f89c2c8a89"), + FE::from_hex_unchecked("576f3b5156f4763e18c7f98df3b2f7b993cdda4eb8cb92415e1be8e6af2fc17"), + FE::from_hex_unchecked("11dc3f15cba928aed5a44b55a5b026df84a61719ed5adbb93c0e8e12d35ef3d"), + FE::from_hex_unchecked("44c40e6bd52e91ad9896403ae4f543ae1c1d9ea047d75f8a6442b8feda04dca"), + FE::from_hex_unchecked("1836d733a54013ebd0ccbf4974e80ac1954bf90fe9ea4e2c914ad01166026d8"), + FE::from_hex_unchecked("3c553be9776b628a8159d306ef084727611df8037761f00f84ca02ce731b3ac"), + FE::from_hex_unchecked("6ce94781c1a23fda1c7b87e0436b1b401ae11a6d757843e342f5017076a059"), + FE::from_hex_unchecked("381ec71fbdef3160253be9f00f4e6b9e107f457812effb7371cc2daa0acd0ed"), + FE::from_hex_unchecked("1844da9cc0eeadc6490d847320d9f3cd4fb574aa687bafdfe0ffa7bf2a8f1a1"), + FE::from_hex_unchecked("7a8bf471f902d5abb27fea5b401483dedf97101047459682acfd7f9b65a812f"), + FE::from_hex_unchecked("633b6fb004de62441915fb51ac174456f5a9cdff7aecb6e6b0d063839e56327"), + FE::from_hex_unchecked("179ee5cec496194771200382bfc6d17bbe546ba88fed8b17535fd70fbc50ab6"), + FE::from_hex_unchecked("2806c0786185986ea9891b42d565256b0312446f07435ac2cae194330bf8c42"), + FE::from_hex_unchecked("438703d948708ae90c7a6b8af194b8b603bb2cdfd26bfa356ac9bb6ee041393"), + FE::from_hex_unchecked("24446628f56029d7153bd3a482b7f6e1c56f4e02225c628a585d58a920035af"), + FE::from_hex_unchecked("4c2a76e5ce832e8b0685cdeeea3a253ae48f6606790d817bd96025e5435e259"), + FE::from_hex_unchecked("78a23323520994592933c079b148aed57d5e4ce1ab122d370983b8caa0e0300"), + FE::from_hex_unchecked("79ca6c5e1025b2151144ea5937dd07cadce1aa691b19e6db87070ba51ec22c0"), + FE::from_hex_unchecked("6b2e4a46e37af3cf952d9d34f8d6bd84a442ebfd1ac5d17314e48922af79c5d"), + FE::from_hex_unchecked("305d6cd95cc2eab6805d93d3d8d74e1ca7d443f11e34a18e3529e0d03435c2"), + FE::from_hex_unchecked("6097b4b8b90db14b39743ed23f8956cabb7aea70cc624a415c7c17b37fbf9a9"), + FE::from_hex_unchecked("64e1b3f16c26c8845bdb98373e77dad3bdcc90865b0f0af96288707c18893f"), + FE::from_hex_unchecked("649fafe673f21e623384d841221b73421c56014af2ffdf57f1579ae911fd335"), + FE::from_hex_unchecked("7d806dccbf1a2696b294404e849722f2baa2f4d19005a49d1ba288a77fefe30"), + FE::from_hex_unchecked("5951a37da53e3bbc0b3e2db1a9a235d7a03f48f443be6d659119c44aafc7522"), + FE::from_hex_unchecked("6d87fa479fb59524d1912c3554ae3d010496a31bdacb542c816a1607a907731"), + FE::from_hex_unchecked("1451cccd4200fa9d473ad73466b4e8c0a712a0b12bb6fc9462a3ac892acc9b2"), + FE::from_hex_unchecked("3ca1b6400b3e51007642535f1ca9b03832ca0faa15e1c4ed82dd1efdc0763da"), + FE::from_hex_unchecked("52c55735b2f0a6560ad1516a8f13592b0dd024ff4162539f993a99c7a1a4d95"), + FE::from_hex_unchecked("7e04de60aa80132f0149d1dee29617de750bd5ce3e9fa5e62951d65f6b924cd"), + FE::from_hex_unchecked("271784e6920a68e47c4c8fab71c8f8303ef29e26f289223edf63291c0a5495"), + FE::from_hex_unchecked("5c7c19061a84d5960a04b8f0adaa603c8afe93f17b7f0e56b49514af43d0c69"), + FE::from_hex_unchecked("172db5affe783af419da337cb79061e090943c2959dea1b38e4436f5482eafe"), + FE::from_hex_unchecked("518b7975a6d8d310eac9fe4082916f021a7ecbadf18809746a9e061a2cb9456"), + FE::from_hex_unchecked("20c5539dc45dd56d4bbc2440a9f5061d74b8ae5e37b34e8755a0315f1e196db"), + FE::from_hex_unchecked("1ea6f5fb309fa4a08bc7d516e80efc3a977b47208283cf35a9d8bc213b90b14"), + FE::from_hex_unchecked("50ce323c5128dc7fdd8ddd8ba9cfe2efd424b5de167c7257d1f766541e29ded"), + FE::from_hex_unchecked("401e37d0e276547695538b41d3c28215b865f5b7d1b497a8919284c613cb7d8"), + FE::from_hex_unchecked("645a0de30acc3117f2893056fc5880255daa12cc61261cc0fab9cf57c57397b"), + FE::from_hex_unchecked("69bc3841eb0a310d9e988d75f09f698d4fdc9d0d69219f676b66ae7fa3d495b"), + FE::from_hex_unchecked("2684bbe315ad2c4bdd47c38fe72db47cf0ae0c455cda5484baf523f136bdc6"), + FE::from_hex_unchecked("11e0f83c547ca5c68202e8d34e5595a88858c2afa664365e4acb821fd8a13ee"), + FE::from_hex_unchecked("4af4a7635f8c7515966567ceec34315d0f86ac66c1e5a5ecac945f1097b82ef"), + FE::from_hex_unchecked("4fba58cf8aaf4893cb7158908ccc18b1dc48894d2bb46225c72b11f4c74b271"), + FE::from_hex_unchecked("397c4c169115b468cc90da2e664f8c29a7f89be0ead679a38b0f44c8a2a0e20"), + FE::from_hex_unchecked("6563b9ebb6450dbad397fa5dd13c501f326dd7f32be22e20998f59ec7bacff"), + FE::from_hex_unchecked("376edb238f7b630ea81d307f4c79f9afec48562076dd09c36cd79e9cb817165"), + FE::from_hex_unchecked("60d4208bb50eb15f29ed22addcd50a1b337504039690eb858584cda96e2e061"), + FE::from_hex_unchecked("6a37d569d2fbc73dbff1019dc3465ec0f30da46918ab020344a52f1df9a9210"), + FE::from_hex_unchecked("d3b174c7290c6bf412083ff35d23821dc512f1df073c1b429130371ac63b1a"), + FE::from_hex_unchecked("226ed3d763477454b46eb2a5c3b814634d974919689fb489fe55e525b980373"), + FE::from_hex_unchecked("5f3997e7dafcb2de0e7a23d33d2fd9ef06f4d79bd7ffa1930e8b0080d218513"), + FE::from_hex_unchecked("7c5eec716d94634434df335a10bbac504f886f7f9d3c1648348c3fae8fdf14d"), + FE::from_hex_unchecked("53cc30d7fe0f84e7e24fd22c0f9ad68a89da85553f871ef63d2f55f57e1a7c"), + FE::from_hex_unchecked("368821ee335d71819b95769f47418569474a24f6e83b268fefa4cd58c4ec8fa"), + FE::from_hex_unchecked("5334f75b052c0235119816883040da72c6d0a61538bdfff46d6a242bfeb7a1"), + FE::from_hex_unchecked("5d0af4fcbd9e056c1020cca9d871ae68f80ee4af2ec6547cd49d6dca50aa431"), + FE::from_hex_unchecked("30131bce2fba5694114a19c46d24e00b4699dc00f1d53ba5ab99537901b1e65"), + FE::from_hex_unchecked("5646a95a7c1ae86b34c0750ed2e641c538f93f13161be3c4957660f2e788965"), + FE::from_hex_unchecked("4b9f291d7b430c79fac36230a11f43e78581f5259692b52c90df47b7d4ec01a"), + FE::from_hex_unchecked("5006d393d3480f41a98f19127072dc83e00becf6ceb4d73d890e74abae01a13"), + FE::from_hex_unchecked("62c9d42199f3b260e7cb8a115143106acf4f702e6b346fd202dc3b26a679d80"), + FE::from_hex_unchecked("51274d092db5099f180b1a8a13b7f2c7606836eabd8af54bf1d9ac2dc5717a5"), + FE::from_hex_unchecked("61fc552b8eb75e17ad0fb7aaa4ca528f415e14f0d9cdbed861a8db0bfff0c5b"), + ]; } diff --git a/crypto/src/hash/poseidon/starknet/round_constants.rs b/crypto/src/hash/poseidon/starknet/round_constants.rs deleted file mode 100644 index a592b6166b..0000000000 --- a/crypto/src/hash/poseidon/starknet/round_constants.rs +++ /dev/null @@ -1,463 +0,0 @@ -// These constants can be found both in Jonathan's implementation -// https://github.com/xJonathanLEI/starknet-rs/blob/35c287e1a06e6ab68447f5f0b9df53f910960f57/starknet-crypto-codegen/src/poseidon/params.rs -// And the round 0 ones matches the one used -// in Cairo Lang -// https://github.com/starkware-libs/cairo-lang/blob/c98fc0b50529185b7018208cb3460191eeb53e0d/src/starkware/cairo/stark_verifier/air/layouts/starknet/autogenerated.cairo#L1574-L1596 - -pub const ROUND_CONSTANTS_HEXSTRINGS: [[&str; 3]; 91] = [ - [ - "0x6861759ea556a2339dd92f9562a30b9e58e2ad98109ae4780b7fd8eac77fe6f", - "0x3827681995d5af9ffc8397a3d00425a3da43f76abf28a64e4ab1a22f27508c4", - "0x3a3956d2fad44d0e7f760a2277dc7cb2cac75dc279b2d687a0dbe17704a8309", - ], - [ - "0x626c47a7d421fe1f13c4282214aa759291c78f926a2d1c6882031afe67ef4cd", - "0x78985f8e16505035bd6df5518cfd41f2d327fcc948d772cadfe17baca05d6a6", - "0x5427f10867514a3204c659875341243c6e26a68b456dc1d142dcf34341696ff", - ], - [ - "0x5af083f36e4c729454361733f0883c5847cd2c5d9d4cb8b0465e60edce699d7", - "0x7d71701bde3d06d54fa3f74f7b352a52d3975f92ff84b1ac77e709bfd388882", - "0x603da06882019009c26f8a6320a1c5eac1b64f699ffea44e39584467a6b1d3e", - ], - [ - "0x4332a6f6bde2f288e79ce13f47ad1cdeebd8870fd13a36b613b9721f6453a5d", - "0x53d0ebf61664c685310a04c4dec2e7e4b9a813aaeff60d6c9e8caeb5cba78e7", - "0x5346a68894845835ae5ebcb88028d2a6c82f99f928494ee1bfc2d15eaabfebc", - ], - [ - "0x550a9e24176509ea7631ccaecb7a4ab8694ab61f238797098147e69dd91e5a3", - "0x219dcccb783b1cbaa62773fedd3570e0f48ad3ed77c8b262b5794daa2687000", - "0x4b085eb1df4258c3453cc97445954bf3433b6ab9dd5a99592864c00f54a3f9a", - ], - [ - "0x53e8a8e8a404c503af2bf3c03e420ea5a465939d04b6c72e2da084e5aabb78d", - "0x5ca045c1312c09d1bd14d2537fe5c19fb4049cb137faf5df4f9ada962be8ca8", - "0x7c74922a456802c44997e959f27a5b06820b1ed97596a969939c46c162517f4", - ], - [ - "0xc0bba6880d2e686bf5088614b9684ff2526a20f91670435dc6f519bb7ab83f", - "0x4526bcaec43e8ebd708dd07234c1b2dc1a6203741decd72843849cd0f87934a", - "0x1cc9a17b00d3607d81efaea5a75a434bef44d92edc6d5b0bfe1ec7f01d613ed", - ], - [ - "0x28b1e269b84c4012aa8cdbead0bc1ce1eb7284e2b28ed90bc7b4a4fde8f01f", - "0x62af2f41d76c4ad1d9a2482fbdaf6590c19656bcb945b58bb724dc7a994498d", - "0x5cfd7e44946daa6b2618213b0d1bf4a2269bed2dc0d4dbf59e285eee627df1a", - ], - [ - "0x7ff2afb40f3300856fdd1b94da8d3bbcf0312ab9f16ac9bc31955dc8386a747", - "0x5cd236bdc15b54183e90bab8ae37f8aab40efae6fa9cd919b3248ee326e929c", - "0x5463841390e22d60c946418bf0e5822bd999084e30688e741a90bbd53a698a", - ], - [ - "0x24c940fff3fe8c8b2021f13eb4d71747efd44a4e51890ae8226e7406144f805", - "0x4e50cb07b3873268dc88f05393d9d03153ca4c02172dd1d7fc77d45e1b04555", - "0x62ca053e4da0fc87b430e53238d2bab1d9b499c35f375d7d0b32e1189b6dcb5", - ], - [ - "0x719f20ac59d1ebcaaf37fe0b851bc2419cd89100adff965951bff3d3d7e1191", - "0x7645ca5e87a9f916a82fe5bb90807f44050ac92ca52f5c798935cf47d55a8fd", - "0x15b8aeaca96ab53200eed38d248ecda23d4b71d17133438015391ca63663767", - ], - [ - "0x53d94dbbca7cb2aa8252f106292ac3b98799e908f928c196c1b658bf10b2e2", - "0x28f90b403e240f1c6f4c0a3b70edbb3942b447c615c0f033913831c34de2d1e", - "0x2485167dc233ba6e1161c4d0bf025159699dd2feb36e3e5b70ae6e770e22081", - ], - [ - "0x1c8b08a90d6ee46ff7de548541dd26988f7fdaacdd58698e938607a5feca6e8", - "0x105c3bf5cba256466b75e79d146f9880c7c4df5ecdad643ce05b16901c4881e", - "0x238019787f4cc0b627a65a21bef2106d5015b85dfbd77b2965418b02dbc6bd7", - ], - [ - "0x15e624d7698fdf9b73dce29a5f24c465c15b52dec8172923a6ebc99a6ddc5e1", - "0x5d3688ba56f34fdf56bc056ad8bf740ca0c2efef23b04a479f612fde5800a0a", - "0x229abdef3fef7ae9e67ed336e82dc6c2e26d872d98b3cce811c69ae363b444d", - ], - [ - "0x3e8096ecfcbcde2ee400801a56f236db2c43d1e33c92b57ac58daf2d3fc44db", - "0x3ad5fec670d7039108d605aae834c7ce6a7cd4e1b47bf6a02265352c57db9bd", - "0x7cf4598c0cf143875877afdbb4df6794ef597fff1f98557adca32046aeaef0a", - ], - [ - "0x58aecc0081b55134a4d1c4c8f27932e4170c37841fef49aca0ec7a123c00ad6", - "0x757b4b7ee98e0a15460b71995790396e4ef3c859db5b714ec09308d65d2ca61", - "0x6b82800937f8981f3cd974f43322169963d2b54fd2b7ed348dc6cc226718b5d", - ], - [ - "0x3a915b1814707273427e34ab8fbb7ca044f14088fedae9606b34a60b1e9c64", - "0x54afbf1bd990043f9bc01028ff44195c0bb609d367b76269a627689547bfbef", - "0x5e1ceb846fe1422b9524c7d014931072c3852df2d991470b08375edf6e762bb", - ], - [ - "0x7f751f98968212ebe5dff3ce06e8cb916709e0c48e3020c6b2b01c1bec0814b", - "0x36f6b64463f7c29fc3180616e340536bea7f01d226b68b6d45cd6dfbff811e4", - "0x61135c9846faf39b4511d74fe8de8b48dd4d0e469d6703d7ed4fe4fe8e0dbac", - ], - [ - "0xb58921a3fbdbb559b78f6acfca9a21a4ba83cc6e0ae3527fbaad907fc912b8", - "0x22a4f8a5cdc7474b9d16b61c2973847211d84eb2fb27b816e52821c2e2b1b1e", - "0x41cf6db5d6145edfeccbbc9a50b2ceedeb1765c61516ffcb112f810ad67036f", - ], - [ - "0xbe44689973db2b1cfc05fa8f4aec6fac6a0ff2fdfab744ade9de11416b6831", - "0x39bf209c4e117e16489cda45128096d6d148a237142dc4951df0b8239be148b", - "0x209cf541e5f74fc2b93310b8ce37b092a58282643860b5707c7eb980ea03a06", - ], - [ - "0x6b562e6005f34ee0bdc218ba681b6ba7232e122287036d18c22dd5afa95326d", - "0xe8103a23902be5dc6d5f59253a627a2a39c8aca11a914670e7a35dea38c8f", - "0x6a3725548c664fd06bdc1b4d5f9bed83ef8ca7468d68f4fbbf345de2d552f72", - ], - [ - "0x67fcd6997472e8e605d0f01a8eccc5f11a45c0aa21eb4ebb447b4af006a4a37", - "0x26144c95c8de3634075784d28c06c162a44366f77792d4064c95db6ecb5cff0", - "0x5b173c8b0eb7e9c4b3a874eb6307cda6fd875e3725061df895dc1466f350239", - ], - [ - "0x7e1c2d6fde8ac9f87bae06ad491d391c448f877e53298b6370f2165c3d54ddb", - "0x4db779f3e5b7424996f451b156fe4e28f74d61e7771f9e3fa433b57ca6627a9", - "0xbb930d8a6c6583713435ec06b6fed7825c3f71114acb93e240eed6970993dd", - ], - [ - "0x4472d73b2830565d708467e9296fb5599d3a08814c31c4189e9579c046e878f", - "0x7ba9c303dfee2d89e10e3c883ca5ce5614d23739b7cb2052cc23612b11170e2", - "0x21c0e3319ede47f0425dc9b2c1ed30e6356cb133e97579b822548eb9c4dc4b7", - ], - [ - "0x2cfd61139e50ddd37b09933816e2a0932e53b7dc4f4947565c1d41e877eb191", - "0x5abea18941a4976844544d92ee0eca65bdd10b3f170b0dc2f30acd37e26d8e7", - "0x77088fdb015c7947a6265e44fef6f724ea28ae28b26e6eee5a751b7ce6bcc21", - ], - [ - "0x3abdc9d677231325b3e3c43cfd443076b4ce33cddbc8446120dce84e6122b73", - "0x2250f430b7fe7d12e5d00b6b83e52a52ca94879ccfab81a7a602662c2d62c4d", - "0x5c92ef479c11bb51fb24ef76d57912b12660e7bd156d6cabbb1efb79a25861b", - ], - [ - "0x235ec597391648b510f616fa8b87900fd08fd4208a785cffcf784a63a0fd5c6", - "0x4ed4e872eb7e736207be77e9d11e38f396b5c0ba3376e855523c00b372cc668", - "0x5f9406febca3879b756ef3f6331890b3d46afa705908f68fb7d861c4f275a1b", - ], - [ - "0x1d9c501d9ff1fba621a9f61b68873c05f17b0384661f06d97edf441abdaa49d", - "0x4b0de22bbd0a58534982c8e28d2f6e169e37ba694774c4dfa530f41c535952e", - "0x1b4d48bd38a3f8602186aabb291eca0d319f0e3648b2574c49d6fd1b033d903", - ], - [ - "0x7558bbea55584bf1725d8aa67ddba626b6596bbd2f4e65719702cefcead4bab", - "0x1108f1a9500a52f561ea174600e266a70b157d56ece95b60a44cf7a3eef17be", - "0x8913d96a4f36b12becb92b4b6ae3f8c209fb90caab6668567289b67087bf60", - ], - [ - "0x6502262c51ad8f616926346857dec8cca2e99f5742b6bf223f4d8a6f32867a6", - "0x7cb5fcdc00892812889280505c915bde962ea034378b343cd3a5931d2ec0e52", - "0x2eb919524a89a26f90be9781a1515145baea3bc96b8cd1f01b221c4d2a1ce87", - ], - [ - "0x58efb6272921bc5eada46635e3567dced0662c0161223e3c1c63e8de3ec3d73", - "0x62fcd49ca9c7587b436d205ffc2a39594254a1ac34acd46d6955e7844d4f88e", - "0x635895330838846e62d9acce0b625f885e5941e54bd3a2106fcf837aef5313b", - ], - [ - "0x7da445b81e9b3d36d47a5f4d23b92a378a17f119d5e6e70629f8b41fefb12e3", - "0x2b22dab62f0817e9fc5737e189d5096a9027882bef1738943b7016256118343", - "0x1af01472348f395bacdfed1d27664d0d5bdea769be8fcb8fbef432b790e50d5", - ], - [ - "0x76b172dbbeec5a31de313b9390f79ec9284163c8e4986bc5b682e5ac6360309", - "0x70efaeae36f6af0f362f6cb423d2009b30ddb4178d46def0bdb2905b3e0862", - "0x6cb99b36e521ac0a39872686b84ee1d28c4942b8036a1c25a0e4117ccaeedf", - ], - [ - "0x29fd44305a5a9a70bbf9674e544bda0fb3d0fe5bb3aa743fd1b8a4fc1dc6055", - "0x6b447ded1046e83629b184d8c36db3a11a6778d8848142aa6363d6619f9764", - "0x642a8b4be4ba812cbfcf55a77339b5d357cceb6946fdc51c14b58f5b8989b59", - ], - [ - "0x489e0a26f65a1eecc6cc6aa5b6e775cbc51a73700bd794a7acd79ae1d95882a", - "0x3b19d4ef195975bbf78ab5dc2fd1d24816428f45a06293c1b9d57b9a02e9200", - "0x7d2dd994756eacba576b74790b2194971596f9cd59e55ad2884c52039013df5", - ], - [ - "0x1922810cc08f50bf300df869823b9f18b3327e29e9e765002970ef0f2e8c5f3", - "0x52f3afaf7c9102f1d46e1d79a70745b39c04376aafff05771cbd4a88ed418ac", - "0x7ccfc88e44a0507a95260f44203086e89552bbe53dcc46b376c5bcab6ea788e", - ], - [ - "0x2949125939e6ad94100228beff83823f5157dd8e067bc8819e40a1ab008dd9c", - "0x6cb64e3a0d37a6a4273ce4ee6929ba372d6811dde135af4078ba6e1912e1014", - "0xd63b53707acf8962f05f688129bf30ad43714257949cd9ded4bf5953837fae", - ], - [ - "0xbcb1549c9cabb5d13bb968b4ea22d0bb7d7460a6965702942092b32ef152d4", - "0x3d1c5233657ce31f5ead698fe76f6492792a7205ba0531a0ca25b8d8fe798c1", - "0x2240b9755182ee9066c2808b1e16ea448e26a83074558d9279f450b79f97516", - ], - [ - "0xcc203d8b0f90e30fe8e54f343cef59fe8d70882137de70c9b43ab6615a646c", - "0x310c6cc475d9346e061bacdc175ea9e119e937dea9d2100fa68e03c1f77910b", - "0x7f84b639f52e57420bc947defced0d8cbdbe033f578699397b83667049106c7", - ], - [ - "0x584ca7f01262c5bd89c4562f57139f47e9f038cb32ec35abe4e1da8de3e164a", - "0x1135eefaf69b6e4af7d02f562868be3e02fdc72e01e9510531f9afa78abbbde", - "0x372082b8a6c07100a50a3d33805827ad350c88b56f62c6d36a0d876856a99e8", - ], - [ - "0x7c3c12b819a8aad87499bac1a143fc59674f132e33898f0c119e3d12462dfe6", - "0x4f1354c51e8f6905b84157cfeff6822c056ce9e29d602eb46bd9b75a23836cf", - "0x2da9f26a8271659075739ba206507a08ac360150e849950ef3973548fbd2fca", - ], - [ - "0x287173956a2beb111b5ec29195e38cc3f6a65ff50801aa75fd78dd550702843", - "0x7273101c190ff64212420095a51c8411c7f3227f6a7a4a64ae6ba7f9201e126", - "0x2dbf2a6b56b26d23ebeb61e500687de749b03d3d349169699258ee4c98005fc", - ], - [ - "0x85b6cbb29739a6808e67f00ab89b52ab89ef8d92530394e4b910efd706c7fb", - "0x3d55b5f1171efda1dacbcbadfd5b910b493fa9589fd937e3e06ce26b08925a3", - "0xaaedaa6ef2fa707d16b3b295410c0e44f7a2f8135c207824f6ae2a9b16e90c", - ], - [ - "0x6aca6ebf70b1cb46c6331e9f1a5c4cc89b80f8adc5d18915c1cd0d496ccf5e1", - "0x1678602af36c28abb010f831d403d94d5e90003e6d37c677e9dd157fb27761", - "0x2022036bdf687f041b547fefdf36d4c2cd3f4b0526a88aafe60a0a8f508bad2", - ], - [ - "0x7bfc350957c968ca664397414bdfb8f9b8dfe49fb63e32353d4e2e8d1d4af6", - "0x2d639cbd418cb9fc24ea29ccd1d15ab81f43a499b27a06d3c5e2176f7ad79af", - "0xecdea7f959a4d488403d5b39687a1fe0dee3369e5fbc0f4779569f64506e0c", - ], - [ - "0x3f656bdc4fefd92b70658e2f1992ef9f22e5f2d28c490e21d4e34357154b558", - "0xd1b8cb1561eed32319638ccab9033dfec47596f8a6f4ce6594e19fddd59254", - "0x758ffc77c62e3e0f86ef6ea01545ad76f281ec2941da7222d1e8b4e2ec1f192", - ], - [ - "0x20315ca079570df995386e96aeaa1b4596aacd28f83c32f29a591c95e6fcac5", - "0x3e55cf341e7c280cb05f3d6ff9c8d9f2cfe76b84a9d1b0f54884b316b740d8d", - "0x4d56feb32cde74feede9749739be452e92c029007a06f6e67c81203bf650c68", - ], - [ - "0x4ee807aa678a9a433b6171eaa6a2544497f7599fb8145d7e8089f465403c89b", - "0x25d2bacc8f1ee7548cb5f394de2cb6e1f365e56a1bc579d0f9a8ad2ef2b3821", - "0x5f573de597ce1709fc20051f6501268cd4b278811924af1f237d15feb17bd49", - ], - [ - "0x30297c3c54a505f5826a280e053cf7a3c1e84a1dcf8b33c682cf85ddac86deb", - "0x2f5e9c47c9a86e043c7526a59783f03c6bc79b69b8709fe6a052b93a8339ae8", - "0x1bf75c7a739da8d29f9c23065ff8ccb1da7deec83e130bcd4a27a416c72b84b", - ], - [ - "0x60563d5f852ae875989017bd5c4cfdc29cd27fc4e91eeabdb8e864df3c3c675", - "0x7a4b1d70885aa820969635468daec94f8156c20e3131bd71005be1cd16ccf9e", - "0x347bb025695e497f1e201cd62aa4600b8b85cf718cd1d400f39c10e59cc5852", - ], - [ - "0x6783ab1e1ef97bb9e7f9381eb6ab0de2c4c9c2de413691ba8aa666292e9e217", - "0x133e0280c6de90e7b3870a07823c081fd9c4cb99d534debd6a7bfb4e5b0dd46", - "0x865d450ce29dc42fb5db72460b3560a2f093695573dff94fd0216eb925beec", - ], - [ - "0x1de023f840e054a35526dabacf0dee948efba06bcbb414ecd81a6b301664e57", - "0x55fc1e341bfdf7805015a96f724c5ac7cc7b892a292d38190631ab1a5388c4", - "0x2df6557bfd4a4e7e7b27bf51552d2b5162706a3e624faca01a307ef8d532858", - ], - [ - "0x113a8a66962ce08d92a6bd3e9c1d55ef8f226da95e4d629046d73d0507f6271", - "0x271577d6ee9fa377f2c889874ba5b44ca1076033db5c2de4f3367b08c008e53", - "0x3396b33911219b6b0365c09348a561ef1ccb956fc673bc5291d311866538574", - ], - [ - "0x1e1392f2da08549c8a7d89e899189306170baa3c3436e6a5398f69c8f321636", - "0x661545081032013df118e1d6e7c61a333e313b1a9a5b6d69c876bd2e7d694ca", - "0x6b14294e71cd7fb776edbd432d20eb8f66d00533574e46573516f0cacdeec88", - ], - [ - "0x7252fbbb06c2848338b1c41df31e4e51fe2a18e2406c671915cab6eb1a1d4f2", - "0x3ccf71be7cc2a9abcf5a09807c69679430c03645747621b7f5327cb00ff99da", - "0x29778dc707504fa6a9f7c97b4ceef0a9b39001d034441617757cd816dac919a", - ], - [ - "0x39473f6f06bb99e33590d34e3bae36e491f7bbf86a26aa55a8f5b27bb98d4c5", - "0x7ba7c32f875b71b895caa0215f996fd4ad92bab187e81417063dde91c08c027", - "0x37c1367e49cbfc403b22aac82abf83b0ed083148a5f4c92839e5d769bdab6b6", - ], - [ - "0x5c9eb899931d2f4b53ffcf833cdfa05c2068375ff933eb37ae34157c0b2d951", - "0x5f6054a4d48698ec27772fb50a7d2e5c1557ffdc1ffd07331f2ca26c6e3b661", - "0x20e6d62a2fe0fe9b0fab83e8c7d1e8bfd0fec827960e40a91df64664dcd7774", - ], - [ - "0x6290a56a489ad52120c426fe0e409c2ff17adf51f528cafb0d026d14ffd6aac", - "0x3703f16f990342c2267a6f7ece342705a32ca4c101417286279f6fc315edc7c", - "0x5194962daf6679b9a0c32b5a9a307ba92e2c630f70e439195b680dd296df3fd", - ], - [ - "0xe8eae20a79a7c1242c34617b01340fb5fd4bea2aa58b98d2400d9b515ee5e2", - "0x369058169d63091ae28bfb28def7cd8d00dd7c2894fae4ffec65242afa5cd45", - "0x418c963bc97195a74077503ee472f22cfdff0973190ab189c7b93103fd78167", - ], - [ - "0x68d07a3eefc78dc5b28b3f4dc93167fb8c97112d14a25b4d4db559720156386", - "0x517e892228df2d4f15a3c4241c98ba25ba0b5557375003f8748583a61836372", - "0x5cc0f0f6cf9be94a150116e7932f8fe74ac20ad8100c41dc9c99538792e279b", - ], - [ - "0x53d5d7863434c6629bdb1f8a648e4820883543e821f0f5c1668884c0be41ec8", - "0xa158126b89e6b0a600bf53f8101707b072218912dd0d9df2528f67de24fdf5", - "0x6b53b807265387ee582069a698323d44c204bed60672b8d8d073bed2fede503", - ], - [ - "0x1097fb448406b7a6de0877efd58c01be53be83bde9601a9acc9e0ca2091fda0", - "0xcbc0ff7239d3763902396389d67b3049ce1fefde66333ce37ca441f5a31bec", - "0x79a3d91dd8a309c632eb43d57b5c5d838ceebd64603f68a8141ebef84280e72", - ], - [ - "0x23fb472fe575135300f74e8f6de8fe1185078218eceb938900e7598a368db9", - "0x7ac73134016d2a8a4c63a6b9494c0bd7a6ba87cc33e8a8e23ebda18bfb67c2a", - "0x19a16068c3eac9c03f1b5c5ee2485ccc163d9ab17bb035d5df6e31c3dcf8f14", - ], - [ - "0x1f24b4356a6bbfd4d4ef9fd1634752820ee86a925725ac392134d90def073ea", - "0x3e44e7f7aeea6add59b6b4d11c60a528fb70727f35d817305971592333d36", - "0x5f93b02f826741414535a511ed3eb4fe85987ae57bc9807cbd94cd7513d394e", - ], - [ - "0xf0a0a88db99247d71c3d51d4197fa3fd1cc76e670607e35ca2d3bada29523a", - "0x3432226916d31f3acac1e211431fd4cd2b6f2e80626af6564bdde3e77608db0", - "0x55625941bfea6f48175192845a7ad74b0b82940ef5f393ca3830528d59cf919", - ], - [ - "0xddf48695b204477dfe4f8cb3ef1b39783e9b92f9276b858e2e585e318e20a4", - "0x260730a657ff8f38851a679ab2a1490434ee50d4953e7c5d3194578b08ae8e3", - "0x4cfd231373aa46d96283840bdb79ba6d7132775b398d324bcd206842b961aa9", - ], - [ - "0x3203843c41cd453f14fa0bc0b2191a27ebc659e74fd48f981e963de57eff25d", - "0x2c2f6ae5624d1fb8435d1c86bf76c260f5e77a54b006293705872e647cc46", - "0x780225456e63903b3e561384ef2e73a85b0e142b69752381535022014765f06", - ], - [ - "0x7f602ec1a80a051fd21b07f8e2960613082fc954b9a9ff641cc432a75c81887", - "0x62561b0a0a72239b60f6aaf7022b7d323fe77cd7c1ab432f0c8c118ca7e6bca", - "0x604fe5a6a22344aa69b05dea16b1cf22450c186d093754cb9b84a8a03b70bc8", - ], - [ - "0x1cf9987a4044716d3dc140bf5f9b76f6eada5995905189f8682eaf88aef2b7b", - "0x6bc0b2487c1eece3db47a4bdd60cf69debee233e91b50e9ee42ce22cbfbacbf", - "0x2f5dbb5055eb749a11403b93e90338b7620c51356d2c6adcbf87ab7ea0792e6", - ], - [ - "0x446328f4dddae6529743c43883d59c45f63b8a623a9cf318489e5fc4a550f61", - "0x4ba30c5240cde5bca6c4010fb4b481a25817b43d358399958584d2c48f5af25", - "0x5f5275f76425b15c89209117734ae85708351d2cf19af5fe39a32f89c2c8a89", - ], - [ - "0x576f3b5156f4763e18c7f98df3b2f7b993cdda4eb8cb92415e1be8e6af2fc17", - "0x11dc3f15cba928aed5a44b55a5b026df84a61719ed5adbb93c0e8e12d35ef3d", - "0x44c40e6bd52e91ad9896403ae4f543ae1c1d9ea047d75f8a6442b8feda04dca", - ], - [ - "0x1836d733a54013ebd0ccbf4974e80ac1954bf90fe9ea4e2c914ad01166026d8", - "0x3c553be9776b628a8159d306ef084727611df8037761f00f84ca02ce731b3ac", - "0x6ce94781c1a23fda1c7b87e0436b1b401ae11a6d757843e342f5017076a059", - ], - [ - "0x381ec71fbdef3160253be9f00f4e6b9e107f457812effb7371cc2daa0acd0ed", - "0x1844da9cc0eeadc6490d847320d9f3cd4fb574aa687bafdfe0ffa7bf2a8f1a1", - "0x7a8bf471f902d5abb27fea5b401483dedf97101047459682acfd7f9b65a812f", - ], - [ - "0x633b6fb004de62441915fb51ac174456f5a9cdff7aecb6e6b0d063839e56327", - "0x179ee5cec496194771200382bfc6d17bbe546ba88fed8b17535fd70fbc50ab6", - "0x2806c0786185986ea9891b42d565256b0312446f07435ac2cae194330bf8c42", - ], - [ - "0x438703d948708ae90c7a6b8af194b8b603bb2cdfd26bfa356ac9bb6ee041393", - "0x24446628f56029d7153bd3a482b7f6e1c56f4e02225c628a585d58a920035af", - "0x4c2a76e5ce832e8b0685cdeeea3a253ae48f6606790d817bd96025e5435e259", - ], - [ - "0x78a23323520994592933c079b148aed57d5e4ce1ab122d370983b8caa0e0300", - "0x79ca6c5e1025b2151144ea5937dd07cadce1aa691b19e6db87070ba51ec22c0", - "0x6b2e4a46e37af3cf952d9d34f8d6bd84a442ebfd1ac5d17314e48922af79c5d", - ], - [ - "0x305d6cd95cc2eab6805d93d3d8d74e1ca7d443f11e34a18e3529e0d03435c2", - "0x6097b4b8b90db14b39743ed23f8956cabb7aea70cc624a415c7c17b37fbf9a9", - "0x64e1b3f16c26c8845bdb98373e77dad3bdcc90865b0f0af96288707c18893f", - ], - [ - "0x649fafe673f21e623384d841221b73421c56014af2ffdf57f1579ae911fd335", - "0x7d806dccbf1a2696b294404e849722f2baa2f4d19005a49d1ba288a77fefe30", - "0x5951a37da53e3bbc0b3e2db1a9a235d7a03f48f443be6d659119c44aafc7522", - ], - [ - "0x6d87fa479fb59524d1912c3554ae3d010496a31bdacb542c816a1607a907731", - "0x1451cccd4200fa9d473ad73466b4e8c0a712a0b12bb6fc9462a3ac892acc9b2", - "0x3ca1b6400b3e51007642535f1ca9b03832ca0faa15e1c4ed82dd1efdc0763da", - ], - [ - "0x52c55735b2f0a6560ad1516a8f13592b0dd024ff4162539f993a99c7a1a4d95", - "0x7e04de60aa80132f0149d1dee29617de750bd5ce3e9fa5e62951d65f6b924cd", - "0x271784e6920a68e47c4c8fab71c8f8303ef29e26f289223edf63291c0a5495", - ], - [ - "0x5c7c19061a84d5960a04b8f0adaa603c8afe93f17b7f0e56b49514af43d0c69", - "0x172db5affe783af419da337cb79061e090943c2959dea1b38e4436f5482eafe", - "0x518b7975a6d8d310eac9fe4082916f021a7ecbadf18809746a9e061a2cb9456", - ], - [ - "0x20c5539dc45dd56d4bbc2440a9f5061d74b8ae5e37b34e8755a0315f1e196db", - "0x1ea6f5fb309fa4a08bc7d516e80efc3a977b47208283cf35a9d8bc213b90b14", - "0x50ce323c5128dc7fdd8ddd8ba9cfe2efd424b5de167c7257d1f766541e29ded", - ], - [ - "0x401e37d0e276547695538b41d3c28215b865f5b7d1b497a8919284c613cb7d8", - "0x645a0de30acc3117f2893056fc5880255daa12cc61261cc0fab9cf57c57397b", - "0x69bc3841eb0a310d9e988d75f09f698d4fdc9d0d69219f676b66ae7fa3d495b", - ], - [ - "0x2684bbe315ad2c4bdd47c38fe72db47cf0ae0c455cda5484baf523f136bdc6", - "0x11e0f83c547ca5c68202e8d34e5595a88858c2afa664365e4acb821fd8a13ee", - "0x4af4a7635f8c7515966567ceec34315d0f86ac66c1e5a5ecac945f1097b82ef", - ], - [ - "0x4fba58cf8aaf4893cb7158908ccc18b1dc48894d2bb46225c72b11f4c74b271", - "0x397c4c169115b468cc90da2e664f8c29a7f89be0ead679a38b0f44c8a2a0e20", - "0x6563b9ebb6450dbad397fa5dd13c501f326dd7f32be22e20998f59ec7bacff", - ], - [ - "0x376edb238f7b630ea81d307f4c79f9afec48562076dd09c36cd79e9cb817165", - "0x60d4208bb50eb15f29ed22addcd50a1b337504039690eb858584cda96e2e061", - "0x6a37d569d2fbc73dbff1019dc3465ec0f30da46918ab020344a52f1df9a9210", - ], - [ - "0xd3b174c7290c6bf412083ff35d23821dc512f1df073c1b429130371ac63b1a", - "0x226ed3d763477454b46eb2a5c3b814634d974919689fb489fe55e525b980373", - "0x5f3997e7dafcb2de0e7a23d33d2fd9ef06f4d79bd7ffa1930e8b0080d218513", - ], - [ - "0x7c5eec716d94634434df335a10bbac504f886f7f9d3c1648348c3fae8fdf14d", - "0x53cc30d7fe0f84e7e24fd22c0f9ad68a89da85553f871ef63d2f55f57e1a7c", - "0x368821ee335d71819b95769f47418569474a24f6e83b268fefa4cd58c4ec8fa", - ], - [ - "0x5334f75b052c0235119816883040da72c6d0a61538bdfff46d6a242bfeb7a1", - "0x5d0af4fcbd9e056c1020cca9d871ae68f80ee4af2ec6547cd49d6dca50aa431", - "0x30131bce2fba5694114a19c46d24e00b4699dc00f1d53ba5ab99537901b1e65", - ], - [ - "0x5646a95a7c1ae86b34c0750ed2e641c538f93f13161be3c4957660f2e788965", - "0x4b9f291d7b430c79fac36230a11f43e78581f5259692b52c90df47b7d4ec01a", - "0x5006d393d3480f41a98f19127072dc83e00becf6ceb4d73d890e74abae01a13", - ], - [ - "0x62c9d42199f3b260e7cb8a115143106acf4f702e6b346fd202dc3b26a679d80", - "0x51274d092db5099f180b1a8a13b7f2c7606836eabd8af54bf1d9ac2dc5717a5", - "0x61fc552b8eb75e17ad0fb7aaa4ca528f415e14f0d9cdbed861a8db0bfff0c5b", - ], -]; diff --git a/crypto/src/merkle_tree/backends/field_element.rs b/crypto/src/merkle_tree/backends/field_element.rs index d9ad88fe17..dcaf09969f 100644 --- a/crypto/src/merkle_tree/backends/field_element.rs +++ b/crypto/src/merkle_tree/backends/field_element.rs @@ -1,12 +1,8 @@ -use crate::hash::poseidon::starknet::parameters::{DefaultPoseidonParams, PermutationParameters}; -use crate::hash::poseidon::starknet::Poseidon; +use crate::hash::poseidon::Poseidon; use crate::merkle_tree::traits::IsMerkleTreeBackend; use lambdaworks_math::{ - field::{ - element::FieldElement, - traits::{IsField, IsPrimeField}, - }, + field::{element::FieldElement, traits::IsField}, traits::Serializable, }; use sha3::{ @@ -53,36 +49,28 @@ where } } -#[derive(Clone)] -pub struct TreePoseidon { - poseidon: Poseidon, +#[derive(Clone, Default)] +pub struct TreePoseidon { + _poseidon: PhantomData

, } -impl Default for TreePoseidon +impl

IsMerkleTreeBackend for TreePoseidon

where - F: IsPrimeField, + P: Poseidon + Default, { - fn default() -> Self { - let params = PermutationParameters::new_with(DefaultPoseidonParams::CairoStark252); - let poseidon = Poseidon::new_with_params(params); - - Self { poseidon } - } -} - -impl IsMerkleTreeBackend for TreePoseidon -where - F: IsPrimeField, -{ - type Node = FieldElement; - type Data = FieldElement; + type Node = FieldElement; + type Data = FieldElement; - fn hash_data(&self, input: &FieldElement) -> FieldElement { - self.poseidon.hash_single(input) + fn hash_data(&self, input: &FieldElement) -> FieldElement { + P::hash_single(input) } - fn hash_new_parent(&self, left: &FieldElement, right: &FieldElement) -> FieldElement { - self.poseidon.hash(left, right) + fn hash_new_parent( + &self, + left: &FieldElement, + right: &FieldElement, + ) -> FieldElement { + P::hash(left, right) } } diff --git a/crypto/src/merkle_tree/backends/field_element_vector.rs b/crypto/src/merkle_tree/backends/field_element_vector.rs index dd245001a6..d49e4cb9b7 100644 --- a/crypto/src/merkle_tree/backends/field_element_vector.rs +++ b/crypto/src/merkle_tree/backends/field_element_vector.rs @@ -1,13 +1,9 @@ use std::marker::PhantomData; -use crate::hash::poseidon::starknet::parameters::{DefaultPoseidonParams, PermutationParameters}; -use crate::hash::poseidon::starknet::Poseidon; +use crate::hash::poseidon::Poseidon; use crate::merkle_tree::traits::IsMerkleTreeBackend; use lambdaworks_math::{ - field::{ - element::FieldElement, - traits::{IsField, IsPrimeField}, - }, + field::{element::FieldElement, traits::IsField}, traits::Serializable, }; use sha3::{ @@ -60,36 +56,28 @@ where } } -#[derive(Clone)] -pub struct BatchPoseidonTree { - poseidon: Poseidon, +#[derive(Clone, Default)] +pub struct BatchPoseidonTree { + _poseidon: PhantomData

, } -impl Default for BatchPoseidonTree +impl

IsMerkleTreeBackend for BatchPoseidonTree

where - F: IsPrimeField, + P: Poseidon + Default, { - fn default() -> Self { - let params = PermutationParameters::new_with(DefaultPoseidonParams::CairoStark252); - let poseidon = Poseidon::new_with_params(params); - - Self { poseidon } - } -} - -impl IsMerkleTreeBackend for BatchPoseidonTree -where - F: IsPrimeField, -{ - type Node = FieldElement; - type Data = Vec>; + type Node = FieldElement; + type Data = Vec>; - fn hash_data(&self, input: &Vec>) -> FieldElement { - self.poseidon.hash_many(input) + fn hash_data(&self, input: &Vec>) -> FieldElement { + P::hash_many(input) } - fn hash_new_parent(&self, left: &FieldElement, right: &FieldElement) -> FieldElement { - self.poseidon.hash(left, right) + fn hash_new_parent( + &self, + left: &FieldElement, + right: &FieldElement, + ) -> FieldElement { + P::hash(left, right) } } diff --git a/examples/merkle-tree-cli/src/main.rs b/examples/merkle-tree-cli/src/main.rs index 661bd73c9e..b23b3f5e1d 100644 --- a/examples/merkle-tree-cli/src/main.rs +++ b/examples/merkle-tree-cli/src/main.rs @@ -1,6 +1,7 @@ mod commands; use clap::Parser; use commands::{MerkleArgs, MerkleEntity}; +use lambdaworks_crypto::hash::poseidon::starknet::PoseidonCairoStark252; use lambdaworks_crypto::merkle_tree::{ backends::field_element::TreePoseidon, merkle::MerkleTree, proof::Proof, }; @@ -30,7 +31,7 @@ fn load_tree_values(tree_path: &String) -> Result, io::Error> { fn generate_merkle_tree(tree_path: String) -> Result<(), io::Error> { let values: Vec = load_tree_values(&tree_path)?; - let merkle_tree = MerkleTree::>::build(&values); + let merkle_tree = MerkleTree::>::build(&values); let root = merkle_tree.root.representative().to_string(); println!("Generated merkle tree with root: {:?}", root); @@ -44,7 +45,7 @@ fn generate_merkle_tree(tree_path: String) -> Result<(), io::Error> { fn generate_merkle_proof(tree_path: String, pos: usize) -> Result<(), io::Error> { let values: Vec = load_tree_values(&tree_path)?; - let merkle_tree = MerkleTree::>::build(&values); + let merkle_tree = MerkleTree::>::build(&values); let Some(proof) = merkle_tree.get_proof_by_pos(pos) else { return Err(io::Error::new(io::ErrorKind::Other, "Index out of bounds")); @@ -70,7 +71,7 @@ fn verify_merkle_proof( let file_str = fs::read_to_string(proof_path)?; let proof: Proof = serde_json::from_str(&file_str)?; - match proof.verify::>(&root_hash, index, &leaf) { + match proof.verify::>(&root_hash, index, &leaf) { true => println!("\x1b[32mMerkle proof verified succesfully\x1b[0m"), false => println!("\x1b[31mMerkle proof failed verifying\x1b[0m"), } From 96f4600fa336ae07db1d95b8d21f04a2dca24531 Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Mon, 4 Dec 2023 13:35:55 -0300 Subject: [PATCH 05/29] Release v0.3.0: Vitello Tonnato (#708) --- Cargo.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d97f54b1eb..dbcf7cad7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,18 +4,18 @@ exclude = ["ensure-no_std"] resolver = "2" [workspace.package] -version = "0.2.0" +version = "0.3.0" edition = "2021" license = "Apache-2.0" repository = "https://github.com/lambdaclass/lambdaworks" [workspace.dependencies] iai-callgrind = "0.3.1" -lambdaworks-crypto = { path = "./crypto", version = "0.2.0" } -lambdaworks-gpu = { path = "./gpu", version = "0.2.0" } -lambdaworks-math = { path = "./math", version = "0.2.0" } -stark-platinum-prover = { path = "./provers/stark", version = "0.2.0" } -cairo-platinum-prover = { path = "./provers/cairo", version = "0.2.0" } +lambdaworks-crypto = { path = "./crypto", version = "0.3.0" } +lambdaworks-gpu = { path = "./gpu", version = "0.3.0" } +lambdaworks-math = { path = "./math", version = "0.3.0" } +stark-platinum-prover = { path = "./provers/stark", version = "0.3.0" } +cairo-platinum-prover = { path = "./provers/cairo", version = "0.3.0" } [profile.bench] lto = true From 3724c1c6f7d85425dc1b284d56a03bb350d4eaa5 Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Tue, 5 Dec 2023 08:34:57 -0300 Subject: [PATCH 06/29] feat: make Merkle backends stateless (#707) Change the associated methods for `IsMerkleBackend` to associated functions. Make the `Data` and `Node` associated types `Sync + Send`. This makes it easier to later add parallelism to Merkle tree construction. --- crypto/src/merkle_tree/backends/field_element.rs | 10 +++++----- .../merkle_tree/backends/field_element_vector.rs | 10 ++++++---- crypto/src/merkle_tree/merkle.rs | 5 ++--- crypto/src/merkle_tree/proof.rs | 7 +++---- crypto/src/merkle_tree/test_merkle.rs | 9 ++++++--- crypto/src/merkle_tree/traits.rs | 12 ++++++------ crypto/src/merkle_tree/utils.rs | 15 ++++++--------- provers/stark/src/fri/mod.rs | 6 +++--- provers/stark/src/prover.rs | 16 ++++++++-------- provers/stark/src/verifier.rs | 16 ++++++++-------- 10 files changed, 53 insertions(+), 53 deletions(-) diff --git a/crypto/src/merkle_tree/backends/field_element.rs b/crypto/src/merkle_tree/backends/field_element.rs index dcaf09969f..11171f2180 100644 --- a/crypto/src/merkle_tree/backends/field_element.rs +++ b/crypto/src/merkle_tree/backends/field_element.rs @@ -29,19 +29,19 @@ impl IsMerkleTreeBackend for FieldElementBackend where F: IsField, - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, [u8; NUM_BYTES]: From::OutputSize>>, { type Node = [u8; NUM_BYTES]; type Data = FieldElement; - fn hash_data(&self, input: &FieldElement) -> [u8; NUM_BYTES] { + fn hash_data(input: &FieldElement) -> [u8; NUM_BYTES] { let mut hasher = D::new(); hasher.update(input.serialize()); hasher.finalize().into() } - fn hash_new_parent(&self, left: &[u8; NUM_BYTES], right: &[u8; NUM_BYTES]) -> [u8; NUM_BYTES] { + fn hash_new_parent(left: &[u8; NUM_BYTES], right: &[u8; NUM_BYTES]) -> [u8; NUM_BYTES] { let mut hasher = D::new(); hasher.update(left); hasher.update(right); @@ -57,16 +57,16 @@ pub struct TreePoseidon { impl

IsMerkleTreeBackend for TreePoseidon

where P: Poseidon + Default, + FieldElement: Sync + Send, { type Node = FieldElement; type Data = FieldElement; - fn hash_data(&self, input: &FieldElement) -> FieldElement { + fn hash_data(input: &FieldElement) -> FieldElement { P::hash_single(input) } fn hash_new_parent( - &self, left: &FieldElement, right: &FieldElement, ) -> FieldElement { diff --git a/crypto/src/merkle_tree/backends/field_element_vector.rs b/crypto/src/merkle_tree/backends/field_element_vector.rs index d49e4cb9b7..509c56752c 100644 --- a/crypto/src/merkle_tree/backends/field_element_vector.rs +++ b/crypto/src/merkle_tree/backends/field_element_vector.rs @@ -32,11 +32,12 @@ where F: IsField, FieldElement: Serializable, [u8; NUM_BYTES]: From::OutputSize>>, + Vec>: Sync + Send, { type Node = [u8; NUM_BYTES]; type Data = Vec>; - fn hash_data(&self, input: &Vec>) -> [u8; NUM_BYTES] { + fn hash_data(input: &Vec>) -> [u8; NUM_BYTES] { let mut hasher = D::new(); for element in input.iter() { hasher.update(element.serialize()); @@ -46,7 +47,7 @@ where result_hash } - fn hash_new_parent(&self, left: &[u8; NUM_BYTES], right: &[u8; NUM_BYTES]) -> [u8; NUM_BYTES] { + fn hash_new_parent(left: &[u8; NUM_BYTES], right: &[u8; NUM_BYTES]) -> [u8; NUM_BYTES] { let mut hasher = D::new(); hasher.update(left); hasher.update(right); @@ -64,16 +65,17 @@ pub struct BatchPoseidonTree { impl

IsMerkleTreeBackend for BatchPoseidonTree

where P: Poseidon + Default, + Vec>: Sync + Send, + FieldElement: Sync + Send, { type Node = FieldElement; type Data = Vec>; - fn hash_data(&self, input: &Vec>) -> FieldElement { + fn hash_data(input: &Vec>) -> FieldElement { P::hash_many(input) } fn hash_new_parent( - &self, left: &FieldElement, right: &FieldElement, ) -> FieldElement { diff --git a/crypto/src/merkle_tree/merkle.rs b/crypto/src/merkle_tree/merkle.rs index e97d821f5b..a7ff332444 100644 --- a/crypto/src/merkle_tree/merkle.rs +++ b/crypto/src/merkle_tree/merkle.rs @@ -13,8 +13,7 @@ where B: IsMerkleTreeBackend, { pub fn build(unhashed_leaves: &[B::Data]) -> Self { - let hasher = B::default(); - let mut hashed_leaves: Vec = hasher.hash_leaves(unhashed_leaves); + let mut hashed_leaves: Vec = B::hash_leaves(unhashed_leaves); //The leaf must be a power of 2 set hashed_leaves = complete_until_power_of_two(&mut hashed_leaves); @@ -26,7 +25,7 @@ where inner_nodes.extend(hashed_leaves); //Build the inner nodes of the tree - build(&mut inner_nodes, ROOT, &hasher); + build::(&mut inner_nodes, ROOT); MerkleTree { root: inner_nodes[ROOT].clone(), diff --git a/crypto/src/merkle_tree/proof.rs b/crypto/src/merkle_tree/proof.rs index c2914cf503..21c1b9c746 100644 --- a/crypto/src/merkle_tree/proof.rs +++ b/crypto/src/merkle_tree/proof.rs @@ -20,14 +20,13 @@ impl Proof { where B: IsMerkleTreeBackend, { - let hasher = B::default(); - let mut hashed_value = hasher.hash_data(value); + let mut hashed_value = B::hash_data(value); for sibling_node in self.merkle_path.iter() { if index % 2 == 0 { - hashed_value = hasher.hash_new_parent(&hashed_value, sibling_node); + hashed_value = B::hash_new_parent(&hashed_value, sibling_node); } else { - hashed_value = hasher.hash_new_parent(sibling_node, &hashed_value); + hashed_value = B::hash_new_parent(sibling_node, &hashed_value); } index >>= 1; diff --git a/crypto/src/merkle_tree/test_merkle.rs b/crypto/src/merkle_tree/test_merkle.rs index 076e26fce7..4fc8e2cdc1 100644 --- a/crypto/src/merkle_tree/test_merkle.rs +++ b/crypto/src/merkle_tree/test_merkle.rs @@ -23,15 +23,18 @@ impl Default for TestBackend { } } -impl IsMerkleTreeBackend for TestBackend { +impl IsMerkleTreeBackend for TestBackend +where + FieldElement: Sync + Send, +{ type Node = FieldElement; type Data = FieldElement; - fn hash_data(&self, input: &Self::Data) -> Self::Node { + fn hash_data(input: &Self::Data) -> Self::Node { input + input } - fn hash_new_parent(&self, left: &Self::Node, right: &Self::Node) -> Self::Node { + fn hash_new_parent(left: &Self::Node, right: &Self::Node) -> Self::Node { left + right } } diff --git a/crypto/src/merkle_tree/traits.rs b/crypto/src/merkle_tree/traits.rs index e8e010eb0f..6f2f7097f6 100644 --- a/crypto/src/merkle_tree/traits.rs +++ b/crypto/src/merkle_tree/traits.rs @@ -2,22 +2,22 @@ /// tree is built from. It also defines the `Node` type and the hash function /// used to build parent nodes from children nodes. pub trait IsMerkleTreeBackend: Default { - type Node: PartialEq + Eq + Clone; - type Data; + type Node: PartialEq + Eq + Clone + Sync + Send; + type Data: Sync + Send; /// This function takes a single variable `Data` and converts it to a node. - fn hash_data(&self, leaf: &Self::Data) -> Self::Node; + fn hash_data(leaf: &Self::Data) -> Self::Node; /// This function takes the list of data from which the Merkle /// tree will be built from and converts it to a list of leaf nodes. - fn hash_leaves(&self, unhashed_leaves: &[Self::Data]) -> Vec { + fn hash_leaves(unhashed_leaves: &[Self::Data]) -> Vec { unhashed_leaves .iter() - .map(|leaf| self.hash_data(leaf)) + .map(|leaf| Self::hash_data(leaf)) .collect() } /// This function takes to children nodes and builds a new parent node. /// It will be used in the construction of the Merkle tree. - fn hash_new_parent(&self, child_1: &Self::Node, child_2: &Self::Node) -> Self::Node; + fn hash_new_parent(child_1: &Self::Node, child_2: &Self::Node) -> Self::Node; } diff --git a/crypto/src/merkle_tree/utils.rs b/crypto/src/merkle_tree/utils.rs index 64dc0de833..ab76176bb7 100644 --- a/crypto/src/merkle_tree/utils.rs +++ b/crypto/src/merkle_tree/utils.rs @@ -28,7 +28,7 @@ pub fn is_power_of_two(x: usize) -> bool { (x != 0) && ((x & (x - 1)) == 0) } -pub fn build(nodes: &mut Vec, parent_index: usize, hasher: &B) +pub fn build(nodes: &mut Vec, parent_index: usize) where B::Node: Clone, { @@ -39,11 +39,10 @@ where let left_child_index = left_child_index(parent_index); let right_child_index = right_child_index(parent_index); - build(nodes, left_child_index, hasher); - build(nodes, right_child_index, hasher); + build::(nodes, left_child_index); + build::(nodes, right_child_index); - nodes[parent_index] = - hasher.hash_new_parent(&nodes[left_child_index], &nodes[right_child_index]); + nodes[parent_index] = B::hash_new_parent(&nodes[left_child_index], &nodes[right_child_index]); } pub fn is_leaf(lenght: usize, node_index: usize) -> bool { @@ -73,8 +72,7 @@ mod tests { // expected |2|4|6|8| fn hash_leaves_from_a_list_of_field_elemnts() { let values: Vec = (1..5).map(FE::new).collect(); - let hasher = TestBackend::default(); - let hashed_leaves = hasher.hash_leaves(&values); + let hashed_leaves = TestBackend::hash_leaves(&values); let list_of_nodes = &[FE::new(2), FE::new(4), FE::new(6), FE::new(8)]; for (leaf, expected_leaf) in hashed_leaves.iter().zip(list_of_nodes) { assert_eq!(leaf, expected_leaf); @@ -105,8 +103,7 @@ mod tests { let mut nodes = vec![FE::zero(); leaves.len() - 1]; nodes.extend(leaves); - let hasher = TestBackend::default(); - build::>(&mut nodes, ROOT, &hasher); + build::>(&mut nodes, ROOT); assert_eq!(nodes[ROOT], FE::new(10)); } } diff --git a/provers/stark/src/fri/mod.rs b/provers/stark/src/fri/mod.rs index 55c5f06a8c..e562f6bd93 100644 --- a/provers/stark/src/fri/mod.rs +++ b/provers/stark/src/fri/mod.rs @@ -29,7 +29,7 @@ pub fn commit_phase( Vec>>, ) where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, { let mut domain_size = domain_size; @@ -77,7 +77,7 @@ pub fn query_phase( iotas: &[usize], ) -> Vec> where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, { if !fri_layers.is_empty() { let query_list = iotas @@ -117,7 +117,7 @@ pub fn new_fri_layer( ) -> crate::fri::fri_commitment::FriLayer> where F: IsFFTField, - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, { let mut evaluation = poly .evaluate_offset_fft(1, Some(domain_size), coset_offset) diff --git a/provers/stark/src/prover.rs b/provers/stark/src/prover.rs index 4cea852e70..2b21455f58 100644 --- a/provers/stark/src/prover.rs +++ b/provers/stark/src/prover.rs @@ -47,7 +47,7 @@ pub struct Round1 where F: IsFFTField, A: AIR, - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, { pub(crate) trace_polys: Vec>>, pub(crate) lde_trace: TraceTable, @@ -59,7 +59,7 @@ where pub struct Round2 where F: IsFFTField, - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, { pub(crate) composition_poly_parts: Vec>>, pub(crate) lde_composition_poly_evaluations: Vec>>, @@ -105,7 +105,7 @@ pub trait IsStarkProver { vectors: &[Vec>], ) -> (BatchedMerkleTree, Commitment) where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, { let tree = BatchedMerkleTree::::build(vectors); let commitment = tree.root; @@ -222,7 +222,7 @@ pub trait IsStarkProver { lde_composition_poly_parts_evaluations: &[Vec>], ) -> (BatchedMerkleTree, Commitment) where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, { // TODO: Remove clones let mut lde_composition_poly_evaluations = Vec::new(); @@ -310,7 +310,7 @@ pub trait IsStarkProver { z: &FieldElement, ) -> Round3 where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, { let z_power = z.pow(round_2_result.composition_poly_parts.len()); @@ -565,7 +565,7 @@ pub trait IsStarkProver { index: usize, ) -> (Proof, Vec>) where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, { let proof = composition_poly_merkle_tree .get_proof_by_pos(index) @@ -591,7 +591,7 @@ pub trait IsStarkProver { index: usize, ) -> (Vec>, Vec>) where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, { let domain_size = domain.lde_roots_of_unity_coset.len(); let lde_trace_evaluations = lde_trace @@ -623,7 +623,7 @@ pub trait IsStarkProver { DeepPolynomialOpenings, ) where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, { let mut openings = Vec::new(); let mut openings_symmetric = Vec::new(); diff --git a/provers/stark/src/verifier.rs b/provers/stark/src/verifier.rs index 3bf1f41a36..c498578d01 100644 --- a/provers/stark/src/verifier.rs +++ b/provers/stark/src/verifier.rs @@ -306,7 +306,7 @@ pub trait IsStarkVerifier { challenges: &Challenges, ) -> bool where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, A: AIR, { let (deep_poly_evaluations, deep_poly_evaluations_sym) = @@ -367,7 +367,7 @@ pub trait IsStarkVerifier { value: &[FieldElement], ) -> bool where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, { proof.verify::>(root, index, &value.to_owned()) } @@ -382,7 +382,7 @@ pub trait IsStarkVerifier { iota: usize, ) -> bool where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, { let lde_trace_evaluations = vec![ deep_poly_openings.lde_trace_evaluations[..num_main_columns].to_vec(), @@ -425,7 +425,7 @@ pub trait IsStarkVerifier { iota: &usize, ) -> bool where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, { let mut value = deep_poly_openings .lde_composition_poly_parts_evaluation @@ -447,7 +447,7 @@ pub trait IsStarkVerifier { challenges: &Challenges, ) -> bool where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, { challenges .iotas @@ -486,7 +486,7 @@ pub trait IsStarkVerifier { iota: usize, ) -> bool where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, { let evaluations = if iota % 2 == 1 { vec![evaluation_sym.clone(), evaluation.clone()] @@ -519,7 +519,7 @@ pub trait IsStarkVerifier { deep_composition_evaluation_sym: &FieldElement, ) -> bool where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, { let fri_layers_merkle_roots = &proof.fri_layers_merkle_roots; let evaluation_point_vec: Vec> = @@ -670,7 +670,7 @@ pub trait IsStarkVerifier { ) -> bool where A: AIR, - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, { // Verify there are enough queries if proof.query_list.len() < proof_options.fri_number_of_queries { From f3622cc3862503f0d300ce28c706dae938090acb Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Tue, 5 Dec 2023 15:17:06 -0300 Subject: [PATCH 07/29] perf: add parallel Merkle tree generation feature (#710) For now only leaves hashing is parallel. Inner nodes are left for future work as they require bigger changes to make the process iterative rather than recursive. --- crypto/Cargo.toml | 2 ++ crypto/src/merkle_tree/traits.rs | 13 +++++++++---- provers/stark/Cargo.toml | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index 8261ee1966..adb02143a9 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -13,6 +13,7 @@ sha3 = "0.10" sha2 = "0.10" thiserror = "1.0.38" serde = { version = "1.0", features = ["derive"] } +rayon = { version = "1.8.0", optional = true } [dev-dependencies] criterion = "0.4" @@ -21,6 +22,7 @@ rand = "0.8.5" [features] test_fiat_shamir = [] +parallel = ["dep:rayon"] [[bench]] name = "criterion_merkle" diff --git a/crypto/src/merkle_tree/traits.rs b/crypto/src/merkle_tree/traits.rs index 6f2f7097f6..dce3e253b6 100644 --- a/crypto/src/merkle_tree/traits.rs +++ b/crypto/src/merkle_tree/traits.rs @@ -1,3 +1,6 @@ +#[cfg(feature = "parallel")] +use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; + /// A backend for Merkle trees. This defines raw `Data` from which the Merkle /// tree is built from. It also defines the `Node` type and the hash function /// used to build parent nodes from children nodes. @@ -11,10 +14,12 @@ pub trait IsMerkleTreeBackend: Default { /// This function takes the list of data from which the Merkle /// tree will be built from and converts it to a list of leaf nodes. fn hash_leaves(unhashed_leaves: &[Self::Data]) -> Vec { - unhashed_leaves - .iter() - .map(|leaf| Self::hash_data(leaf)) - .collect() + #[cfg(feature = "parallel")] + let iter = unhashed_leaves.par_iter(); + #[cfg(not(feature = "parallel"))] + let iter = unhashed_leaves.iter(); + + iter.map(|leaf| Self::hash_data(leaf)).collect() } /// This function takes to children nodes and builds a new parent node. diff --git a/provers/stark/Cargo.toml b/provers/stark/Cargo.toml index 26f6e25c98..aa1f5c6832 100644 --- a/provers/stark/Cargo.toml +++ b/provers/stark/Cargo.toml @@ -45,7 +45,7 @@ wasm-bindgen-test = "0.3.0" test_fiat_shamir = [] instruments = [] # This enables timing prints in prover and verifier metal = ["lambdaworks-math/metal"] -parallel = ["dep:rayon"] +parallel = ["dep:rayon", "lambdaworks-crypto/parallel"] wasm = ["dep:wasm-bindgen", "dep:serde-wasm-bindgen", "dep:web-sys"] [target.'cfg(not(all(target_arch = "wasm32", target_os = "unknown")))'.dev-dependencies] From b9b3118c99adf78f33412f254cc066581dc7f83a Mon Sep 17 00:00:00 2001 From: Marcos Villagra <71292159+mdvillagra@users.noreply.github.com> Date: Wed, 6 Dec 2023 11:59:35 -0300 Subject: [PATCH 08/29] Add Pallas and Vesta fields (#690) * added pallas field * cargo fmt * Added Vesta field * removed unneded functions from pasta curves * deleted the serialization part * fixed error --------- Co-authored-by: Gabriel Fior --- math/src/field/fields/mod.rs | 4 ++++ math/src/field/fields/pallas_field.rs | 17 +++++++++++++++++ math/src/field/fields/vesta_field.rs | 11 +++++++++++ 3 files changed, 32 insertions(+) create mode 100644 math/src/field/fields/pallas_field.rs create mode 100644 math/src/field/fields/vesta_field.rs diff --git a/math/src/field/fields/mod.rs b/math/src/field/fields/mod.rs index 33c39d8166..0bcbd28688 100644 --- a/math/src/field/fields/mod.rs +++ b/math/src/field/fields/mod.rs @@ -5,7 +5,11 @@ pub mod mersenne31; pub mod montgomery_backed_prime_fields; /// Implementation of the Goldilocks Prime field (p = 2^448 - 2^224 - 1) pub mod p448_goldilocks_prime_field; +/// Implemenation of Pallas field +pub mod pallas_field; /// Implementation of the u64 Goldilocks Prime field (p = 2^64 - 2^32 + 1) pub mod u64_goldilocks_field; /// Implementation of prime fields over 64 bit unsigned integers. pub mod u64_prime_field; +/// Implemenation of Vesta Prime field (p = 2^254 + 45560315531506369815346746415080538113) +mod vesta_field; diff --git a/math/src/field/fields/pallas_field.rs b/math/src/field/fields/pallas_field.rs new file mode 100644 index 0000000000..aeee1e74ee --- /dev/null +++ b/math/src/field/fields/pallas_field.rs @@ -0,0 +1,17 @@ +use crate::{ + field::fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, + unsigned_integer::element::U256, +}; + +type PallasMontgomeryBackendPrimeField = MontgomeryBackendPrimeField; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct MontgomeryConfigPallas255PrimeField; +impl IsModulus for MontgomeryConfigPallas255PrimeField { + const MODULUS: U256 = U256::from_hex_unchecked( + "40000000000000000000000000000000224698fc094cf91b992d30ed00000001", + ); +} + +pub type Pallas255PrimeField = + PallasMontgomeryBackendPrimeField; diff --git a/math/src/field/fields/vesta_field.rs b/math/src/field/fields/vesta_field.rs new file mode 100644 index 0000000000..317f418333 --- /dev/null +++ b/math/src/field/fields/vesta_field.rs @@ -0,0 +1,11 @@ +use crate::{ + field::fields::montgomery_backed_prime_fields::IsModulus, unsigned_integer::element::U256, +}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct MontgomeryConfigVesta255PrimeField; +impl IsModulus for MontgomeryConfigVesta255PrimeField { + const MODULUS: U256 = U256::from_hex_unchecked( + "0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001", + ); +} From 7a84ab529a7a95b8b37225f2e6cc812f39079b4a Mon Sep 17 00:00:00 2001 From: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> Date: Wed, 6 Dec 2023 13:51:38 -0300 Subject: [PATCH 09/29] Math: Implement subfield logic (#709) * add subfield trait * implement IsSubfieldOf for Degree2ExtensionField struct in BLS12381 * fix mul * avoid using field elements in issubfield impl. Add tests * clippy and fmt * simplify trait bounds * add explicit type * change iter to into_iter * fix metal code * remove explicit type --- .../curves/bls12_381/field_extension.rs | 84 +++++++- .../curves/bls12_381/pairing.rs | 15 +- math/src/fft/gpu/metal/ops.rs | 6 +- math/src/field/element.rs | 195 ++++++++++-------- math/src/field/extensions/cubic.rs | 2 +- math/src/field/traits.rs | 50 ++++- provers/stark/src/debug.rs | 8 +- provers/stark/src/fri/fri_decommit.rs | 4 +- provers/stark/src/prover.rs | 2 +- provers/stark/src/transcript.rs | 2 +- 10 files changed, 258 insertions(+), 110 deletions(-) diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs index e8cff61963..26c34e1739 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs @@ -6,7 +6,7 @@ use crate::field::{ quadratic::{HasQuadraticNonResidue, QuadraticExtensionField}, }, fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, - traits::IsField, + traits::{IsField, IsSubFieldOf}, }; use crate::traits::ByteConversion; use crate::unsigned_integer::element::U384; @@ -71,7 +71,7 @@ impl IsField for Degree2ExtensionField { /// Returns the division of `a` and `b` fn div(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - Self::mul(a, &Self::inv(b).unwrap()) + ::mul(a, &Self::inv(b).unwrap()) } /// Returns a boolean indicating whether `a` and `b` are equal component wise. @@ -103,6 +103,47 @@ impl IsField for Degree2ExtensionField { } } +impl IsSubFieldOf for BLS12381PrimeField { + fn mul( + a: &Self::BaseType, + b: &::BaseType, + ) -> ::BaseType { + let c0 = FieldElement::from_raw(::mul(a, b[0].value())); + let c1 = FieldElement::from_raw(::mul(a, b[1].value())); + [c0, c1] + } + + fn add( + a: &Self::BaseType, + b: &::BaseType, + ) -> ::BaseType { + let c0 = FieldElement::from_raw(::add(a, b[0].value())); + let c1 = FieldElement::from_raw(*b[1].value()); + [c0, c1] + } + + fn div( + a: &Self::BaseType, + b: &::BaseType, + ) -> ::BaseType { + let b_inv = Degree2ExtensionField::inv(b).unwrap(); + >::mul(a, &b_inv) + } + + fn sub( + a: &Self::BaseType, + b: &::BaseType, + ) -> ::BaseType { + let c0 = FieldElement::from_raw(::sub(a, b[0].value())); + let c1 = FieldElement::from_raw(::neg(b[1].value())); + [c0, c1] + } + + fn embed(a: Self::BaseType) -> ::BaseType { + [FieldElement::from_raw(a), FieldElement::zero()] + } +} + impl ByteConversion for FieldElement { #[cfg(feature = "std")] fn to_bytes_be(&self) -> Vec { @@ -328,4 +369,43 @@ mod tests { assert_eq!(g_to_fp12_x, expectedx); assert_eq!(g_to_fp12_y, expectedy); } + + #[test] + fn add_base_field_with_degree_2_extension() { + let a = FieldElement::::from(3); + let a_extension = FieldElement::::from(3); + let b = FieldElement::::from(2); + assert_eq!(a + &b, a_extension + b); + } + + #[test] + fn mul_base_field_with_degree_2_extension() { + let a = FieldElement::::from(3); + let a_extension = FieldElement::::from(3); + let b = FieldElement::::from(2); + assert_eq!(a * &b, a_extension * b); + } + + #[test] + fn sub_base_field_with_degree_2_extension() { + let a = FieldElement::::from(3); + let a_extension = FieldElement::::from(3); + let b = FieldElement::::from(2); + assert_eq!(a - &b, a_extension - b); + } + + #[test] + fn div_base_field_with_degree_2_extension() { + let a = FieldElement::::from(3); + let a_extension = FieldElement::::from(3); + let b = FieldElement::::from(2); + assert_eq!(a / &b, a_extension / b); + } + + #[test] + fn embed_base_field_with_degree_2_extension() { + let a = FieldElement::::from(3); + let a_extension = FieldElement::::from(3); + assert_eq!(a.to_extension::(), a_extension); + } } diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs index 25bfb579e1..0e509dee8a 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs @@ -1,6 +1,6 @@ use super::{ curve::{BLS12381Curve, MILLER_LOOP_CONSTANT}, - field_extension::{Degree12ExtensionField, Degree2ExtensionField}, + field_extension::{BLS12381PrimeField, Degree12ExtensionField, Degree2ExtensionField}, twist::BLS12381TwistCurve, }; use crate::{ @@ -56,22 +56,23 @@ fn double_accumulate_line( let [px, py, _] = p.coordinates(); let residue = LevelTwoResidue::residue(); let two_inv = FieldElement::::new_base("d0088f51cbff34d258dd3db21a5d66bb23ba5c279c2895fb39869507b587b120f55ffff58a9ffffdcff7fffffffd556"); + let three = FieldElement::::from(3); let a = &two_inv * x1 * y1; let b = y1.square(); let c = z1.square(); - let d = FieldElement::from(3) * &c; + let d = &three * &c; let e = BLS12381TwistCurve::b() * d; - let f = FieldElement::from(3) * &e; + let f = &three * &e; let g = two_inv * (&b + &f); let h = (y1 + z1).square() - (&b + &c); let x3 = &a * (&b - &f); - let y3 = g.square() - (FieldElement::from(3) * e.square()); + let y3 = g.square() - (&three * e.square()); let z3 = &b * &h; let [h0, h1] = h.value(); - let x1_sq_3 = FieldElement::from(3) * x1.square(); + let x1_sq_3 = three * x1.square(); let [x1_sq_30, x1_sq_31] = x1_sq_3.value(); t.0.value = [x3, y3, z3]; @@ -120,7 +121,7 @@ fn add_accumulate_line( let e = &lambda * &d; let f = z1 * c; let g = x1 * d; - let h = &e + f - FieldElement::from(2) * &g; + let h = &e + f - FieldElement::::from(2) * &g; let i = y1 * &e; let x3 = &lambda * &h; @@ -195,7 +196,7 @@ fn frobenius_square( let f0 = FieldElement::new([a0.clone(), a1 * &omega_3, a2 * &omega_3_squared]); let f1 = FieldElement::new([b0.clone(), b1 * omega_3, b2 * omega_3_squared]); - FieldElement::new([f0, f1 * w_raised_to_p_squared_minus_one]) + FieldElement::new([f0, w_raised_to_p_squared_minus_one * f1]) } // To understand more about how to reduce the final exponentiation diff --git a/math/src/fft/gpu/metal/ops.rs b/math/src/fft/gpu/metal/ops.rs index c9d7131f25..c9a347dfcc 100644 --- a/math/src/fft/gpu/metal/ops.rs +++ b/math/src/fft/gpu/metal/ops.rs @@ -55,7 +55,7 @@ pub fn fft( let result = MetalState::retrieve_contents(&input_buffer); let result = bitrev_permutation::(&result, state)?; - Ok(result.iter().map(FieldElement::from_raw).collect()) + Ok(result.into_iter().map(FieldElement::from_raw).collect()) } /// Generates 2^{`order-1`} twiddle factors in parallel, with a certain `config`, in Metal. @@ -89,7 +89,7 @@ pub fn gen_twiddles( let (command_buffer, command_encoder) = state.setup_command(&pipeline, Some(&[(0, &result_buffer)])); - let root = F::get_primitive_root_of_unity::(order).unwrap(); + let root = F::get_primitive_root_of_unity(order).unwrap(); command_encoder.set_bytes(1, mem::size_of::() as u64, void_ptr(&root)); let grid_size = MTLSize::new(len as u64, 1, 1); @@ -103,7 +103,7 @@ pub fn gen_twiddles( }); let result = MetalState::retrieve_contents(&result_buffer); - Ok(result.iter().map(FieldElement::from_raw).collect()) + Ok(result.into_iter().map(FieldElement::from_raw).collect()) } /// Executes a parallel bit-reverse permutation with the elements of `input`, in Metal. diff --git a/math/src/field/element.rs b/math/src/field/element.rs index 3c1f864843..67707b791c 100644 --- a/math/src/field/element.rs +++ b/math/src/field/element.rs @@ -32,7 +32,7 @@ use serde::ser::{Serialize, SerializeStruct, Serializer}; use serde::Deserialize; use super::fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}; -use super::traits::{IsPrimeField, LegendreSymbol}; +use super::traits::{IsPrimeField, IsSubFieldOf, LegendreSymbol}; /// A field element with operations algorithms defined in `F` #[allow(clippy::derived_hash_with_manual_eq)] @@ -95,10 +95,8 @@ where F::BaseType: Clone, F: IsField, { - pub fn from_raw(value: &F::BaseType) -> Self { - Self { - value: value.clone(), - } + pub fn from_raw(value: F::BaseType) -> Self { + Self { value } } pub const fn const_from_raw(value: F::BaseType) -> Self { @@ -119,59 +117,64 @@ where impl Eq for FieldElement where F: IsField {} /// Addition operator overloading for field elements -impl Add<&FieldElement> for &FieldElement +impl Add<&FieldElement> for &FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn add(self, rhs: &FieldElement) -> Self::Output { + fn add(self, rhs: &FieldElement) -> Self::Output { Self::Output { - value: F::add(&self.value, &rhs.value), + value: >::add(&self.value, &rhs.value), } } } -impl Add> for FieldElement +impl Add> for FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn add(self, rhs: FieldElement) -> Self::Output { + fn add(self, rhs: FieldElement) -> Self::Output { &self + &rhs } } -impl Add<&FieldElement> for FieldElement +impl Add<&FieldElement> for FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn add(self, rhs: &FieldElement) -> Self::Output { + fn add(self, rhs: &FieldElement) -> Self::Output { &self + rhs } } -impl Add> for &FieldElement +impl Add> for &FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn add(self, rhs: FieldElement) -> Self::Output { + fn add(self, rhs: FieldElement) -> Self::Output { self + &rhs } } /// AddAssign operator overloading for field elements -impl AddAssign> for FieldElement +impl AddAssign> for FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { fn add_assign(&mut self, rhs: FieldElement) { - self.value = F::add(&self.value, &rhs.value); + self.value = >::add(&rhs.value, &self.value); } } @@ -186,142 +189,154 @@ where } /// Subtraction operator overloading for field elements*/ -impl Sub<&FieldElement> for &FieldElement +impl Sub<&FieldElement> for &FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn sub(self, rhs: &FieldElement) -> Self::Output { + fn sub(self, rhs: &FieldElement) -> Self::Output { Self::Output { - value: F::sub(&self.value, &rhs.value), + value: >::sub(&self.value, &rhs.value), } } } -impl Sub> for FieldElement +impl Sub> for FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn sub(self, rhs: FieldElement) -> Self::Output { + fn sub(self, rhs: FieldElement) -> Self::Output { &self - &rhs } } -impl Sub<&FieldElement> for FieldElement +impl Sub<&FieldElement> for FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn sub(self, rhs: &FieldElement) -> Self::Output { + fn sub(self, rhs: &FieldElement) -> Self::Output { &self - rhs } } -impl Sub> for &FieldElement +impl Sub> for &FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn sub(self, rhs: FieldElement) -> Self::Output { + fn sub(self, rhs: FieldElement) -> Self::Output { self - &rhs } } /// Multiplication operator overloading for field elements*/ -impl Mul<&FieldElement> for &FieldElement +impl Mul<&FieldElement> for &FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn mul(self, rhs: &FieldElement) -> Self::Output { + fn mul(self, rhs: &FieldElement) -> Self::Output { Self::Output { - value: F::mul(&self.value, &rhs.value), + value: >::mul(&self.value, &rhs.value), } } } -impl Mul> for FieldElement +impl Mul> for FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn mul(self, rhs: FieldElement) -> Self::Output { + fn mul(self, rhs: FieldElement) -> Self::Output { &self * &rhs } } -impl Mul<&FieldElement> for FieldElement +impl Mul<&FieldElement> for FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn mul(self, rhs: &FieldElement) -> Self::Output { + fn mul(self, rhs: &FieldElement) -> Self::Output { &self * rhs } } -impl Mul> for &FieldElement +impl Mul> for &FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn mul(self, rhs: FieldElement) -> Self::Output { + fn mul(self, rhs: FieldElement) -> Self::Output { self * &rhs } } /// Division operator overloading for field elements*/ -impl Div<&FieldElement> for &FieldElement +impl Div<&FieldElement> for &FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn div(self, rhs: &FieldElement) -> Self::Output { + fn div(self, rhs: &FieldElement) -> Self::Output { Self::Output { - value: F::div(&self.value, &rhs.value), + value: >::div(&self.value, &rhs.value), } } } -impl Div> for FieldElement +impl Div> for FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn div(self, rhs: FieldElement) -> Self::Output { + fn div(self, rhs: FieldElement) -> Self::Output { &self / &rhs } } -impl Div<&FieldElement> for FieldElement +impl Div<&FieldElement> for FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn div(self, rhs: &FieldElement) -> Self::Output { + fn div(self, rhs: &FieldElement) -> Self::Output { &self / rhs } } -impl Div> for &FieldElement +impl Div> for &FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn div(self, rhs: FieldElement) -> Self::Output { + fn div(self, rhs: FieldElement) -> Self::Output { self / &rhs } } @@ -418,6 +433,16 @@ where pub fn zero() -> Self { Self { value: F::zero() } } + + #[inline(always)] + pub fn to_extension(self) -> FieldElement + where + F: IsSubFieldOf, + { + FieldElement { + value: >::embed(self.value), + } + } } impl FieldElement { @@ -450,7 +475,11 @@ impl FieldElement { } #[cfg(feature = "lambdaworks-serde-binary")] -impl Serialize for FieldElement { +impl Serialize for FieldElement +where + F: IsField, + F::BaseType: ByteConversion, +{ fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -478,7 +507,11 @@ impl Serialize for FieldElement { } #[cfg(feature = "lambdaworks-serde-binary")] -impl<'de, F: IsPrimeField> Deserialize<'de> for FieldElement { +impl<'de, F> Deserialize<'de> for FieldElement +where + F: IsField, + F::BaseType: ByteConversion, +{ fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, @@ -491,7 +524,7 @@ impl<'de, F: IsPrimeField> Deserialize<'de> for FieldElement { struct FieldElementVisitor(PhantomData F>); - impl<'de, F: IsPrimeField> Visitor<'de> for FieldElementVisitor { + impl<'de, F: IsField> Visitor<'de> for FieldElementVisitor { type Value = FieldElement; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { @@ -515,7 +548,7 @@ impl<'de, F: IsPrimeField> Deserialize<'de> for FieldElement { } let value = value.ok_or_else(|| de::Error::missing_field("value"))?; let val = F::BaseType::from_bytes_be(&value).unwrap(); - Ok(FieldElement::from_raw(&val)) + Ok(FieldElement::from_raw(val)) } fn visit_seq(self, mut seq: S) -> Result, S::Error> @@ -531,7 +564,7 @@ impl<'de, F: IsPrimeField> Deserialize<'de> for FieldElement { } let value = value.ok_or_else(|| de::Error::missing_field("value"))?; let val = F::BaseType::from_bytes_be(&value).unwrap(); - Ok(FieldElement::from_raw(&val)) + Ok(FieldElement::from_raw(val)) } } diff --git a/math/src/field/extensions/cubic.rs b/math/src/field/extensions/cubic.rs index ac57d7dfe7..447c790f3d 100644 --- a/math/src/field/extensions/cubic.rs +++ b/math/src/field/extensions/cubic.rs @@ -106,7 +106,7 @@ where fn inv( a: &[FieldElement; 3], ) -> Result<[FieldElement; 3], FieldError> { - let three = FieldElement::from(3_u64); + let three = FieldElement::::from(3_u64); let d = a[0].pow(3_u64) + a[1].pow(3_u64) * Q::residue() diff --git a/math/src/field/traits.rs b/math/src/field/traits.rs index e30bc66a7b..8441b70825 100644 --- a/math/src/field/traits.rs +++ b/math/src/field/traits.rs @@ -14,6 +14,44 @@ pub enum RootsConfig { BitReverseInversed, // same as above but exponents are negated. } +pub trait IsSubFieldOf: IsField { + fn mul(a: &Self::BaseType, b: &F::BaseType) -> F::BaseType; + fn add(a: &Self::BaseType, b: &F::BaseType) -> F::BaseType; + fn div(a: &Self::BaseType, b: &F::BaseType) -> F::BaseType; + fn sub(a: &Self::BaseType, b: &F::BaseType) -> F::BaseType; + fn embed(a: Self::BaseType) -> F::BaseType; +} + +impl IsSubFieldOf for F +where + F: IsField, +{ + #[inline(always)] + fn mul(a: &Self::BaseType, b: &F::BaseType) -> F::BaseType { + F::mul(a, b) + } + + #[inline(always)] + fn add(a: &Self::BaseType, b: &F::BaseType) -> F::BaseType { + F::add(a, b) + } + + #[inline(always)] + fn sub(a: &Self::BaseType, b: &F::BaseType) -> F::BaseType { + F::sub(a, b) + } + + #[inline(always)] + fn div(a: &Self::BaseType, b: &F::BaseType) -> F::BaseType { + F::div(a, b) + } + + #[inline(always)] + fn embed(a: Self::BaseType) -> F::BaseType { + a + } +} + /// Trait to define necessary parameters for FFT-friendly Fields. /// Two-Adic fields are ones whose order is of the form $2^n k + 1$. /// Here $n$ is usually called the *two-adicity* of the field. The @@ -22,7 +60,7 @@ pub enum RootsConfig { /// A two-adic primitive root of unity is a number w that satisfies w^(2^n) = 1 /// and w^(j) != 1 for every j below 2^n. With this primitive root we can generate /// any other root of unity we need to perform FFT. -pub trait IsFFTField: IsPrimeField { +pub trait IsFFTField: IsField { const TWO_ADICITY: u64; const TWO_ADIC_PRIMITVE_ROOT_OF_UNITY: Self::BaseType; @@ -33,18 +71,16 @@ pub trait IsFFTField: IsPrimeField { } /// Returns a primitive root of unity of order $2^{order}$. - fn get_primitive_root_of_unity( - order: u64, - ) -> Result, FieldError> { + fn get_primitive_root_of_unity(order: u64) -> Result, FieldError> { let two_adic_primitive_root_of_unity = - FieldElement::new(F::TWO_ADIC_PRIMITVE_ROOT_OF_UNITY); + FieldElement::new(Self::TWO_ADIC_PRIMITVE_ROOT_OF_UNITY); if order == 0 { return Ok(FieldElement::one()); } - if order > F::TWO_ADICITY { + if order > Self::TWO_ADICITY { return Err(FieldError::RootOfUnityError(order)); } - let log_power = F::TWO_ADICITY - order; + let log_power = Self::TWO_ADICITY - order; let root = (0..log_power).fold(two_adic_primitive_root_of_unity, |acc, _| acc.square()); Ok(root) } diff --git a/provers/stark/src/debug.rs b/provers/stark/src/debug.rs index d1abd51d26..1b02007a20 100644 --- a/provers/stark/src/debug.rs +++ b/provers/stark/src/debug.rs @@ -51,7 +51,7 @@ pub fn validate_trace>( if &boundary_value != trace_value { ret = false; - error!("Boundary constraint inconsistency - Expected value {} in step {} and column {}, found: {}", boundary_value.representative(), step, col, trace_value.representative()); + error!("Boundary constraint inconsistency - Expected value {:?} in step {} and column {}, found: {:?}", boundary_value, step, col, trace_value); } }); @@ -84,10 +84,8 @@ pub fn validate_trace>( if step < exemption_steps[i] && eval != &FieldElement::::zero() { ret = false; error!( - "Inconsistent evaluation of transition {} in step {} - expected 0, got {}", - i, - step, - eval.representative() + "Inconsistent evaluation of transition {} in step {} - expected 0, got {:?}", + i, step, eval ); } }) diff --git a/provers/stark/src/fri/fri_decommit.rs b/provers/stark/src/fri/fri_decommit.rs index 63da9ec5fd..8bc378ac82 100644 --- a/provers/stark/src/fri/fri_decommit.rs +++ b/provers/stark/src/fri/fri_decommit.rs @@ -2,12 +2,12 @@ pub use lambdaworks_crypto::fiat_shamir::transcript::Transcript; use lambdaworks_crypto::merkle_tree::proof::Proof; use lambdaworks_math::field::element::FieldElement; -use lambdaworks_math::field::traits::IsPrimeField; +use lambdaworks_math::field::traits::IsField; use crate::config::Commitment; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub struct FriDecommitment { +pub struct FriDecommitment { pub layers_auth_paths: Vec>, pub layers_evaluations_sym: Vec>, } diff --git a/provers/stark/src/prover.rs b/provers/stark/src/prover.rs index 2b21455f58..bc8cd9449c 100644 --- a/provers/stark/src/prover.rs +++ b/provers/stark/src/prover.rs @@ -964,7 +964,7 @@ mod tests { for i in 0..(trace_length * blowup_factor) { assert_eq!( domain.lde_roots_of_unity_coset[i], - FieldElement::from(coset_offset) * primitive_root.pow(i) + primitive_root.pow(i) * FieldElement::from(coset_offset) ); } } diff --git a/provers/stark/src/transcript.rs b/provers/stark/src/transcript.rs index b6952b70bb..4349b30929 100644 --- a/provers/stark/src/transcript.rs +++ b/provers/stark/src/transcript.rs @@ -138,7 +138,7 @@ impl IsStarkTranscript for StoneProverTranscript { while result >= Self::MODULUS_MAX_MULTIPLE { result = self.sample_big_int(); } - FieldElement::new(result) * FieldElement::new(Self::R_INV) + FieldElement::::new(result) * FieldElement::new(Self::R_INV) } fn sample_u64(&mut self, upper_bound: u64) -> u64 { From cd957b1f710e72985c5c867e85a11aaf15542c06 Mon Sep 17 00:00:00 2001 From: Marcos Villagra <71292159+mdvillagra@users.noreply.github.com> Date: Wed, 6 Dec 2023 16:25:57 -0300 Subject: [PATCH 10/29] Babybear quadratic extension (#629) * constructing quadratic babybear * adding unit tests * cargo clippy * added unit tests * fixed error * updated file name --------- Co-authored-by: Mauro Toscano <12560266+MauroToscano@users.noreply.github.com> --- math/src/field/fields/fft_friendly/mod.rs | 4 +- .../fields/fft_friendly/quadratic_babybear.rs | 80 +++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 math/src/field/fields/fft_friendly/quadratic_babybear.rs diff --git a/math/src/field/fields/fft_friendly/mod.rs b/math/src/field/fields/fft_friendly/mod.rs index 92b6a52868..a9b2f0f2eb 100644 --- a/math/src/field/fields/fft_friendly/mod.rs +++ b/math/src/field/fields/fft_friendly/mod.rs @@ -1,6 +1,8 @@ /// Implemenation of the Babybear Prime Field p = 2^31 - 2^27 + 1 pub mod babybear; -/// Implementation of two-adic Prime Field over 256 bit unsigned integers. +/// Implemenation of the quadratic extension of the babybear field +pub mod quadratic_babybear; +/// Implementation of two-adic prime field over 256 bit unsigned integers. pub mod stark_252_prime_field; /// Implemenation of the Goldilocks Prime Field p = 2^64 - 2^32 + 1 pub mod u64_goldilocks; diff --git a/math/src/field/fields/fft_friendly/quadratic_babybear.rs b/math/src/field/fields/fft_friendly/quadratic_babybear.rs new file mode 100644 index 0000000000..2a1990a198 --- /dev/null +++ b/math/src/field/fields/fft_friendly/quadratic_babybear.rs @@ -0,0 +1,80 @@ +use crate::field::{ + element::FieldElement, extensions::quadratic::*, + fields::fft_friendly::babybear::Babybear31PrimeField, +}; + +/// Quadratic field extension of Babybear +pub type QuadraticBabybearField = QuadraticExtensionField; + +/// Field element type for the quadratic extension of Babybear +pub type QuadraticBabybearFieldElement = QuadraticExtensionFieldElement; + +impl HasQuadraticNonResidue for Babybear31PrimeField { + type BaseField = Babybear31PrimeField; + + fn residue() -> FieldElement { + -FieldElement::one() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + type FE = FieldElement; + type Fee = QuadraticBabybearFieldElement; + + #[test] + fn test_add_quadratic() { + let a = Fee::new([FE::from(0), FE::from(3)]); + let b = Fee::new([-FE::from(2), FE::from(8)]); + let expected_result = Fee::new([FE::from(0) - FE::from(2), FE::from(3) + FE::from(8)]); + assert_eq!(a + b, expected_result); + } + + #[test] + fn test_sub_quadratic() { + let a = Fee::new([FE::from(0), FE::from(3)]); + let b = Fee::new([-FE::from(2), FE::from(8)]); + let expected_result = Fee::new([FE::from(0) + FE::from(2), FE::from(3) - FE::from(8)]); + assert_eq!(a - b, expected_result); + } + + #[test] + fn test_mul_quadratic() { + let a = Fee::new([FE::from(12), FE::from(5)]); + let b = Fee::new([-FE::from(4), FE::from(2)]); + let expected_result = Fee::new([ + FE::from(12) * (-FE::from(4)) + + FE::from(5) * FE::from(2) * Babybear31PrimeField::residue(), + FE::from(12) * FE::from(2) + FE::from(5) * (-FE::from(4)), + ]); + assert_eq!(a * b, expected_result); + } + + #[test] + fn test_inv_quadratic() { + let a = Fee::new([FE::from(12), FE::from(5)]); + let inv_norm = (FE::from(12).pow(2_u64) + - Babybear31PrimeField::residue() * FE::from(5).pow(2_u64)) + .inv() + .unwrap(); + let expected_result = Fee::new([FE::from(12) * &inv_norm, -&FE::from(5) * inv_norm]); + assert_eq!(a.inv().unwrap(), expected_result); + } + + #[test] + fn test_div_quadratic() { + let a = Fee::new([FE::from(12), FE::from(5)]); + let b = Fee::new([-FE::from(4), FE::from(2)]); + let expected_result = &a * b.inv().unwrap(); + assert_eq!(a / b, expected_result); + } + + #[test] + fn test_conjugate_quadratic() { + let a = Fee::new([FE::from(12), FE::from(5)]); + let expected_result = Fee::new([FE::from(12), -FE::from(5)]); + assert_eq!(a.conjugate(), expected_result); + } +} From 2828a9f21ce69e26c0914f2138faeb9c07aea048 Mon Sep 17 00:00:00 2001 From: juan518munoz <62400508+juan518munoz@users.noreply.github.com> Date: Wed, 6 Dec 2023 16:56:52 -0300 Subject: [PATCH 11/29] Public inputs refactor (#676) * rename MemorySegment to SegmentName * add SegmentName enum options * Segment struct declaration * initial integratation of struct Segment * rmv comment * update cairo-vm * change cairo-vm branch * add ecdsa & pedersen * test pub inputs from vm * public inputs most fields derived from vm * clippy & fmt * update cairo-vm dep to latest rev * get memory segment from vm * rename data_len to codelen * Extract public memory directly from public inputs of Cairo VM * Refactor get_memory_holes function * Remove unnecessary function and legacy test * Remove some commented code and fix some tests * Solve clippy issues * Remove legacy test * Fix some comments on tests * Refactor pub addresses in add_pub_memory_in_public_input_section function * Refactor segment_size method * iterate over addr value pairs in add_pub_memory_in_public_input_section function --------- Co-authored-by: Mariano Nicolini --- exercises/message/src/cairo/air.rs | 3 +- provers/cairo/Cargo.toml | 2 +- provers/cairo/src/air.rs | 305 +++++++-------------------- provers/cairo/src/execution_trace.rs | 60 ++++-- provers/cairo/src/runner/run.rs | 45 +++- 5 files changed, 149 insertions(+), 266 deletions(-) diff --git a/exercises/message/src/cairo/air.rs b/exercises/message/src/cairo/air.rs index 4afa9076c4..d361058279 100644 --- a/exercises/message/src/cairo/air.rs +++ b/exercises/message/src/cairo/air.rs @@ -485,8 +485,7 @@ fn add_pub_memory_in_public_input_section( let mut v_aux = values.to_owned(); let public_input_section = addresses.len() - public_input.public_memory.len(); - let output_range = public_input.memory_segments.get(&MemorySegment::Output); - let pub_memory_addrs = get_pub_memory_addrs(output_range, public_input); + let pub_memory_addrs = public_input.public_memory.keys().cloned().collect(); a_aux.splice(public_input_section.., pub_memory_addrs); for i in public_input_section..a_aux.len() { diff --git a/provers/cairo/Cargo.toml b/provers/cairo/Cargo.toml index f503ef547c..f9fcdb58ec 100644 --- a/provers/cairo/Cargo.toml +++ b/provers/cairo/Cargo.toml @@ -23,7 +23,7 @@ thiserror = "1.0.38" log = "0.4.17" bincode = { version = "2.0.0-rc.2", tag = "v2.0.0-rc.2", git = "https://github.com/bincode-org/bincode.git", features= ['serde'] } # NOTE: For cairo 1 compatibility, add the `cairo-1-hints` feature. -cairo-vm = { git = "https://github.com/lambdaclass/cairo-vm", rev = "e763cef", default-features = false } +cairo-vm = { git = "https://github.com/lambdaclass/cairo-vm", rev = "e61ae177edb94e29470fed23bdda43329b16c057", default-features = false } sha3 = "0.10.6" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/provers/cairo/src/air.rs b/provers/cairo/src/air.rs index 48b0b67ee0..a85952d6ea 100644 --- a/provers/cairo/src/air.rs +++ b/provers/cairo/src/air.rs @@ -1,6 +1,4 @@ -use std::ops::Range; - -use cairo_vm::without_std::collections::HashMap; +use cairo_vm::{air_public_input::MemorySegmentAddresses, without_std::collections::HashMap}; use lambdaworks_math::{ errors::DeserializationError, field::{ @@ -160,12 +158,62 @@ pub const MEM_P_TRACE_OFFSET: usize = 17; pub const MEM_A_TRACE_OFFSET: usize = 19; #[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] -pub enum MemorySegment { +pub enum SegmentName { RangeCheck, Output, + Program, + Execution, + Ecdsa, + Pedersen, } -pub type MemorySegmentMap = HashMap>; +impl From<&str> for SegmentName { + fn from(value: &str) -> Self { + match value { + "range_check" => SegmentName::RangeCheck, + "output" => SegmentName::Output, + "program" => SegmentName::Program, + "execution" => SegmentName::Execution, + "ecdsa" => SegmentName::Ecdsa, + "pedersen" => SegmentName::Pedersen, + n => panic!("Invalid segment name {n}"), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Segment { + pub begin_addr: usize, + pub stop_ptr: usize, +} + +impl Segment { + pub fn new(begin_addr: u64, stop_ptr: u64) -> Self { + let begin_addr: usize = begin_addr.try_into().unwrap(); + let stop_ptr: usize = stop_ptr.try_into().unwrap(); + + stop_ptr.checked_sub(begin_addr).unwrap(); + + Self { + begin_addr, + stop_ptr, + } + } + pub fn segment_size(&self) -> usize { + self.stop_ptr - self.begin_addr - 1 + } +} + +impl From<&MemorySegmentAddresses> for Segment { + fn from(value: &MemorySegmentAddresses) -> Self { + Self { + begin_addr: value.begin_addr, + stop_ptr: value.stop_ptr, + } + } +} + +pub type MemorySegmentMap = HashMap; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct PublicInputs { @@ -186,7 +234,6 @@ pub struct PublicInputs { pub memory_segments: MemorySegmentMap, pub public_memory: HashMap, pub num_steps: usize, // number of execution steps - pub codelen: usize, // length of the program segment } impl PublicInputs { @@ -215,7 +262,6 @@ impl PublicInputs { memory_segments: MemorySegmentMap::new(), public_memory, num_steps: register_states.steps(), - codelen, } } } @@ -249,12 +295,16 @@ impl Serializable for PublicInputs { let mut memory_segment_bytes = vec![]; for (segment, range) in self.memory_segments.iter() { let segment_type = match segment { - MemorySegment::RangeCheck => 0u8, - MemorySegment::Output => 1u8, + SegmentName::RangeCheck => 0u8, + SegmentName::Output => 1u8, + SegmentName::Program => 2u8, + SegmentName::Execution => 3u8, + SegmentName::Ecdsa => 4u8, + SegmentName::Pedersen => 5u8, }; memory_segment_bytes.extend(segment_type.to_be_bytes()); - memory_segment_bytes.extend(range.start.to_be_bytes()); - memory_segment_bytes.extend(range.end.to_be_bytes()); + memory_segment_bytes.extend(range.begin_addr.to_be_bytes()); + memory_segment_bytes.extend(range.stop_ptr.to_be_bytes()); } let memory_segment_length = self.memory_segments.len(); bytes.extend(memory_segment_length.to_be_bytes()); @@ -270,7 +320,6 @@ impl Serializable for PublicInputs { bytes.extend(public_memory_bytes); bytes.extend(self.num_steps.to_be_bytes()); - bytes.extend(self.codelen.to_be_bytes()); bytes } @@ -377,8 +426,12 @@ impl Deserializable for PublicInputs { return Err(DeserializationError::InvalidAmountOfBytes); } let segment_type = match bytes[0] { - 0 => MemorySegment::RangeCheck, - 1 => MemorySegment::Output, + 0u8 => SegmentName::RangeCheck, + 1u8 => SegmentName::Output, + 2u8 => SegmentName::Program, + 3u8 => SegmentName::Execution, + 4u8 => SegmentName::Ecdsa, + 5u8 => SegmentName::Pedersen, _ => return Err(DeserializationError::FieldFromBytesError), }; bytes = &bytes[1..]; @@ -398,7 +451,7 @@ impl Deserializable for PublicInputs { .map_err(|_| DeserializationError::InvalidAmountOfBytes)?, ); bytes = &bytes[8..]; - memory_segments.insert(segment_type, start..end); + memory_segments.insert(segment_type, Segment::new(start, end)); } let mut public_memory = HashMap::new(); @@ -434,14 +487,6 @@ impl Deserializable for PublicInputs { .map_err(|_| DeserializationError::InvalidAmountOfBytes)?, ); - let codelen = usize::from_be_bytes( - bytes - .get(0..8) - .ok_or(DeserializationError::InvalidAmountOfBytes)? - .try_into() - .map_err(|_| DeserializationError::InvalidAmountOfBytes)?, - ); - Ok(Self { pc_init, ap_init, @@ -453,7 +498,6 @@ impl Deserializable for PublicInputs { memory_segments, public_memory, num_steps, - codelen, }) } } @@ -487,10 +531,7 @@ fn add_pub_memory_in_public_input_section( let mut a_aux = addresses.to_owned(); let mut v_aux = values.to_owned(); - let output_range = public_input.memory_segments.get(&MemorySegment::Output); - - let pub_addrs = get_pub_memory_addrs(output_range, public_input); - let mut pub_addrs_iter = pub_addrs.iter(); + let mut pub_addrs = public_input.public_memory.iter(); // Iterate over addresses for (i, a) in a_aux.iter_mut().enumerate() { @@ -498,9 +539,9 @@ fn add_pub_memory_in_public_input_section( if a == &Felt252::zero() { // While there are public memory addresses left, overwrite the dummy // (addr, value) accesses with the real public memory pairs. - if let Some(pub_addr) = pub_addrs_iter.next() { + if let Some((pub_addr, pub_value)) = pub_addrs.next() { *a = *pub_addr; - v_aux[i] = *public_input.public_memory.get(pub_addr).unwrap(); + v_aux[i] = *pub_value; } else { // When there are no public memory pairs left to write, break the // loop and return the (addr, value) pairs with dummy accesses @@ -513,29 +554,6 @@ fn add_pub_memory_in_public_input_section( (a_aux, v_aux) } -/// Gets public memory addresses of a program. First, this function builds a `Vec` of `FieldElement`s, filling it -/// incrementally with addresses from `1` to `program_len - 1`, where `program_len` is the length of the program. -/// If the output builtin is used, `output_range` is `Some(...)` and this function adds incrementally to the resulting -/// `Vec` addresses from the start to the end of the unwrapped `output_range`. -fn get_pub_memory_addrs( - output_range: Option<&Range>, - public_input: &PublicInputs, -) -> Vec> { - let public_memory_len = public_input.public_memory.len() as u64; - - if let Some(output_range) = output_range { - let output_section = output_range.end - output_range.start; - let program_section = public_memory_len - output_section; - - (1..=program_section) - .map(FieldElement::from) - .chain(output_range.clone().map(FieldElement::from)) - .collect() - } else { - (1..=public_memory_len).map(FieldElement::from).collect() - } -} - fn sort_columns_by_memory_address( adresses: Vec, values: Vec, @@ -1287,181 +1305,6 @@ mod test { use super::*; use lambdaworks_math::field::element::FieldElement; - #[test] - fn test_build_auxiliary_trace_add_program_in_public_input_section_works() { - let dummy_public_input = PublicInputs { - pc_init: FieldElement::zero(), - ap_init: FieldElement::zero(), - fp_init: FieldElement::zero(), - pc_final: FieldElement::zero(), - ap_final: FieldElement::zero(), - public_memory: HashMap::from([ - (FieldElement::one(), FieldElement::from(10)), - (FieldElement::from(2), FieldElement::from(20)), - ]), - range_check_max: None, - range_check_min: None, - num_steps: 1, - memory_segments: MemorySegmentMap::new(), - codelen: 3, - }; - - let a = vec![ - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::zero(), - FieldElement::from(2), - FieldElement::from(2), - FieldElement::from(2), - FieldElement::from(2), - FieldElement::zero(), - ]; - let v = vec![ - FieldElement::one(), - FieldElement::one(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - ]; - let (ap, vp) = add_pub_memory_in_public_input_section(&a, &v, &dummy_public_input); - assert_eq!( - ap, - vec![ - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::from(2), - FieldElement::from(2), - FieldElement::from(2), - FieldElement::from(2), - FieldElement::from(2), - ] - ); - assert_eq!( - vp, - vec![ - FieldElement::one(), - FieldElement::one(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::from(10), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::from(20), - ] - ); - } - - #[test] - fn test_build_auxiliary_trace_add_program_with_output_in_public_input_section_works() { - let dummy_public_input = PublicInputs { - pc_init: FieldElement::zero(), - ap_init: FieldElement::zero(), - fp_init: FieldElement::zero(), - pc_final: FieldElement::zero(), - ap_final: FieldElement::zero(), - public_memory: HashMap::from([ - (FieldElement::one(), FieldElement::from(10)), - (FieldElement::from(2), FieldElement::from(20)), - (FieldElement::from(20), FieldElement::from(40)), - ]), - range_check_max: None, - range_check_min: None, - num_steps: 1, - memory_segments: MemorySegmentMap::from([(MemorySegment::Output, 20..21)]), - codelen: 3, - }; - - let a = vec![ - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::zero(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::zero(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::zero(), - ]; - - let v = vec![ - FieldElement::one(), - FieldElement::one(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - ]; - - let (ap, vp) = add_pub_memory_in_public_input_section(&a, &v, &dummy_public_input); - assert_eq!( - ap, - vec![ - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::from(2), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::from(20), - ] - ); - assert_eq!( - vp, - vec![ - FieldElement::one(), - FieldElement::one(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::from(10), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::from(20), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::from(40), - ] - ); - } - #[test] fn test_build_auxiliary_trace_sort_columns_by_memory_address() { let a = vec![ @@ -1560,7 +1403,7 @@ mod prop_test { Felt252, }; - use super::{MemorySegment, MemorySegmentMap, PublicInputs}; + use super::{MemorySegmentMap, PublicInputs, Segment, SegmentName}; prop_compose! { fn some_felt()(base in any::(), exponent in any::()) -> Felt252 { @@ -1579,10 +1422,9 @@ mod prop_test { range_check_max in proptest::option::of(any::()), range_check_min in proptest::option::of(any::()), num_steps in any::(), - codelen in any::(), ) -> PublicInputs { let public_memory = public_memory.iter().map(|(k, v)| (Felt252::from(*k), Felt252::from(*v))).collect(); - let memory_segments = MemorySegmentMap::from([(MemorySegment::Output, 10u64..16u64), (MemorySegment::RangeCheck, 20u64..71u64)]); + let memory_segments = MemorySegmentMap::from([(SegmentName::Output, Segment::new(10u64, 16u64)), (SegmentName::RangeCheck, Segment::new(20u64, 71u64))]); PublicInputs { pc_init, ap_init, @@ -1594,7 +1436,6 @@ mod prop_test { range_check_min, num_steps, memory_segments, - codelen, } } } diff --git a/provers/cairo/src/execution_trace.rs b/provers/cairo/src/execution_trace.rs index c67eb7cfff..075aa86d70 100644 --- a/provers/cairo/src/execution_trace.rs +++ b/provers/cairo/src/execution_trace.rs @@ -9,13 +9,15 @@ use super::{ }, register_states::RegisterStates, }; +use crate::air::{EXTRA_ADDR, RC_HOLES}; use crate::{ air::{ - PublicInputs, EXTRA_ADDR, FRAME_DST_ADDR, FRAME_OP0_ADDR, FRAME_OP1_ADDR, FRAME_PC, - OFF_DST, OFF_OP0, OFF_OP1, RC_HOLES, + PublicInputs, FRAME_DST_ADDR, FRAME_OP0_ADDR, FRAME_OP1_ADDR, FRAME_PC, OFF_DST, OFF_OP0, + OFF_OP1, }, Felt252, }; +use cairo_vm::without_std::collections::HashMap; use lambdaworks_math::{ field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, unsigned_integer::element::UnsignedInteger, @@ -55,11 +57,15 @@ pub fn build_main_trace( address_cols.sort_by_key(|x| x.representative()); let (rc_holes, rc_min, rc_max) = get_rc_holes(&main_trace, &[OFF_DST, OFF_OP0, OFF_OP1]); - public_input.range_check_min = Some(rc_min); - public_input.range_check_max = Some(rc_max); + + // this will avaluate to true if the public inputs weren't obtained from the run_program() function + if public_input.range_check_min.is_none() && public_input.range_check_max.is_none() { + public_input.range_check_min = Some(rc_min); + public_input.range_check_max = Some(rc_max); + } fill_rc_holes(&mut main_trace, &rc_holes); - let memory_holes = get_memory_holes(&address_cols, public_input.codelen); + let memory_holes = get_memory_holes(&address_cols, &public_input.public_memory); if !memory_holes.is_empty() { fill_memory_holes(&mut main_trace, &memory_holes); @@ -152,8 +158,11 @@ fn fill_rc_holes(trace: &mut CairoTraceTable, holes: &[Felt252]) { /// # Arguments /// /// * `sorted_addrs` - Vector of sorted memory addresses. -/// * `codelen` - the length of the Cairo program instructions. -fn get_memory_holes(sorted_addrs: &[Felt252], codelen: usize) -> Vec { +/// * `pub_memory` - The public memory of the executed program. +fn get_memory_holes( + sorted_addrs: &[Felt252], + pub_memory: &HashMap, +) -> Vec { let mut memory_holes = Vec::new(); let mut prev_addr = &sorted_addrs[0]; @@ -163,14 +172,11 @@ fn get_memory_holes(sorted_addrs: &[Felt252], codelen: usize) -> Vec { // If the candidate memory hole has an address belonging to the program segment (public // memory), that is not accounted here since public memory is added in a posterior step of // the protocol. - if addr_diff != Felt252::one() - && addr_diff != Felt252::zero() - && addr.representative() > (codelen as u64).into() - { + if addr_diff != Felt252::one() && addr_diff != Felt252::zero() { let mut hole_addr = prev_addr + Felt252::one(); while hole_addr.representative() < addr.representative() { - if hole_addr.representative() > (codelen as u64).into() { + if !pub_memory.contains_key(&hole_addr) { memory_holes.push(hole_addr); } hole_addr += Felt252::one(); @@ -650,16 +656,16 @@ mod test { } #[test] - fn test_get_memory_holes_no_codelen() { + fn test_get_memory_holes_empty_pub_memory() { // We construct a sorted addresses list [1, 2, 3, 6, 7, 8, 9, 13, 14, 15], and - // set codelen = 0. With this value of codelen, any holes present between + // an empty public memory. This way, any holes present between // the min and max addresses should be returned by the function. let mut addrs: Vec = (1..4).map(Felt252::from).collect(); let addrs_extension: Vec = (6..10).map(Felt252::from).collect(); addrs.extend_from_slice(&addrs_extension); let addrs_extension: Vec = (13..16).map(Felt252::from).collect(); addrs.extend_from_slice(&addrs_extension); - let codelen = 0; + let pub_memory = HashMap::new(); let expected_memory_holes = vec![ Felt252::from(4), @@ -668,7 +674,7 @@ mod test { Felt252::from(11), Felt252::from(12), ]; - let calculated_memory_holes = get_memory_holes(&addrs, codelen); + let calculated_memory_holes = get_memory_holes(&addrs, &pub_memory); assert_eq!(expected_memory_holes, calculated_memory_holes); } @@ -676,15 +682,20 @@ mod test { #[test] fn test_get_memory_holes_inside_program_section() { // We construct a sorted addresses list [1, 2, 3, 8, 9] and we - // set a codelen of 9. Since all the holes will be inside the + // set public memory from address 1 to 9. Since all the holes will be inside the // program segment (meaning from addresses 1 to 9), the function // should not return any of them. let mut addrs: Vec = (1..4).map(Felt252::from).collect(); let addrs_extension: Vec = (8..10).map(Felt252::from).collect(); addrs.extend_from_slice(&addrs_extension); - let codelen = 9; - let calculated_memory_holes = get_memory_holes(&addrs, codelen); + let mut pub_memory = HashMap::new(); + (1..=9).for_each(|k| { + let addr = Felt252::from(k); + pub_memory.insert(addr, addr * Felt252::from(2)); + }); + + let calculated_memory_holes = get_memory_holes(&addrs, &pub_memory); let expected_memory_holes: Vec = Vec::new(); assert_eq!(expected_memory_holes, calculated_memory_holes); @@ -693,15 +704,20 @@ mod test { #[test] fn test_get_memory_holes_outside_program_section() { // We construct a sorted addresses list [1, 2, 3, 8, 9] and we - // set a codelen of 6. The holes found inside the program section, + // set public memory from addresses 1 to 6. The holes found inside the program section, // i.e. in the address range between 1 to 6, should not be returned. // So addresses 4, 5 and 6 will no be returned, only address 7. let mut addrs: Vec = (1..4).map(Felt252::from).collect(); let addrs_extension: Vec = (8..10).map(Felt252::from).collect(); addrs.extend_from_slice(&addrs_extension); - let codelen = 6; - let calculated_memory_holes = get_memory_holes(&addrs, codelen); + let mut pub_memory = HashMap::new(); + (1..=6).for_each(|k| { + let addr = Felt252::from(k); + pub_memory.insert(addr, addr * Felt252::from(2)); + }); + + let calculated_memory_holes = get_memory_holes(&addrs, &pub_memory); let expected_memory_holes = vec![Felt252::from(7)]; assert_eq!(expected_memory_holes, calculated_memory_holes); diff --git a/provers/cairo/src/runner/run.rs b/provers/cairo/src/runner/run.rs index 24df6f73f0..45b22b3c0b 100644 --- a/provers/cairo/src/runner/run.rs +++ b/provers/cairo/src/runner/run.rs @@ -1,8 +1,9 @@ -use crate::air::PublicInputs; +use crate::air::{PublicInputs, Segment, SegmentName}; use crate::cairo_layout::CairoLayout; use crate::cairo_mem::CairoMemory; use crate::execution_trace::build_main_trace; use crate::register_states::RegisterStates; +use crate::Felt252; use super::vec_writer::VecWriter; use cairo_vm::cairo_run::{self, EncodeTraceError}; @@ -13,6 +14,7 @@ use cairo_vm::vm::errors::{ cairo_run_errors::CairoRunError, trace_errors::TraceError, vm_errors::VirtualMachineError, }; +use cairo_vm::without_std::collections::HashMap; use lambdaworks_math::field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField; use stark_platinum_prover::trace::TraceTable; @@ -79,7 +81,7 @@ pub fn run_program( entrypoint_function: Option<&str>, layout: CairoLayout, program_content: &[u8], -) -> Result<(RegisterStates, CairoMemory, usize), Error> { +) -> Result<(RegisterStates, CairoMemory, PublicInputs), Error> { // default value for entrypoint is "main" let entrypoint = entrypoint_function.unwrap_or("main"); @@ -92,6 +94,7 @@ pub fn run_program( layout: layout.as_str(), proof_mode: true, secure_run: None, + disable_trace_padding: false, }; let (runner, vm) = @@ -122,22 +125,46 @@ pub fn run_program( let cairo_mem = CairoMemory::from_bytes_le(&memory_vec).unwrap(); let register_states = RegisterStates::from_bytes_le(&trace_vec).unwrap(); - let data_len = runner.get_program().data_len(); + let vm_pub_inputs = runner.get_air_public_input(&vm).unwrap(); + + let mut pub_memory: HashMap = HashMap::new(); + vm_pub_inputs.public_memory.iter().for_each(|mem_cell| { + let addr = Felt252::from(mem_cell.address as u64); + let value = Felt252::from_hex_unchecked(&mem_cell.value.as_ref().unwrap().to_str_radix(16)); + pub_memory.insert(addr, value); + }); + + let mut memory_segments: HashMap = HashMap::new(); + vm_pub_inputs.memory_segments.iter().for_each(|(k, v)| { + memory_segments.insert(SegmentName::from(*k), Segment::from(v)); + }); + + let num_steps = register_states.steps(); + let public_inputs = PublicInputs { + pc_init: Felt252::from(register_states.rows[0].pc), + ap_init: Felt252::from(register_states.rows[0].ap), + fp_init: Felt252::from(register_states.rows[0].fp), + pc_final: Felt252::from(register_states.rows[num_steps - 1].pc), + ap_final: Felt252::from(register_states.rows[num_steps - 1].ap), + range_check_min: Some(vm_pub_inputs.rc_min as u16), + range_check_max: Some(vm_pub_inputs.rc_max as u16), + memory_segments, + public_memory: pub_memory, + num_steps, + }; - Ok((register_states, cairo_mem, data_len)) + Ok((register_states, cairo_mem, public_inputs)) } pub fn generate_prover_args( program_content: &[u8], layout: CairoLayout, ) -> Result<(TraceTable, PublicInputs), Error> { - let (register_states, memory, program_size) = run_program(None, layout, program_content)?; - - let mut pub_inputs = PublicInputs::from_regs_and_mem(®ister_states, &memory, program_size); + let (register_states, memory, mut public_inputs) = run_program(None, layout, program_content)?; - let main_trace = build_main_trace(®ister_states, &memory, &mut pub_inputs); + let main_trace = build_main_trace(®ister_states, &memory, &mut public_inputs); - Ok((main_trace, pub_inputs)) + Ok((main_trace, public_inputs)) } pub fn generate_prover_args_from_trace( From 4d9d23384247058d4bd7ca9af1dd31b9ecf58179 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> Date: Thu, 7 Dec 2023 11:32:52 -0300 Subject: [PATCH 12/29] Math: Implement IsSubFieldOf for field extensions (#712) * add subfield trait * implement IsSubfieldOf for Degree2ExtensionField struct in BLS12381 * fix mul * avoid using field elements in issubfield impl. Add tests * clippy and fmt * simplify trait bounds * add explicit type * change iter to into_iter * fix metal code * remove explicit type * refactor quadratic extensions and implement IsSubFieldOf * refactor cubic extensions and implement issubfield * add to_subfield_vec method to field element * fix type * clippy * make to_subfield_vec available only under std feature * fmt * merge main --- math/src/elliptic_curve/edwards/traits.rs | 2 +- math/src/elliptic_curve/point.rs | 5 +- .../curves/bls12_381/field_extension.rs | 17 +- .../curves/bls12_381/pairing.rs | 4 +- .../short_weierstrass/curves/test_curve_1.rs | 10 +- .../short_weierstrass/curves/test_curve_2.rs | 6 +- math/src/fft/test_helpers.rs | 3 +- math/src/field/element.rs | 16 ++ math/src/field/extensions/cubic.rs | 186 +++++++++++++---- math/src/field/extensions/quadratic.rs | 196 ++++++++++++++---- .../fields/fft_friendly/quadratic_babybear.rs | 14 +- math/src/field/fields/mersenne31/extension.rs | 14 +- math/src/field/fields/u64_goldilocks_field.rs | 6 +- math/src/field/traits.rs | 7 + provers/plonk/src/prover.rs | 6 +- provers/plonk/src/verifier.rs | 6 +- 16 files changed, 364 insertions(+), 134 deletions(-) diff --git a/math/src/elliptic_curve/edwards/traits.rs b/math/src/elliptic_curve/edwards/traits.rs index aedb1e7a88..9890c051cd 100644 --- a/math/src/elliptic_curve/edwards/traits.rs +++ b/math/src/elliptic_curve/edwards/traits.rs @@ -12,7 +12,7 @@ pub trait IsEdwards: IsEllipticCurve + Clone + Debug { y: &FieldElement, ) -> FieldElement { (Self::a() * x.pow(2_u16) + y.pow(2_u16)) - - FieldElement::one() + - FieldElement::::one() - Self::d() * x.pow(2_u16) * y.pow(2_u16) } } diff --git a/math/src/elliptic_curve/point.rs b/math/src/elliptic_curve/point.rs index aaf62e7d14..b55b541e1c 100644 --- a/math/src/elliptic_curve/point.rs +++ b/math/src/elliptic_curve/point.rs @@ -68,7 +68,8 @@ impl Eq for ProjectivePoint {} mod tests { use crate::cyclic_group::IsGroup; use crate::elliptic_curve::short_weierstrass::curves::test_curve_1::{ - TestCurve1, TestCurveQuadraticNonResidue, TEST_CURVE_1_MAIN_SUBGROUP_ORDER, + TestCurve1, TestCurvePrimeField, TestCurveQuadraticNonResidue, + TEST_CURVE_1_MAIN_SUBGROUP_ORDER, }; use crate::elliptic_curve::short_weierstrass::curves::test_curve_2::TestCurve2; use crate::field::element::FieldElement; @@ -78,7 +79,7 @@ mod tests { use crate::field::extensions::quadratic::QuadraticExtensionFieldElement; #[allow(clippy::upper_case_acronyms)] - type FEE = QuadraticExtensionFieldElement; + type FEE = QuadraticExtensionFieldElement; // This tests only apply for the specific curve found in the configuration file. #[test] diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs index 26c34e1739..4d57d18cd3 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs @@ -142,6 +142,11 @@ impl IsSubFieldOf for BLS12381PrimeField { fn embed(a: Self::BaseType) -> ::BaseType { [FieldElement::from_raw(a), FieldElement::zero()] } + + #[cfg(feature = "std")] + fn to_subfield_vec(b: ::BaseType) -> Vec { + b.into_iter().map(|x| x.to_raw()).collect() + } } impl ByteConversion for FieldElement { @@ -183,9 +188,7 @@ impl ByteConversion for FieldElement { /////////////// #[derive(Debug, Clone)] pub struct LevelTwoResidue; -impl HasCubicNonResidue for LevelTwoResidue { - type BaseField = Degree2ExtensionField; - +impl HasCubicNonResidue for LevelTwoResidue { fn residue() -> FieldElement { FieldElement::new([ FieldElement::new(U384::from("1")), @@ -194,13 +197,11 @@ impl HasCubicNonResidue for LevelTwoResidue { } } -pub type Degree6ExtensionField = CubicExtensionField; +pub type Degree6ExtensionField = CubicExtensionField; #[derive(Debug, Clone)] pub struct LevelThreeResidue; -impl HasQuadraticNonResidue for LevelThreeResidue { - type BaseField = Degree6ExtensionField; - +impl HasQuadraticNonResidue for LevelThreeResidue { fn residue() -> FieldElement { FieldElement::new([ FieldElement::zero(), @@ -210,7 +211,7 @@ impl HasQuadraticNonResidue for LevelThreeResidue { } } -pub type Degree12ExtensionField = QuadraticExtensionField; +pub type Degree12ExtensionField = QuadraticExtensionField; impl FieldElement { pub fn new_base(a_hex: &str) -> Self { diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs index 0e509dee8a..0e2afa8095 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs @@ -87,7 +87,7 @@ fn double_accumulate_line( let [a1, a3, a5] = y.value(); let b0 = e - b; let b2 = FieldElement::new([x1_sq_30 * px, x1_sq_31 * px]); - let b3 = FieldElement::new([-h0 * py, -h1 * py]); + let b3 = FieldElement::::new([-h0 * py, -h1 * py]); *accumulator = FieldElement::new([ FieldElement::new([ a0 * &b0 + &residue * (a3 * &b3 + a4 * &b2), // w0 @@ -138,7 +138,7 @@ fn add_accumulate_line( let [a1, a3, a5] = y.value(); let b0 = -lambda.clone() * y2 + theta.clone() * x2; let b2 = FieldElement::new([-theta0 * px, -theta1 * px]); - let b3 = FieldElement::new([lambda0 * py, lambda1 * py]); + let b3 = FieldElement::::new([lambda0 * py, lambda1 * py]); *accumulator = FieldElement::new([ FieldElement::new([ a0 * &b0 + &residue * (a3 * &b3 + a4 * &b2), // w0 diff --git a/math/src/elliptic_curve/short_weierstrass/curves/test_curve_1.rs b/math/src/elliptic_curve/short_weierstrass/curves/test_curve_1.rs index 030339f74b..c8601ce23c 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/test_curve_1.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/test_curve_1.rs @@ -19,15 +19,15 @@ pub const TEST_CURVE_1_PRIME_FIELD_ORDER: u64 = 59; /// Order of the subgroup of the curve. pub const TEST_CURVE_1_MAIN_SUBGROUP_ORDER: u64 = 5; +pub type TestCurvePrimeField = U64PrimeField; + /// In F59 the element -1 is not a square. We use this property /// to construct a Quadratic Field Extension out of it by adding /// its square root. #[derive(Debug, Clone)] pub struct TestCurveQuadraticNonResidue; -impl HasQuadraticNonResidue for TestCurveQuadraticNonResidue { - type BaseField = U64PrimeField; - - fn residue() -> FieldElement> { +impl HasQuadraticNonResidue for TestCurveQuadraticNonResidue { + fn residue() -> FieldElement { -FieldElement::one() } } @@ -37,7 +37,7 @@ impl HasQuadraticNonResidue for TestCurveQuadraticNonResidue { pub struct TestCurve1; impl IsEllipticCurve for TestCurve1 { - type BaseField = QuadraticExtensionField; + type BaseField = QuadraticExtensionField; type PointRepresentation = ShortWeierstrassProjectivePoint; fn generator() -> Self::PointRepresentation { diff --git a/math/src/elliptic_curve/short_weierstrass/curves/test_curve_2.rs b/math/src/elliptic_curve/short_weierstrass/curves/test_curve_2.rs index 961e9e5129..ca71c4a9d4 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/test_curve_2.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/test_curve_2.rs @@ -34,9 +34,7 @@ type TestCurve2PrimeField = MontgomeryBackendPrimeField; /// its square root. #[derive(Debug, Clone)] pub struct TestCurve2QuadraticNonResidue; -impl HasQuadraticNonResidue for TestCurve2QuadraticNonResidue { - type BaseField = TestCurve2PrimeField; - +impl HasQuadraticNonResidue for TestCurve2QuadraticNonResidue { fn residue() -> FieldElement { -FieldElement::one() } @@ -47,7 +45,7 @@ impl HasQuadraticNonResidue for TestCurve2QuadraticNonResidue { pub struct TestCurve2; impl IsEllipticCurve for TestCurve2 { - type BaseField = QuadraticExtensionField; + type BaseField = QuadraticExtensionField; type PointRepresentation = ShortWeierstrassProjectivePoint; fn generator() -> Self::PointRepresentation { diff --git a/math/src/fft/test_helpers.rs b/math/src/fft/test_helpers.rs index d45c74e86e..0c177a1da1 100644 --- a/math/src/fft/test_helpers.rs +++ b/math/src/fft/test_helpers.rs @@ -12,7 +12,8 @@ pub fn naive_matrix_dft_test(input: &[FieldElement]) -> Vec(order.into(), n, RootsConfig::Natural).unwrap(); let mut output = Vec::with_capacity(n); for row in 0..n { diff --git a/math/src/field/element.rs b/math/src/field/element.rs index 67707b791c..0404b9fe48 100644 --- a/math/src/field/element.rs +++ b/math/src/field/element.rs @@ -63,6 +63,17 @@ impl FieldElement { numbers[0] = bi_inv; Ok(()) } + + #[inline(always)] + pub fn to_subfield_vec(self) -> Vec> + where + S: IsSubFieldOf, + { + S::to_subfield_vec(self.value) + .into_iter() + .map(|x| FieldElement::from_raw(x)) + .collect() + } } /// From overloading for field elements @@ -434,6 +445,11 @@ where Self { value: F::zero() } } + /// Returns the raw base type + pub fn to_raw(self) -> F::BaseType { + self.value + } + #[inline(always)] pub fn to_extension(self) -> FieldElement where diff --git a/math/src/field/extensions/cubic.rs b/math/src/field/extensions/cubic.rs index 447c790f3d..7443b48a78 100644 --- a/math/src/field/extensions/cubic.rs +++ b/math/src/field/extensions/cubic.rs @@ -1,6 +1,6 @@ use crate::field::element::FieldElement; use crate::field::errors::FieldError; -use crate::field::traits::IsField; +use crate::field::traits::{IsField, IsSubFieldOf}; #[cfg(feature = "lambdaworks-serde-binary")] use crate::traits::ByteConversion; use core::fmt::Debug; @@ -9,21 +9,20 @@ use core::marker::PhantomData; /// A general cubic extension field over `F` /// with cubic non residue `Q::residue()` #[derive(Debug, Clone, PartialEq, Eq)] -pub struct CubicExtensionField { - phantom: PhantomData, +pub struct CubicExtensionField { + field: PhantomData, + non_residue: PhantomData, } -pub type CubicExtensionFieldElement = FieldElement>; +pub type CubicExtensionFieldElement = FieldElement>; /// Trait to fix a cubic non residue. /// Used to construct a cubic extension field by adding /// a square root of `residue()`. -pub trait HasCubicNonResidue { - type BaseField: IsField; - +pub trait HasCubicNonResidue { /// This function must return an element that is not a cube in Fp, /// that is, a cubic non-residue. - fn residue() -> FieldElement; + fn residue() -> FieldElement; } #[cfg(feature = "lambdaworks-serde-binary")] @@ -56,17 +55,15 @@ where } } -impl IsField for CubicExtensionField +impl IsField for CubicExtensionField where - Q: Clone + Debug + HasCubicNonResidue, + F: IsField, + Q: Clone + Debug + HasCubicNonResidue, { - type BaseType = [FieldElement; 3]; + type BaseType = [FieldElement; 3]; /// Returns the component wise addition of `a` and `b` - fn add( - a: &[FieldElement; 3], - b: &[FieldElement; 3], - ) -> [FieldElement; 3] { + fn add(a: &[FieldElement; 3], b: &[FieldElement; 3]) -> [FieldElement; 3] { [&a[0] + &b[0], &a[1] + &b[1], &a[2] + &b[2]] } @@ -74,10 +71,7 @@ where /// equation: /// (a0 + a1 * t) * (b0 + b1 * t) = a0 * b0 + a1 * b1 * Q::residue() + (a0 * b1 + a1 * b0) * t /// where `t.pow(2)` equals `Q::residue()`. - fn mul( - a: &[FieldElement; 3], - b: &[FieldElement; 3], - ) -> [FieldElement; 3] { + fn mul(a: &[FieldElement; 3], b: &[FieldElement; 3]) -> [FieldElement; 3] { let v0 = &a[0] * &b[0]; let v1 = &a[1] * &b[1]; let v2 = &a[2] * &b[2]; @@ -90,24 +84,18 @@ where } /// Returns the component wise subtraction of `a` and `b` - fn sub( - a: &[FieldElement; 3], - b: &[FieldElement; 3], - ) -> [FieldElement; 3] { + fn sub(a: &[FieldElement; 3], b: &[FieldElement; 3]) -> [FieldElement; 3] { [&a[0] - &b[0], &a[1] - &b[1], &a[2] - &b[2]] } /// Returns the component wise negation of `a` - fn neg(a: &[FieldElement; 3]) -> [FieldElement; 3] { + fn neg(a: &[FieldElement; 3]) -> [FieldElement; 3] { [-&a[0], -&a[1], -&a[2]] } /// Returns the multiplicative inverse of `a` - fn inv( - a: &[FieldElement; 3], - ) -> Result<[FieldElement; 3], FieldError> { - let three = FieldElement::::from(3_u64); - + fn inv(a: &[FieldElement; 3]) -> Result<[FieldElement; 3], FieldError> { + let three = FieldElement::::from(3_u64); let d = a[0].pow(3_u64) + a[1].pow(3_u64) * Q::residue() + a[2].pow(3_u64) * Q::residue().pow(2_u64) @@ -121,20 +109,17 @@ where } /// Returns the division of `a` and `b` - fn div( - a: &[FieldElement; 3], - b: &[FieldElement; 3], - ) -> [FieldElement; 3] { - Self::mul(a, &Self::inv(b).unwrap()) + fn div(a: &[FieldElement; 3], b: &[FieldElement; 3]) -> [FieldElement; 3] { + ::mul(a, &Self::inv(b).unwrap()) } /// Returns a boolean indicating whether `a` and `b` are equal component wise. - fn eq(a: &[FieldElement; 3], b: &[FieldElement; 3]) -> bool { + fn eq(a: &[FieldElement; 3], b: &[FieldElement; 3]) -> bool { a[0] == b[0] && a[1] == b[1] && a[2] == b[2] } /// Returns the additive neutral element of the field extension. - fn zero() -> [FieldElement; 3] { + fn zero() -> [FieldElement; 3] { [ FieldElement::zero(), FieldElement::zero(), @@ -143,7 +128,7 @@ where } /// Returns the multiplicative neutral element of the field extension. - fn one() -> [FieldElement; 3] { + fn one() -> [FieldElement; 3] { [ FieldElement::one(), FieldElement::zero(), @@ -164,11 +149,66 @@ where /// of that element in the field. /// Note: for this case this is simply the identity, because the components /// already have correct representations. - fn from_base_type(x: [FieldElement; 3]) -> [FieldElement; 3] { + fn from_base_type(x: [FieldElement; 3]) -> [FieldElement; 3] { x } } +impl IsSubFieldOf> for F +where + F: IsField, + Q: Clone + Debug + HasCubicNonResidue, +{ + fn mul( + a: &Self::BaseType, + b: & as IsField>::BaseType, + ) -> as IsField>::BaseType { + let c0 = FieldElement::from_raw(F::mul(a, b[0].value())); + let c1 = FieldElement::from_raw(F::mul(a, b[1].value())); + let c2 = FieldElement::from_raw(F::mul(a, b[2].value())); + [c0, c1, c2] + } + + fn add( + a: &Self::BaseType, + b: & as IsField>::BaseType, + ) -> as IsField>::BaseType { + let c0 = FieldElement::from_raw(F::add(a, b[0].value())); + [c0, b[1].clone(), b[2].clone()] + } + + fn div( + a: &Self::BaseType, + b: & as IsField>::BaseType, + ) -> as IsField>::BaseType { + let b_inv = as IsField>::inv(b).unwrap(); + >>::mul(a, &b_inv) + } + + fn sub( + a: &Self::BaseType, + b: & as IsField>::BaseType, + ) -> as IsField>::BaseType { + let c0 = FieldElement::from_raw(F::sub(a, b[0].value())); + let c1 = FieldElement::from_raw(F::neg(b[1].value())); + let c2 = FieldElement::from_raw(F::neg(b[2].value())); + [c0, c1, c2] + } + + fn embed(a: Self::BaseType) -> as IsField>::BaseType { + [ + FieldElement::from_raw(a), + FieldElement::zero(), + FieldElement::zero(), + ] + } + + #[cfg(feature = "std")] + fn to_subfield_vec(b: as IsField>::BaseType) -> Vec { + b.into_iter().map(|x| x.to_raw()).collect() + } +} + #[cfg(test)] mod tests { use crate::field::fields::u64_prime_field::{U64FieldElement, U64PrimeField}; @@ -179,16 +219,14 @@ mod tests { #[derive(Debug, Clone)] struct MyCubicNonResidue; - impl HasCubicNonResidue for MyCubicNonResidue { - type BaseField = U64PrimeField; - + impl HasCubicNonResidue> for MyCubicNonResidue { fn residue() -> FieldElement> { -FieldElement::from(11) } } type FE = U64FieldElement; - type MyFieldExtensionBackend = CubicExtensionField; + type MyFieldExtensionBackend = CubicExtensionField, MyCubicNonResidue>; #[allow(clippy::upper_case_acronyms)] type FEE = FieldElement; @@ -285,4 +323,68 @@ mod tests { let expected_result = FEE::new([FE::new(8), FE::new(3), FE::new(5)]); assert_eq!(a.inv().unwrap(), expected_result); } + + #[test] + fn test_add_as_subfield_1() { + let a = FE::new(5); + let b = FEE::new([-FE::new(2), FE::new(8), FE::new(10)]); + let expected_result = FEE::new([FE::new(3), FE::new(8), FE::new(10)]); + assert_eq!(a + b, expected_result); + } + + #[test] + fn test_add_as_subfield_2() { + let a = FE::new(12); + let b = FEE::new([-FE::new(4), FE::new(2), FE::new(8)]); + let expected_result = FEE::new([FE::new(8), FE::new(2), FE::new(8)]); + assert_eq!(a + b, expected_result); + } + + #[test] + fn test_sub_as_subfield_1() { + let a = FE::new(3); + let b = FEE::new([-FE::new(2), FE::new(8), FE::new(2)]); + let expected_result = FEE::new([FE::new(5), FE::new(5), FE::new(11)]); + assert_eq!(a - b, expected_result); + } + + #[test] + fn test_sub_as_subfield_2() { + let a = FE::new(12); + let b = FEE::new([-FE::new(4), FE::new(2), FE::new(3)]); + let expected_result = FEE::new([FE::new(3), FE::new(11), FE::new(10)]); + assert_eq!(a - b, expected_result); + } + + #[test] + fn test_mul_as_subfield_1() { + let a = FE::new(5); + let b = FEE::new([-FE::new(2), FE::new(8), FE::new(6)]); + let expected_result = FEE::new([FE::new(3), FE::new(1), FE::new(4)]); + assert_eq!(a * b, expected_result); + } + + #[test] + fn test_mul_as_subfield_2() { + let a = FE::new(11); + let b = FEE::new([-FE::new(4), FE::new(2), FE::new(15)]); + let expected_result = FEE::new([FE::new(8), FE::new(9), FE::new(9)]); + assert_eq!(a * b, expected_result); + } + + #[test] + fn test_div_as_subfield_1() { + let a = FE::new(2); + let b = FEE::new([-FE::new(2), FE::new(8), FE::new(5)]); + let expected_result = FEE::new([FE::new(8), FE::new(4), FE::new(10)]); + assert_eq!(a / b, expected_result); + } + + #[test] + fn test_div_as_subfield_2() { + let a = FE::new(4); + let b = FEE::new([-FE::new(4), FE::new(2), FE::new(2)]); + let expected_result = FEE::new([FE::new(3), FE::new(6), FE::new(11)]); + assert_eq!(a / b, expected_result); + } } diff --git a/math/src/field/extensions/quadratic.rs b/math/src/field/extensions/quadratic.rs index 3cb09cd0d6..6cd3fd46fc 100644 --- a/math/src/field/extensions/quadratic.rs +++ b/math/src/field/extensions/quadratic.rs @@ -1,6 +1,6 @@ use crate::field::element::FieldElement; use crate::field::errors::FieldError; -use crate::field::traits::IsField; +use crate::field::traits::{IsField, IsSubFieldOf}; #[cfg(feature = "lambdaworks-serde-binary")] use crate::traits::ByteConversion; use core::fmt::Debug; @@ -9,24 +9,28 @@ use core::marker::PhantomData; /// A general quadratic extension field over `F` /// with quadratic non residue `Q::residue()` #[derive(Debug, Clone, PartialEq, Eq)] -pub struct QuadraticExtensionField { - phantom: PhantomData, +pub struct QuadraticExtensionField +where + F: IsField, + T: HasQuadraticNonResidue, +{ + field: PhantomData, + non_residue: PhantomData, } -pub type QuadraticExtensionFieldElement = FieldElement>; +pub type QuadraticExtensionFieldElement = FieldElement>; /// Trait to fix a quadratic non residue. /// Used to construct a quadratic extension field by adding /// a square root of `residue()`. -pub trait HasQuadraticNonResidue { - type BaseField: IsField; - - fn residue() -> FieldElement; +pub trait HasQuadraticNonResidue { + fn residue() -> FieldElement; } -impl FieldElement> +impl FieldElement> where - Q: Clone + Debug + HasQuadraticNonResidue, + F: IsField, + Q: Clone + Debug + HasQuadraticNonResidue, { pub fn conjugate(&self) -> Self { let [a, b] = self.value(); @@ -64,17 +68,15 @@ where } } -impl IsField for QuadraticExtensionField +impl IsField for QuadraticExtensionField where - Q: Clone + Debug + HasQuadraticNonResidue, + F: IsField, + Q: Clone + Debug + HasQuadraticNonResidue, { - type BaseType = [FieldElement; 2]; + type BaseType = [FieldElement; 2]; /// Returns the component wise addition of `a` and `b` - fn add( - a: &[FieldElement; 2], - b: &[FieldElement; 2], - ) -> [FieldElement; 2] { + fn add(a: &[FieldElement; 2], b: &[FieldElement; 2]) -> [FieldElement; 2] { [&a[0] + &b[0], &a[1] + &b[1]] } @@ -82,10 +84,7 @@ where /// equation: /// (a0 + a1 * t) * (b0 + b1 * t) = a0 * b0 + a1 * b1 * Q::residue() + (a0 * b1 + a1 * b0) * t /// where `t.pow(2)` equals `Q::residue()`. - fn mul( - a: &[FieldElement; 2], - b: &[FieldElement; 2], - ) -> [FieldElement; 2] { + fn mul(a: &[FieldElement; 2], b: &[FieldElement; 2]) -> [FieldElement; 2] { let q = Q::residue(); let a0b0 = &a[0] * &b[0]; let a1b1 = &a[1] * &b[1]; @@ -93,7 +92,7 @@ where [&a0b0 + &a1b1 * q, z - a0b0 - a1b1] } - fn square(a: &[FieldElement; 2]) -> [FieldElement; 2] { + fn square(a: &[FieldElement; 2]) -> [FieldElement; 2] { let [a0, a1] = a; let v0 = a0 * a1; let c0 = (a0 + a1) * (a0 + Q::residue() * a1) - &v0 - Q::residue() * &v0; @@ -102,47 +101,39 @@ where } /// Returns the component wise subtraction of `a` and `b` - fn sub( - a: &[FieldElement; 2], - b: &[FieldElement; 2], - ) -> [FieldElement; 2] { + fn sub(a: &[FieldElement; 2], b: &[FieldElement; 2]) -> [FieldElement; 2] { [&a[0] - &b[0], &a[1] - &b[1]] } /// Returns the component wise negation of `a` - fn neg(a: &[FieldElement; 2]) -> [FieldElement; 2] { + fn neg(a: &[FieldElement; 2]) -> [FieldElement; 2] { [-&a[0], -&a[1]] } /// Returns the multiplicative inverse of `a` /// This uses the equality `(a0 + a1 * t) * (a0 - a1 * t) = a0.pow(2) - a1.pow(2) * Q::residue()` - fn inv( - a: &[FieldElement; 2], - ) -> Result<[FieldElement; 2], FieldError> { + fn inv(a: &[FieldElement; 2]) -> Result<[FieldElement; 2], FieldError> { let inv_norm = (a[0].pow(2_u64) - Q::residue() * a[1].pow(2_u64)).inv()?; Ok([&a[0] * &inv_norm, -&a[1] * inv_norm]) } /// Returns the division of `a` and `b` - fn div( - a: &[FieldElement; 2], - b: &[FieldElement; 2], - ) -> [FieldElement; 2] { - Self::mul(a, &Self::inv(b).unwrap()) + fn div(a: &[FieldElement; 2], b: &[FieldElement; 2]) -> [FieldElement; 2] { + ::mul(a, &Self::inv(b).unwrap()) } /// Returns a boolean indicating whether `a` and `b` are equal component wise. - fn eq(a: &[FieldElement; 2], b: &[FieldElement; 2]) -> bool { + fn eq(a: &[FieldElement; 2], b: &[FieldElement; 2]) -> bool { a[0] == b[0] && a[1] == b[1] } /// Returns the additive neutral element of the field extension. - fn zero() -> [FieldElement; 2] { + fn zero() -> [FieldElement; 2] { [FieldElement::zero(), FieldElement::zero()] } /// Returns the multiplicative neutral element of the field extension. - fn one() -> [FieldElement; 2] { + fn one() -> [FieldElement; 2] { [FieldElement::one(), FieldElement::zero()] } @@ -155,12 +146,66 @@ where /// of that element in the field. /// Note: for this case this is simply the identity, because the components /// already have correct representations. - fn from_base_type(x: [FieldElement; 2]) -> [FieldElement; 2] { + fn from_base_type(x: [FieldElement; 2]) -> [FieldElement; 2] { x } } -impl FieldElement> {} +impl IsSubFieldOf> for F +where + F: IsField, + Q: Clone + Debug + HasQuadraticNonResidue, +{ + fn mul( + a: &Self::BaseType, + b: & as IsField>::BaseType, + ) -> as IsField>::BaseType { + let c0 = FieldElement::from_raw(F::mul(a, b[0].value())); + let c1 = FieldElement::from_raw(F::mul(a, b[1].value())); + [c0, c1] + } + + fn add( + a: &Self::BaseType, + b: & as IsField>::BaseType, + ) -> as IsField>::BaseType { + let c0 = FieldElement::from_raw(F::add(a, b[0].value())); + [c0, b[1].clone()] + } + + fn div( + a: &Self::BaseType, + b: & as IsField>::BaseType, + ) -> as IsField>::BaseType { + let b_inv = as IsField>::inv(b).unwrap(); + >>::mul(a, &b_inv) + } + + fn sub( + a: &Self::BaseType, + b: & as IsField>::BaseType, + ) -> as IsField>::BaseType { + let c0 = FieldElement::from_raw(F::sub(a, b[0].value())); + let c1 = FieldElement::from_raw(F::neg(b[1].value())); + [c0, c1] + } + + fn embed(a: Self::BaseType) -> as IsField>::BaseType { + [FieldElement::from_raw(a), FieldElement::zero()] + } + + #[cfg(feature = "std")] + fn to_subfield_vec( + b: as IsField>::BaseType, + ) -> Vec { + b.into_iter().map(|x| x.to_raw()).collect() + } +} + +impl> + FieldElement> +{ +} #[cfg(test)] mod tests { @@ -172,16 +217,15 @@ mod tests { #[derive(Debug, Clone)] struct MyQuadraticNonResidue; - impl HasQuadraticNonResidue for MyQuadraticNonResidue { - type BaseField = U64PrimeField; - + impl HasQuadraticNonResidue> for MyQuadraticNonResidue { fn residue() -> FieldElement> { -FieldElement::one() } } type FE = U64FieldElement; - type MyFieldExtensionBackend = QuadraticExtensionField; + type MyFieldExtensionBackend = + QuadraticExtensionField, MyQuadraticNonResidue>; #[allow(clippy::upper_case_acronyms)] type FEE = FieldElement; @@ -285,4 +329,68 @@ mod tests { let expected_result = FEE::new([FE::new(12), -FE::new(5)]); assert_eq!(a.conjugate(), expected_result); } + + #[test] + fn test_add_as_subfield_1() { + let a = -FE::new(2); + let b = FEE::new([FE::new(0), FE::new(3)]); + let expected_result = FEE::new([FE::new(57), FE::new(3)]); + assert_eq!(a + b, expected_result); + } + + #[test] + fn test_add_as_subfield_2() { + let a = -FE::new(4); + let b = FEE::new([FE::new(12), FE::new(5)]); + let expected_result = FEE::new([FE::new(8), FE::new(5)]); + assert_eq!(a + b, expected_result); + } + + #[test] + fn test_sub_as_subfield_1() { + let a = FE::new(0); + let b = FEE::new([-FE::new(2), FE::new(8)]); + let expected_result = FEE::new([FE::new(2), FE::new(51)]); + assert_eq!(a - b, expected_result); + } + + #[test] + fn test_sub_a_subfield_2() { + let a = FE::new(12); + let b = FEE::new([-FE::new(4), -FE::new(2)]); + let expected_result = FEE::new([FE::new(16), FE::new(2)]); + assert_eq!(a - b, expected_result); + } + + #[test] + fn test_mul_as_subfield_1() { + let a = FE::new(2); + let b = FEE::new([-FE::new(2), FE::new(8)]); + let expected_result = FEE::new([FE::new(55), FE::new(16)]); + assert_eq!(a * b, expected_result); + } + + #[test] + fn test_mul_as_subfield_2() { + let a = FE::new(12); + let b = FEE::new([-FE::new(4), FE::new(2)]); + let expected_result = FEE::new([FE::new(11), FE::new(24)]); + assert_eq!(a * b, expected_result); + } + + #[test] + fn test_div_as_subfield_1() { + let a = FE::new(3); + let b = FEE::new([-FE::new(2), FE::new(8)]); + let expected_result = FEE::new([FE::new(19), FE::new(17)]); + assert_eq!(a / b, expected_result); + } + + #[test] + fn test_div_as_subfield_2() { + let a = FE::new(22); + let b = FEE::new([FE::new(4), FE::new(2)]); + let expected_result = FEE::new([FE::new(28), FE::new(45)]); + assert_eq!(a / b, expected_result); + } } diff --git a/math/src/field/fields/fft_friendly/quadratic_babybear.rs b/math/src/field/fields/fft_friendly/quadratic_babybear.rs index 2a1990a198..d3d4ea0c9a 100644 --- a/math/src/field/fields/fft_friendly/quadratic_babybear.rs +++ b/math/src/field/fields/fft_friendly/quadratic_babybear.rs @@ -4,19 +4,19 @@ use crate::field::{ }; /// Quadratic field extension of Babybear -pub type QuadraticBabybearField = QuadraticExtensionField; - -/// Field element type for the quadratic extension of Babybear -pub type QuadraticBabybearFieldElement = QuadraticExtensionFieldElement; - -impl HasQuadraticNonResidue for Babybear31PrimeField { - type BaseField = Babybear31PrimeField; +pub type QuadraticBabybearField = + QuadraticExtensionField; +impl HasQuadraticNonResidue for Babybear31PrimeField { fn residue() -> FieldElement { -FieldElement::one() } } +/// Field element type for the quadratic extension of Babybear +pub type QuadraticBabybearFieldElement = + QuadraticExtensionFieldElement; + #[cfg(test)] mod tests { use super::*; diff --git a/math/src/field/fields/mersenne31/extension.rs b/math/src/field/fields/mersenne31/extension.rs index e8773e8d5a..3c89a21472 100644 --- a/math/src/field/fields/mersenne31/extension.rs +++ b/math/src/field/fields/mersenne31/extension.rs @@ -93,12 +93,11 @@ impl IsField for Mersenne31Complex { } } -pub type Mersenne31ComplexQuadraticExtensionField = QuadraticExtensionField; +pub type Mersenne31ComplexQuadraticExtensionField = + QuadraticExtensionField; //TODO: Check this should be for complex and not base field -impl HasQuadraticNonResidue for Mersenne31Complex { - type BaseField = Mersenne31Complex; - +impl HasQuadraticNonResidue for Mersenne31Complex { // Verifiable in Sage with // ```sage // p = 2**31 - 1 # Mersenne31 @@ -117,11 +116,10 @@ impl HasQuadraticNonResidue for Mersenne31Complex { } } -pub type Mersenne31ComplexCubicExtensionField = CubicExtensionField; - -impl HasCubicNonResidue for Mersenne31Complex { - type BaseField = Mersenne31Complex; +pub type Mersenne31ComplexCubicExtensionField = + CubicExtensionField; +impl HasCubicNonResidue for Mersenne31Complex { // Verifiable in Sage with // ```sage // p = 2**31 - 1 # Mersenne31 diff --git a/math/src/field/fields/u64_goldilocks_field.rs b/math/src/field/fields/u64_goldilocks_field.rs index 240eb71da4..82be9ef4de 100644 --- a/math/src/field/fields/u64_goldilocks_field.rs +++ b/math/src/field/fields/u64_goldilocks_field.rs @@ -211,11 +211,9 @@ fn exp_power_of_2(base: &u64) -> u64 { res } -pub type Goldilocks64ExtensionField = QuadraticExtensionField; - -impl HasQuadraticNonResidue for Goldilocks64Field { - type BaseField = Goldilocks64Field; +pub type Goldilocks64ExtensionField = QuadraticExtensionField; +impl HasQuadraticNonResidue for Goldilocks64Field { // Verifiable in Sage with // `R. = GF(p)[]; assert (x^2 - 7).is_irreducible()` fn residue() -> FieldElement { diff --git a/math/src/field/traits.rs b/math/src/field/traits.rs index 8441b70825..67df42e8d4 100644 --- a/math/src/field/traits.rs +++ b/math/src/field/traits.rs @@ -20,6 +20,8 @@ pub trait IsSubFieldOf: IsField { fn div(a: &Self::BaseType, b: &F::BaseType) -> F::BaseType; fn sub(a: &Self::BaseType, b: &F::BaseType) -> F::BaseType; fn embed(a: Self::BaseType) -> F::BaseType; + #[cfg(feature = "std")] + fn to_subfield_vec(b: F::BaseType) -> Vec; } impl IsSubFieldOf for F @@ -50,6 +52,11 @@ where fn embed(a: Self::BaseType) -> F::BaseType { a } + + #[cfg(feature = "std")] + fn to_subfield_vec(b: F::BaseType) -> Vec { + vec![b] + } } /// Trait to define necessary parameters for FFT-friendly Fields. diff --git a/provers/plonk/src/prover.rs b/provers/plonk/src/prover.rs index c8bee06666..5e78b70f55 100644 --- a/provers/plonk/src/prover.rs +++ b/provers/plonk/src/prover.rs @@ -486,7 +486,7 @@ where let p_permutation_2_eval: Vec<_> = p_z_eval .iter() .zip(l1_eval.iter()) - .map(|(z, l)| (z - FieldElement::one()) * l) + .map(|(z, l)| (z - FieldElement::::one()) * l) .collect(); let p_eval: Vec<_> = p_permutation_2_eval @@ -573,8 +573,8 @@ where let zeta_raised_n = Polynomial::new_monomial(r4.zeta.pow(cpi.n + 2), 0); // TODO: Paper says n and 2n, but Gnark uses n+2 and 2n+4 let zeta_raised_2n = Polynomial::new_monomial(r4.zeta.pow(2 * cpi.n + 4), 0); - let l1_zeta = (&r4.zeta.pow(cpi.n as u64) - FieldElement::one()) - / (&r4.zeta - FieldElement::one()) + let l1_zeta = (&r4.zeta.pow(cpi.n as u64) - FieldElement::::one()) + / (&r4.zeta - FieldElement::::one()) / FieldElement::from(cpi.n as u64); let mut p_non_constant = &cpi.qm * &r4.a_zeta * &r4.b_zeta diff --git a/provers/plonk/src/verifier.rs b/provers/plonk/src/verifier.rs index 6da8e83d0d..8ee15c5747 100644 --- a/provers/plonk/src/verifier.rs +++ b/provers/plonk/src/verifier.rs @@ -76,13 +76,13 @@ impl> Verifier { { // TODO: First three steps are validations: belonging to main subgroup, belonging to prime field. let [beta, gamma, alpha, zeta, upsilon] = self.compute_challenges(p, vk, public_input); - let zh_zeta = zeta.pow(input.n) - FieldElement::one(); + let zh_zeta = zeta.pow(input.n) - FieldElement::::one(); let k1 = &input.k1; let k2 = k1 * k1; - let l1_zeta = (zeta.pow(input.n as u64) - FieldElement::one()) - / (&zeta - FieldElement::one()) + let l1_zeta = (zeta.pow(input.n as u64) - FieldElement::::one()) + / (&zeta - FieldElement::::one()) / FieldElement::from(input.n as u64); // Use the following equality to compute PI(ΞΆ) From 438113f76159955fd8fd20a97f733557b01c1fc3 Mon Sep 17 00:00:00 2001 From: Marcos Villagra <71292159+mdvillagra@users.noreply.github.com> Date: Thu, 7 Dec 2023 11:52:30 -0300 Subject: [PATCH 13/29] Make Babybear field FFT friendly (#576) * added isFFTField trait * added isFFT trait to babybear field with test * added tests but the first test is failing * all tests succesfull * added correction to pass checks * deleted to_string to pass check and lints * added changes to pass checks * added feature=std to tests * added quickfix to past gpu test * added quickfix * added quickfix * added quickfix * Fix mac * Fix mac * Test fix mac * cfg(not(any(feature = "metal", feature = "cuda") * added fix * cargo fmt * added more fixes * added more fixes * cargo fmt * fix * fix * test formatting fix --------- Co-authored-by: MauroFab Co-authored-by: Mauro Toscano <12560266+MauroToscano@users.noreply.github.com> --- .../src/field/fields/fft_friendly/babybear.rs | 280 ++++++++++++++---- 1 file changed, 229 insertions(+), 51 deletions(-) diff --git a/math/src/field/fields/fft_friendly/babybear.rs b/math/src/field/fields/fft_friendly/babybear.rs index 68da03f91a..01aa9e920f 100644 --- a/math/src/field/fields/fft_friendly/babybear.rs +++ b/math/src/field/fields/fft_friendly/babybear.rs @@ -2,8 +2,9 @@ use crate::{ field::{ element::FieldElement, fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, + traits::IsFFTField, }, - unsigned_integer::element::U64, + unsigned_integer::element::{UnsignedInteger, U64}, }; pub type U64MontgomeryBackendPrimeField = MontgomeryBackendPrimeField; @@ -18,6 +19,22 @@ impl IsModulus for MontgomeryConfigBabybear31PrimeField { pub type Babybear31PrimeField = U64MontgomeryBackendPrimeField; +//a two-adic primitive root of unity is 21^(2^24) +// 21^(2^24)=1 mod 2013265921 +// 2^27(2^4-1)+1 where n=27 (two-adicity) and k=2^4+1 + +//In the future we should allow this with metal and cuda feature, and just dispatch it to the CPU until the implementation is done +#[cfg(any(not(feature = "metal"), not(feature = "cuda")))] +impl IsFFTField for Babybear31PrimeField { + const TWO_ADICITY: u64 = 24; + + const TWO_ADIC_PRIMITVE_ROOT_OF_UNITY: Self::BaseType = UnsignedInteger { limbs: [21] }; + + fn field_name() -> &'static str { + "babybear31" + } +} + impl FieldElement { pub fn to_bytes_le(&self) -> [u8; 8] { let limbs = self.representative().limbs; @@ -31,59 +48,220 @@ impl FieldElement { } #[cfg(test)] -mod test_babybear_31_bytes_ops { - use super::Babybear31PrimeField; - use crate::{field::element::FieldElement, traits::ByteConversion}; - - #[test] - #[cfg(feature = "std")] - fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_le() { - let element = FieldElement::::from_hex_unchecked( - "\ - 0123456701234567\ - ", - ); - let bytes = element.to_bytes_le(); - let expected_bytes: [u8; 8] = ByteConversion::to_bytes_le(&element).try_into().unwrap(); - assert_eq!(bytes, expected_bytes); - } +mod tests { + use super::*; - #[test] - #[cfg(feature = "std")] - fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_be() { - let element = FieldElement::::from_hex_unchecked( - "\ - 0123456701234567\ - ", - ); - let bytes = element.to_bytes_be(); - let expected_bytes: [u8; 8] = ByteConversion::to_bytes_be(&element).try_into().unwrap(); - assert_eq!(bytes, expected_bytes); - } + mod test_babybear_31_bytes_ops { + use super::*; + use crate::{field::element::FieldElement, traits::ByteConversion}; - #[test] - - fn byte_serialization_and_deserialization_works_le() { - let element = FieldElement::::from_hex_unchecked( - "\ - 7654321076543210\ - ", - ); - let bytes = element.to_bytes_le(); - let from_bytes = FieldElement::::from_bytes_le(&bytes).unwrap(); - assert_eq!(element, from_bytes); + #[test] + #[cfg(feature = "std")] + fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_le() { + let element = + FieldElement::::from_hex_unchecked("0123456701234567"); + let bytes = element.to_bytes_le(); + let expected_bytes: [u8; 8] = ByteConversion::to_bytes_le(&element).try_into().unwrap(); + assert_eq!(bytes, expected_bytes); + } + + #[test] + #[cfg(feature = "std")] + fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_be() { + let element = + FieldElement::::from_hex_unchecked("0123456701234567"); + let bytes = element.to_bytes_be(); + let expected_bytes: [u8; 8] = ByteConversion::to_bytes_be(&element).try_into().unwrap(); + assert_eq!(bytes, expected_bytes); + } + + #[test] + fn byte_serialization_and_deserialization_works_le() { + let element = + FieldElement::::from_hex_unchecked("7654321076543210"); + let bytes = element.to_bytes_le(); + let from_bytes = FieldElement::::from_bytes_le(&bytes).unwrap(); + assert_eq!(element, from_bytes); + } + + #[test] + fn byte_serialization_and_deserialization_works_be() { + let element = + FieldElement::::from_hex_unchecked("7654321076543210"); + let bytes = element.to_bytes_be(); + let from_bytes = FieldElement::::from_bytes_be(&bytes).unwrap(); + assert_eq!(element, from_bytes); + } } - #[test] - - fn byte_serialization_and_deserialization_works_be() { - let element = FieldElement::::from_hex_unchecked( - "\ - 7654321076543210\ - ", - ); - let bytes = element.to_bytes_be(); - let from_bytes = FieldElement::::from_bytes_be(&bytes).unwrap(); - assert_eq!(element, from_bytes); + #[cfg(all(feature = "std", not(feature = "instruments")))] + mod test_babybear_31_fft { + use super::*; + #[cfg(not(any(feature = "metal", feature = "cuda")))] + use crate::fft::cpu::roots_of_unity::{ + get_powers_of_primitive_root, get_powers_of_primitive_root_coset, + }; + #[cfg(not(any(feature = "metal", feature = "cuda")))] + use crate::fft::polynomial::FFTPoly; + use crate::field::element::FieldElement; + #[cfg(not(any(feature = "metal", feature = "cuda")))] + use crate::field::traits::{IsFFTField, RootsConfig}; + use crate::polynomial::Polynomial; + use proptest::{collection, prelude::*, std_facade::Vec}; + + #[cfg(not(any(feature = "metal", feature = "cuda")))] + fn gen_fft_and_naive_evaluation( + poly: Polynomial>, + ) -> (Vec>, Vec>) { + let len = poly.coeff_len().next_power_of_two(); + let order = len.trailing_zeros(); + let twiddles = + get_powers_of_primitive_root(order.into(), len, RootsConfig::Natural).unwrap(); + + let fft_eval = poly.evaluate_fft(1, None).unwrap(); + let naive_eval = poly.evaluate_slice(&twiddles); + + (fft_eval, naive_eval) + } + + #[cfg(not(any(feature = "metal", feature = "cuda")))] + fn gen_fft_coset_and_naive_evaluation( + poly: Polynomial>, + offset: FieldElement, + blowup_factor: usize, + ) -> (Vec>, Vec>) { + let len = poly.coeff_len().next_power_of_two(); + let order = (len * blowup_factor).trailing_zeros(); + let twiddles = + get_powers_of_primitive_root_coset(order.into(), len * blowup_factor, &offset) + .unwrap(); + + let fft_eval = poly + .evaluate_offset_fft(blowup_factor, None, &offset) + .unwrap(); + let naive_eval = poly.evaluate_slice(&twiddles); + + (fft_eval, naive_eval) + } + + #[cfg(not(any(feature = "metal", feature = "cuda")))] + fn gen_fft_and_naive_interpolate( + fft_evals: &[FieldElement], + ) -> (Polynomial>, Polynomial>) { + let order = fft_evals.len().trailing_zeros() as u64; + let twiddles = + get_powers_of_primitive_root(order, 1 << order, RootsConfig::Natural).unwrap(); + + let naive_poly = Polynomial::interpolate(&twiddles, fft_evals).unwrap(); + let fft_poly = Polynomial::interpolate_fft(fft_evals).unwrap(); + + (fft_poly, naive_poly) + } + + #[cfg(not(any(feature = "metal", feature = "cuda")))] + fn gen_fft_and_naive_coset_interpolate( + fft_evals: &[FieldElement], + offset: &FieldElement, + ) -> (Polynomial>, Polynomial>) { + let order = fft_evals.len().trailing_zeros() as u64; + let twiddles = get_powers_of_primitive_root_coset(order, 1 << order, offset).unwrap(); + + let naive_poly = Polynomial::interpolate(&twiddles, fft_evals).unwrap(); + let fft_poly = Polynomial::interpolate_offset_fft(fft_evals, offset).unwrap(); + + (fft_poly, naive_poly) + } + + #[cfg(not(any(feature = "metal", feature = "cuda")))] + fn gen_fft_interpolate_and_evaluate( + poly: Polynomial>, + ) -> (Polynomial>, Polynomial>) { + let eval = poly.evaluate_fft(1, None).unwrap(); + let new_poly = Polynomial::interpolate_fft(&eval).unwrap(); + + (poly, new_poly) + } + + prop_compose! { + fn powers_of_two(max_exp: u8)(exp in 1..max_exp) -> usize { 1 << exp } + // max_exp cannot be multiple of the bits that represent a usize, generally 64 or 32. + // also it can't exceed the test field's two-adicity. + } + prop_compose! { + fn field_element()(num in any::().prop_filter("Avoid null coefficients", |x| x != &0)) -> FieldElement { + FieldElement::::from(num) + } + } + prop_compose! { + fn offset()(num in any::(), factor in any::()) -> FieldElement { FieldElement::::from(num).pow(factor) } + } + prop_compose! { + fn field_vec(max_exp: u8)(vec in collection::vec(field_element(), 0..1 << max_exp)) -> Vec> { + vec + } + } + prop_compose! { + fn non_power_of_two_sized_field_vec(max_exp: u8)(vec in collection::vec(field_element(), 2..1< Vec> { + vec + } + } + prop_compose! { + fn poly(max_exp: u8)(coeffs in field_vec(max_exp)) -> Polynomial> { + Polynomial::new(&coeffs) + } + } + prop_compose! { + fn poly_with_non_power_of_two_coeffs(max_exp: u8)(coeffs in non_power_of_two_sized_field_vec(max_exp)) -> Polynomial> { + Polynomial::new(&coeffs) + } + } + + proptest! { + // Property-based test that ensures FFT eval. gives same result as a naive polynomial evaluation. + #[test] + #[cfg(not(any(feature = "metal",feature = "cuda")))] + fn test_fft_matches_naive_evaluation(poly in poly(8)) { + let (fft_eval, naive_eval) = gen_fft_and_naive_evaluation(poly); + prop_assert_eq!(fft_eval, naive_eval); + } + + // Property-based test that ensures FFT eval. with coset gives same result as a naive polynomial evaluation. + #[test] + #[cfg(not(any(feature = "metal",feature = "cuda")))] + fn test_fft_coset_matches_naive_evaluation(poly in poly(4), offset in offset(), blowup_factor in powers_of_two(4)) { + let (fft_eval, naive_eval) = gen_fft_coset_and_naive_evaluation(poly, offset, blowup_factor); + prop_assert_eq!(fft_eval, naive_eval); + } + + // #[cfg(not(any(feature = "metal"),not(feature = "cuda")))] + // Property-based test that ensures FFT interpolation is the same as naive.. + #[test] + #[cfg(not(any(feature = "metal",feature = "cuda")))] + fn test_fft_interpolate_matches_naive(fft_evals in field_vec(4) + .prop_filter("Avoid polynomials of size not power of two", + |evals| evals.len().is_power_of_two())) { + let (fft_poly, naive_poly) = gen_fft_and_naive_interpolate(&fft_evals); + prop_assert_eq!(fft_poly, naive_poly); + } + + // Property-based test that ensures FFT interpolation with an offset is the same as naive. + #[test] + #[cfg(not(any(feature = "metal",feature = "cuda")))] + fn test_fft_interpolate_coset_matches_naive(offset in offset(), fft_evals in field_vec(4) + .prop_filter("Avoid polynomials of size not power of two", + |evals| evals.len().is_power_of_two())) { + let (fft_poly, naive_poly) = gen_fft_and_naive_coset_interpolate(&fft_evals, &offset); + prop_assert_eq!(fft_poly, naive_poly); + } + + // Property-based test that ensures interpolation is the inverse operation of evaluation. + #[test] + #[cfg(not(any(feature = "metal",feature = "cuda")))] + fn test_fft_interpolate_is_inverse_of_evaluate( + poly in poly(4).prop_filter("Avoid non pows of two", |poly| poly.coeff_len().is_power_of_two())) { + let (poly, new_poly) = gen_fft_interpolate_and_evaluate(poly); + prop_assert_eq!(poly, new_poly); + } + } } } From 75dd92f9593bd2629ff7bf5d69b56c16a518d04b Mon Sep 17 00:00:00 2001 From: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> Date: Mon, 11 Dec 2023 15:57:34 -0300 Subject: [PATCH 14/29] Math: FFT over field extensions (#711) * add subfield trait * implement IsSubfieldOf for Degree2ExtensionField struct in BLS12381 * fix mul * avoid using field elements in issubfield impl. Add tests * clippy and fmt * simplify trait bounds * add explicit type * change iter to into_iter * fix metal code * remove explicit type * refactor fftpoly * fmt * into iter for gpu * update evaluate_fft_metal * update metal fft * fix imports * add explicit types * clippy, fmt * update metal benches * add explicit types * refactor quadratic extensions and implement IsSubFieldOf * refactor cubic extensions and implement issubfield * add to_subfield_vec method to field element * add test fft over field extension * fix type * clippy * make to_subfield_vec available only under std feature * fmt * run test only on std feature * update fft docs * update docs. Fix babybear --- math/benches/utils/fft_functions.rs | 8 +- math/benches/utils/metal_functions.rs | 5 +- math/src/fft/cpu/fft.rs | 16 +- math/src/fft/cpu/ops.rs | 15 +- math/src/fft/cpu/roots_of_unity.rs | 2 +- math/src/fft/gpu/metal/ops.rs | 18 ++- math/src/fft/gpu/metal/polynomial.rs | 22 +-- math/src/fft/polynomial.rs | 153 +++++++++--------- .../src/field/fields/fft_friendly/babybear.rs | 14 +- math/src/field/test_fields/u64_test_field.rs | 35 +++- math/src/polynomial.rs | 4 +- provers/groth16/src/qap.rs | 35 ++-- provers/plonk/src/prover.rs | 54 +++---- provers/plonk/src/setup.rs | 17 +- provers/plonk/src/test_utils/circuit_1.rs | 17 +- provers/plonk/src/test_utils/circuit_json.rs | 47 ++++-- provers/stark/src/debug.rs | 7 +- provers/stark/src/fri/mod.rs | 6 +- provers/stark/src/prover.rs | 5 +- provers/stark/src/trace.rs | 3 +- provers/stark/src/traits.rs | 4 +- 21 files changed, 277 insertions(+), 210 deletions(-) diff --git a/math/benches/utils/fft_functions.rs b/math/benches/utils/fft_functions.rs index 7dc97e04f3..2b2f50fb65 100644 --- a/math/benches/utils/fft_functions.rs +++ b/math/benches/utils/fft_functions.rs @@ -6,9 +6,7 @@ use lambdaworks_math::fft::cpu::{ fft::{in_place_nr_2radix_fft, in_place_rn_2radix_fft}, roots_of_unity::get_twiddles, }; -use lambdaworks_math::{ - fft::polynomial::FFTPoly, field::traits::RootsConfig, polynomial::Polynomial, -}; +use lambdaworks_math::{field::traits::RootsConfig, polynomial::Polynomial}; use super::stark252_utils::{F, FE}; @@ -29,9 +27,9 @@ pub fn bitrev_permute(input: &mut [FE]) { } pub fn poly_evaluate_fft(poly: &Polynomial) -> Vec { - poly.evaluate_fft(black_box(1), black_box(None)).unwrap() + Polynomial::evaluate_fft::(poly, black_box(1), black_box(None)).unwrap() } pub fn poly_interpolate_fft(evals: &[FE]) { - Polynomial::interpolate_fft(evals).unwrap(); + Polynomial::interpolate_fft::(evals).unwrap(); } diff --git a/math/benches/utils/metal_functions.rs b/math/benches/utils/metal_functions.rs index 6838c291b1..7fd60c514f 100644 --- a/math/benches/utils/metal_functions.rs +++ b/math/benches/utils/metal_functions.rs @@ -1,6 +1,5 @@ use lambdaworks_gpu::metal::abstractions::state::MetalState; use lambdaworks_math::fft::gpu::metal::ops::*; -use lambdaworks_math::fft::polynomial::FFTPoly; use lambdaworks_math::{field::traits::RootsConfig, polynomial::Polynomial}; // WARN: These should always be fields supported by Metal, else the last two benches will use CPU FFT. @@ -22,8 +21,8 @@ pub fn bitrev_permute(input: &[FE]) { } pub fn poly_evaluate_fft(poly: &Polynomial) { - poly.evaluate_fft(1, None).unwrap(); + Polynomial::evaluate_fft::(poly, 1, None).unwrap(); } pub fn poly_interpolate_fft(evals: &[FE]) { - Polynomial::interpolate_fft(evals).unwrap(); + Polynomial::interpolate_fft::(evals).unwrap(); } diff --git a/math/src/fft/cpu/fft.rs b/math/src/fft/cpu/fft.rs index 194e596dda..724ca9ca04 100644 --- a/math/src/fft/cpu/fft.rs +++ b/math/src/fft/cpu/fft.rs @@ -1,4 +1,7 @@ -use crate::field::{element::FieldElement, traits::IsFFTField}; +use crate::field::{ + element::FieldElement, + traits::{IsFFTField, IsField, IsSubFieldOf}, +}; /// In-Place Radix-2 NR DIT FFT algorithm over a slice of two-adic field elements. /// It's required that the twiddle factors are in bit-reverse order. Else this function will not @@ -12,9 +15,12 @@ use crate::field::{element::FieldElement, traits::IsFFTField}; /// - NR: natural to reverse order, meaning that the input is naturally ordered and the output will /// be bit-reversed ordered. /// - DIT: decimation in time -pub fn in_place_nr_2radix_fft(input: &mut [FieldElement], twiddles: &[FieldElement]) +/// +/// It supports values in a field E and domain in a subfield F. +pub fn in_place_nr_2radix_fft(input: &mut [FieldElement], twiddles: &[FieldElement]) where - F: IsFFTField, + F: IsFFTField + IsSubFieldOf, + E: IsField, { // divide input in groups, starting with 1, duplicating the number of groups in each stage. let mut group_count = 1; @@ -60,6 +66,8 @@ where /// - RN: reverse to natural order, meaning that the input is bit-reversed ordered and the output will /// be naturally ordered. /// - DIT: decimation in time +/// +/// It supports values in a field E and domain in a subfield F. #[allow(dead_code)] pub fn in_place_rn_2radix_fft(input: &mut [FieldElement], twiddles: &[FieldElement]) where @@ -135,7 +143,7 @@ mod tests { let twiddles = get_twiddles(order.into(), RootsConfig::BitReverse).unwrap(); let mut result = coeffs; - in_place_nr_2radix_fft(&mut result, &twiddles); + in_place_nr_2radix_fft::(&mut result, &twiddles); in_place_bit_reverse_permute(&mut result); prop_assert_eq!(expected, result); diff --git a/math/src/fft/cpu/ops.rs b/math/src/fft/cpu/ops.rs index da9876f98d..f4bdffd7a3 100644 --- a/math/src/fft/cpu/ops.rs +++ b/math/src/fft/cpu/ops.rs @@ -1,16 +1,19 @@ use crate::{ fft::errors::FFTError, - field::{element::FieldElement, traits::IsFFTField}, + field::{ + element::FieldElement, + traits::{IsFFTField, IsField, IsSubFieldOf}, + }, }; use super::{bit_reversing::in_place_bit_reverse_permute, fft::in_place_nr_2radix_fft}; -/// Executes Fast Fourier Transform over elements of a two-adic finite field `F`. Usually used for -/// fast polynomial evaluation. -pub fn fft( - input: &[FieldElement], +/// Executes Fast Fourier Transform over elements of a two-adic finite field `E` and domain in a +/// subfield `F`. Usually used for fast polynomial evaluation. +pub fn fft, E: IsField>( + input: &[FieldElement], twiddles: &[FieldElement], -) -> Result>, FFTError> { +) -> Result>, FFTError> { if !input.len().is_power_of_two() { return Err(FFTError::InputError(input.len())); } diff --git a/math/src/fft/cpu/roots_of_unity.rs b/math/src/fft/cpu/roots_of_unity.rs index 6632cc6d16..43456860e0 100644 --- a/math/src/fft/cpu/roots_of_unity.rs +++ b/math/src/fft/cpu/roots_of_unity.rs @@ -54,7 +54,7 @@ pub fn get_powers_of_primitive_root_coset( offset: &FieldElement, ) -> Result>, FFTError> { let root = F::get_primitive_root_of_unity(n)?; - let results = (0..count).map(|i| root.pow(i) * offset); + let results = (0..count).map(|i| offset * root.pow(i)); Ok(results.collect()) } diff --git a/math/src/fft/gpu/metal/ops.rs b/math/src/fft/gpu/metal/ops.rs index c9a347dfcc..c22fc86200 100644 --- a/math/src/fft/gpu/metal/ops.rs +++ b/math/src/fft/gpu/metal/ops.rs @@ -1,6 +1,6 @@ use crate::field::{ element::FieldElement, - traits::{IsFFTField, RootsConfig}, + traits::{IsFFTField, IsField, IsSubFieldOf, RootsConfig}, }; use lambdaworks_gpu::metal::abstractions::{errors::MetalError, state::*}; @@ -15,11 +15,17 @@ use core::mem; /// in this order too. Natural order means that input[i] corresponds to the i-th coefficient, /// as opposed to bit-reverse order in which input[bit_rev(i)] corresponds to the i-th /// coefficient. -pub fn fft( - input: &[FieldElement], +/// +/// It supports values in a field E and domain in a subfield F. +pub fn fft( + input: &[FieldElement], twiddles: &[FieldElement], state: &MetalState, -) -> Result>, MetalError> { +) -> Result>, MetalError> +where + F: IsFFTField + IsSubFieldOf, + E: IsField, +{ // TODO: make a twiddle factor abstraction for handling invalid twiddles if !input.len().is_power_of_two() { return Err(MetalError::InputError(input.len())); @@ -173,7 +179,7 @@ mod tests { fn test_metal_fft_matches_sequential(input in field_vec(6)) { let metal_state = MetalState::new(None).unwrap(); let order = input.len().trailing_zeros(); - let twiddles = get_twiddles(order.into(), RootsConfig::BitReverse).unwrap(); + let twiddles = get_twiddles::(order.into(), RootsConfig::BitReverse).unwrap(); let metal_result = super::fft(&input, &twiddles, &metal_state).unwrap(); let sequential_result = crate::fft::cpu::ops::fft(&input, &twiddles).unwrap(); @@ -190,7 +196,7 @@ mod tests { let metal_state = MetalState::new(None).unwrap(); let order = input.len().trailing_zeros(); - let twiddles = get_twiddles(order.into(), RootsConfig::BitReverse).unwrap(); + let twiddles = get_twiddles::(order.into(), RootsConfig::BitReverse).unwrap(); let metal_result = super::fft(&input, &twiddles, &metal_state).unwrap(); let sequential_result = crate::fft::cpu::ops::fft(&input, &twiddles).unwrap(); diff --git a/math/src/fft/gpu/metal/polynomial.rs b/math/src/fft/gpu/metal/polynomial.rs index 4e1714fe41..f46e3e29ac 100644 --- a/math/src/fft/gpu/metal/polynomial.rs +++ b/math/src/fft/gpu/metal/polynomial.rs @@ -1,7 +1,7 @@ use crate::{ field::{ element::FieldElement, - traits::{IsFFTField, RootsConfig}, + traits::{IsFFTField, IsField, IsSubFieldOf, RootsConfig}, }, polynomial::Polynomial, }; @@ -9,30 +9,34 @@ use lambdaworks_gpu::metal::abstractions::{errors::MetalError, state::MetalState use super::ops::*; -pub fn evaluate_fft_metal(coeffs: &[FieldElement]) -> Result>, MetalError> +pub fn evaluate_fft_metal( + coeffs: &[FieldElement], +) -> Result>, MetalError> where - F: IsFFTField, + F: IsFFTField + IsSubFieldOf, + E: IsField, { let state = MetalState::new(None)?; let order = coeffs.len().trailing_zeros(); - let twiddles = gen_twiddles(order.into(), RootsConfig::BitReverse, &state)?; + let twiddles = gen_twiddles::(order.into(), RootsConfig::BitReverse, &state)?; fft(coeffs, &twiddles, &state) } /// Returns a new polynomial that interpolates `fft_evals`, which are evaluations using twiddle /// factors. This is considered to be the inverse operation of [evaluate_fft_metal()]. -pub fn interpolate_fft_metal( - fft_evals: &[FieldElement], -) -> Result>, MetalError> +pub fn interpolate_fft_metal( + fft_evals: &[FieldElement], +) -> Result>, MetalError> where - F: IsFFTField, + F: IsFFTField + IsSubFieldOf, + E: IsField, { let metal_state = MetalState::new(None)?; let order = fft_evals.len().trailing_zeros(); - let twiddles = gen_twiddles(order.into(), RootsConfig::BitReverseInversed, &metal_state)?; + let twiddles = gen_twiddles::(order.into(), RootsConfig::BitReverseInversed, &metal_state)?; let coeffs = fft(fft_evals, &twiddles, &metal_state)?; diff --git a/math/src/fft/polynomial.rs b/math/src/fft/polynomial.rs index eaaf40b97f..481e326925 100644 --- a/math/src/fft/polynomial.rs +++ b/math/src/fft/polynomial.rs @@ -1,5 +1,6 @@ use crate::fft::errors::FFTError; +use crate::field::traits::{IsField, IsSubFieldOf}; use crate::{ field::{ element::FieldElement, @@ -15,58 +16,37 @@ use crate::fft::gpu::metal::polynomial::{evaluate_fft_metal, interpolate_fft_met use super::cpu::{ops, roots_of_unity}; -pub trait FFTPoly { - fn evaluate_fft( - &self, - blowup_factor: usize, - domain_size: Option, - ) -> Result>, FFTError>; - fn evaluate_offset_fft( - &self, - blowup_factor: usize, - domain_size: Option, - offset: &FieldElement, - ) -> Result>, FFTError>; - fn interpolate_fft( - fft_evals: &[FieldElement], - ) -> Result>, FFTError>; - fn interpolate_offset_fft( - fft_evals: &[FieldElement], - offset: &FieldElement, - ) -> Result>, FFTError>; -} - -impl FFTPoly for Polynomial> { - /// Returns `N` evaluations of this polynomial using FFT (so the results +impl Polynomial> { + /// Returns `N` evaluations of this polynomial using FFT over a domain in a subfield F of E (so the results /// are P(w^i), with w being a primitive root of unity). /// `N = max(self.coeff_len(), domain_size).next_power_of_two() * blowup_factor`. /// If `domain_size` is `None`, it defaults to 0. - fn evaluate_fft( - &self, + pub fn evaluate_fft>( + poly: &Polynomial>, blowup_factor: usize, domain_size: Option, - ) -> Result>, FFTError> { + ) -> Result>, FFTError> { let domain_size = domain_size.unwrap_or(0); - let len = std::cmp::max(self.coeff_len(), domain_size).next_power_of_two() * blowup_factor; + let len = std::cmp::max(poly.coeff_len(), domain_size).next_power_of_two() * blowup_factor; - if self.coefficients().is_empty() { + if poly.coefficients().is_empty() { return Ok(vec![FieldElement::zero(); len]); } - let mut coeffs = self.coefficients().to_vec(); + let mut coeffs = poly.coefficients().to_vec(); coeffs.resize(len, FieldElement::zero()); // padding with zeros will make FFT return more evaluations of the same polynomial. #[cfg(feature = "metal")] { if !F::field_name().is_empty() { - Ok(evaluate_fft_metal(&coeffs)?) + Ok(evaluate_fft_metal::(&coeffs)?) } else { println!( "GPU evaluation failed for field {}. Program will fallback to CPU.", std::any::type_name::() ); - evaluate_fft_cpu(&coeffs) + evaluate_fft_cpu::(&coeffs) } } @@ -76,44 +56,46 @@ impl FFTPoly for Polynomial> { if F::field_name() == "stark256" { Ok(evaluate_fft_cuda(&coeffs)?) } else { - evaluate_fft_cpu(&coeffs) + evaluate_fft_cpu::(&coeffs) } } #[cfg(all(not(feature = "metal"), not(feature = "cuda")))] { - evaluate_fft_cpu(&coeffs) + evaluate_fft_cpu::(&coeffs) } } - /// Returns `N` evaluations with an offset of this polynomial using FFT + /// Returns `N` evaluations with an offset of this polynomial using FFT over a domain in a subfield F of E /// (so the results are P(w^i), with w being a primitive root of unity). /// `N = max(self.coeff_len(), domain_size).next_power_of_two() * blowup_factor`. /// If `domain_size` is `None`, it defaults to 0. - fn evaluate_offset_fft( - &self, + pub fn evaluate_offset_fft>( + poly: &Polynomial>, blowup_factor: usize, domain_size: Option, offset: &FieldElement, - ) -> Result>, FFTError> { - let scaled = self.scale(offset); - scaled.evaluate_fft(blowup_factor, domain_size) + ) -> Result>, FFTError> { + let scaled = poly.scale(offset); + Polynomial::evaluate_fft::(&scaled, blowup_factor, domain_size) } /// Returns a new polynomial that interpolates `(w^i, fft_evals[i])`, with `w` being a - /// Nth primitive root of unity, and `i in 0..N`, with `N = fft_evals.len()`. + /// Nth primitive root of unity in a subfield F of E, and `i in 0..N`, with `N = fft_evals.len()`. /// This is considered to be the inverse operation of [Self::evaluate_fft()]. - fn interpolate_fft(fft_evals: &[FieldElement]) -> Result { + pub fn interpolate_fft>( + fft_evals: &[FieldElement], + ) -> Result { #[cfg(feature = "metal")] { if !F::field_name().is_empty() { - Ok(interpolate_fft_metal(fft_evals)?) + Ok(interpolate_fft_metal::(fft_evals)?) } else { println!( "GPU interpolation failed for field {}. Program will fallback to CPU.", std::any::type_name::() ); - interpolate_fft_cpu(fft_evals) + interpolate_fft_cpu::(fft_evals) } } @@ -122,63 +104,67 @@ impl FFTPoly for Polynomial> { if !F::field_name().is_empty() { Ok(interpolate_fft_cuda(fft_evals)?) } else { - interpolate_fft_cpu(fft_evals) + interpolate_fft_cpu::(fft_evals) } } #[cfg(all(not(feature = "metal"), not(feature = "cuda")))] { - interpolate_fft_cpu(fft_evals) + interpolate_fft_cpu::(fft_evals) } } /// Returns a new polynomial that interpolates offset `(w^i, fft_evals[i])`, with `w` being a - /// Nth primitive root of unity, and `i in 0..N`, with `N = fft_evals.len()`. + /// Nth primitive root of unity in a subfield F of E, and `i in 0..N`, with `N = fft_evals.len()`. /// This is considered to be the inverse operation of [Self::evaluate_offset_fft()]. - fn interpolate_offset_fft( - fft_evals: &[FieldElement], + pub fn interpolate_offset_fft>( + fft_evals: &[FieldElement], offset: &FieldElement, - ) -> Result>, FFTError> { - let scaled = Polynomial::interpolate_fft(fft_evals)?; + ) -> Result>, FFTError> { + let scaled = Polynomial::interpolate_fft::(fft_evals)?; Ok(scaled.scale(&offset.inv().unwrap())) } } -pub fn compose_fft( - poly_1: &Polynomial>, - poly_2: &Polynomial>, -) -> Polynomial> +pub fn compose_fft( + poly_1: &Polynomial>, + poly_2: &Polynomial>, +) -> Polynomial> where - F: IsFFTField, + F: IsFFTField + IsSubFieldOf, + E: IsField, { - let poly_2_evaluations = poly_2.evaluate_fft(1, None).unwrap(); + let poly_2_evaluations = Polynomial::evaluate_fft::(poly_2, 1, None).unwrap(); let values: Vec<_> = poly_2_evaluations .iter() .map(|value| poly_1.evaluate(value)) .collect(); - Polynomial::interpolate_fft(values.as_slice()).unwrap() + Polynomial::interpolate_fft::(values.as_slice()).unwrap() } -pub fn evaluate_fft_cpu(coeffs: &[FieldElement]) -> Result>, FFTError> +pub fn evaluate_fft_cpu(coeffs: &[FieldElement]) -> Result>, FFTError> where - F: IsFFTField, + F: IsFFTField + IsSubFieldOf, + E: IsField, { let order = coeffs.len().trailing_zeros(); - let twiddles = roots_of_unity::get_twiddles(order.into(), RootsConfig::BitReverse)?; + let twiddles = roots_of_unity::get_twiddles::(order.into(), RootsConfig::BitReverse)?; // Bit reverse order is needed for NR DIT FFT. ops::fft(coeffs, &twiddles) } -pub fn interpolate_fft_cpu( - fft_evals: &[FieldElement], -) -> Result>, FFTError> +pub fn interpolate_fft_cpu( + fft_evals: &[FieldElement], +) -> Result>, FFTError> where - F: IsFFTField, + F: IsFFTField + IsSubFieldOf, + E: IsField, { let order = fft_evals.len().trailing_zeros(); - let twiddles = roots_of_unity::get_twiddles(order.into(), RootsConfig::BitReverseInversed)?; + let twiddles = + roots_of_unity::get_twiddles::(order.into(), RootsConfig::BitReverseInversed)?; let coeffs = ops::fft(fft_evals, &twiddles)?; @@ -191,7 +177,10 @@ mod tests { #[cfg(all(not(feature = "metal"), not(feature = "cuda")))] use crate::field::traits::IsField; - use crate::field::traits::RootsConfig; + use crate::field::{ + test_fields::u64_test_field::{U64TestField, U64TestFieldExtension}, + traits::RootsConfig, + }; use proptest::{collection, prelude::*}; use roots_of_unity::{get_powers_of_primitive_root, get_powers_of_primitive_root_coset}; @@ -206,7 +195,7 @@ mod tests { let twiddles = get_powers_of_primitive_root(order.into(), len, RootsConfig::Natural).unwrap(); - let fft_eval = poly.evaluate_fft(1, None).unwrap(); + let fft_eval = Polynomial::evaluate_fft::(&poly, 1, None).unwrap(); let naive_eval = poly.evaluate_slice(&twiddles); (fft_eval, naive_eval) @@ -222,9 +211,8 @@ mod tests { let twiddles = get_powers_of_primitive_root_coset(order.into(), len * blowup_factor, &offset).unwrap(); - let fft_eval = poly - .evaluate_offset_fft(blowup_factor, None, &offset) - .unwrap(); + let fft_eval = + Polynomial::evaluate_offset_fft::(&poly, blowup_factor, None, &offset).unwrap(); let naive_eval = poly.evaluate_slice(&twiddles); (fft_eval, naive_eval) @@ -238,7 +226,7 @@ mod tests { get_powers_of_primitive_root(order, 1 << order, RootsConfig::Natural).unwrap(); let naive_poly = Polynomial::interpolate(&twiddles, fft_evals).unwrap(); - let fft_poly = Polynomial::interpolate_fft(fft_evals).unwrap(); + let fft_poly = Polynomial::interpolate_fft::(fft_evals).unwrap(); (fft_poly, naive_poly) } @@ -259,8 +247,8 @@ mod tests { fn gen_fft_interpolate_and_evaluate( poly: Polynomial>, ) -> (Polynomial>, Polynomial>) { - let eval = poly.evaluate_fft(1, None).unwrap(); - let new_poly = Polynomial::interpolate_fft(&eval).unwrap(); + let eval = Polynomial::evaluate_fft::(&poly, 1, None).unwrap(); + let new_poly = Polynomial::interpolate_fft::(&eval).unwrap(); (poly, new_poly) } @@ -357,7 +345,7 @@ mod tests { let p = Polynomial::new(&[FE::new(0), FE::new(2)]); let q = Polynomial::new(&[FE::new(0), FE::new(0), FE::new(0), FE::new(1)]); assert_eq!( - compose_fft(&p, &q), + compose_fft::(&p, &q), Polynomial::new(&[FE::new(0), FE::new(0), FE::new(0), FE::new(2)]) ); } @@ -447,4 +435,21 @@ mod tests { } } } + + #[test] + fn test_fft_with_values_in_field_extension_over_domain_in_prime_field() { + type TF = U64TestField; + type TL = U64TestFieldExtension; + + let a = FieldElement::::from(&[FieldElement::one(), FieldElement::one()]); + let b = FieldElement::::from(&[-FieldElement::from(2), FieldElement::from(17)]); + let c = FieldElement::::one(); + let poly = Polynomial::new(&[a, b, c]); + + let eval = Polynomial::evaluate_offset_fft::(&poly, 8, Some(4), &FieldElement::from(2)) + .unwrap(); + let new_poly = + Polynomial::interpolate_offset_fft::(&eval, &FieldElement::from(2)).unwrap(); + assert_eq!(poly, new_poly); + } } diff --git a/math/src/field/fields/fft_friendly/babybear.rs b/math/src/field/fields/fft_friendly/babybear.rs index 01aa9e920f..a516e85232 100644 --- a/math/src/field/fields/fft_friendly/babybear.rs +++ b/math/src/field/fields/fft_friendly/babybear.rs @@ -102,7 +102,6 @@ mod tests { get_powers_of_primitive_root, get_powers_of_primitive_root_coset, }; #[cfg(not(any(feature = "metal", feature = "cuda")))] - use crate::fft::polynomial::FFTPoly; use crate::field::element::FieldElement; #[cfg(not(any(feature = "metal", feature = "cuda")))] use crate::field::traits::{IsFFTField, RootsConfig}; @@ -118,7 +117,7 @@ mod tests { let twiddles = get_powers_of_primitive_root(order.into(), len, RootsConfig::Natural).unwrap(); - let fft_eval = poly.evaluate_fft(1, None).unwrap(); + let fft_eval = Polynomial::evaluate_fft::(&poly, 1, None).unwrap(); let naive_eval = poly.evaluate_slice(&twiddles); (fft_eval, naive_eval) @@ -136,9 +135,8 @@ mod tests { get_powers_of_primitive_root_coset(order.into(), len * blowup_factor, &offset) .unwrap(); - let fft_eval = poly - .evaluate_offset_fft(blowup_factor, None, &offset) - .unwrap(); + let fft_eval = + Polynomial::evaluate_offset_fft::(&poly, blowup_factor, None, &offset).unwrap(); let naive_eval = poly.evaluate_slice(&twiddles); (fft_eval, naive_eval) @@ -153,7 +151,7 @@ mod tests { get_powers_of_primitive_root(order, 1 << order, RootsConfig::Natural).unwrap(); let naive_poly = Polynomial::interpolate(&twiddles, fft_evals).unwrap(); - let fft_poly = Polynomial::interpolate_fft(fft_evals).unwrap(); + let fft_poly = Polynomial::interpolate_fft::(fft_evals).unwrap(); (fft_poly, naive_poly) } @@ -176,8 +174,8 @@ mod tests { fn gen_fft_interpolate_and_evaluate( poly: Polynomial>, ) -> (Polynomial>, Polynomial>) { - let eval = poly.evaluate_fft(1, None).unwrap(); - let new_poly = Polynomial::interpolate_fft(&eval).unwrap(); + let eval = Polynomial::evaluate_fft::(&poly, 1, None).unwrap(); + let new_poly = Polynomial::interpolate_fft::(&eval).unwrap(); (poly, new_poly) } diff --git a/math/src/field/test_fields/u64_test_field.rs b/math/src/field/test_fields/u64_test_field.rs index 780924d2a4..bd24582f06 100644 --- a/math/src/field/test_fields/u64_test_field.rs +++ b/math/src/field/test_fields/u64_test_field.rs @@ -1,7 +1,11 @@ use crate::{ errors::CreationError, - field::errors::FieldError, - field::traits::{IsFFTField, IsField, IsPrimeField}, + field::{ + element::FieldElement, + extensions::quadratic::QuadraticExtensionField, + traits::{IsFFTField, IsField, IsPrimeField}, + }, + field::{errors::FieldError, extensions::quadratic::HasQuadraticNonResidue}, }; #[derive(Debug, Clone, PartialEq, Eq)] @@ -94,9 +98,23 @@ impl IsFFTField for U64TestField { const TWO_ADIC_PRIMITVE_ROOT_OF_UNITY: u64 = 1753635133440165772; } +#[derive(Clone, Debug)] +pub struct TestNonResidue; +impl HasQuadraticNonResidue for TestNonResidue { + fn residue() -> FieldElement { + FieldElement::from(7) + } +} + +pub type U64TestFieldExtension = QuadraticExtensionField; + #[cfg(test)] mod tests_u64_test_field { - use crate::field::{test_fields::u64_test_field::U64TestField, traits::IsPrimeField}; + use crate::field::{ + element::FieldElement, + test_fields::u64_test_field::{U64TestField, U64TestFieldExtension}, + traits::IsPrimeField, + }; #[test] fn from_hex_for_b_is_11() { @@ -110,4 +128,15 @@ mod tests_u64_test_field { 64 ); } + + #[cfg(feature = "std")] + #[test] + fn test_to_subfield_vec() { + let a = FieldElement::::from(&[ + FieldElement::from(1), + FieldElement::from(3), + ]); + let b = a.to_subfield_vec::(); + assert_eq!(b, vec![FieldElement::from(1), FieldElement::from(3)]); + } } diff --git a/math/src/polynomial.rs b/math/src/polynomial.rs index 940e49cfc8..d479f8881d 100644 --- a/math/src/polynomial.rs +++ b/math/src/polynomial.rs @@ -1,5 +1,5 @@ use super::field::element::FieldElement; -use crate::field::traits::IsField; +use crate::field::traits::{IsField, IsSubFieldOf}; use std::ops; /// Represents the polynomial c_0 + c_1 * X + c_2 * X^2 + ... + c_n * X^n @@ -202,7 +202,7 @@ impl Polynomial> { } } - pub fn scale(&self, factor: &FieldElement) -> Self { + pub fn scale>(&self, factor: &FieldElement) -> Self { let scaled_coefficients = self .coefficients .iter() diff --git a/provers/groth16/src/qap.rs b/provers/groth16/src/qap.rs index 35bb8f76d5..d2433c4f5b 100644 --- a/provers/groth16/src/qap.rs +++ b/provers/groth16/src/qap.rs @@ -1,4 +1,4 @@ -use lambdaworks_math::{fft::polynomial::FFTPoly, polynomial::Polynomial}; +use lambdaworks_math::polynomial::Polynomial; use crate::common::*; @@ -56,9 +56,12 @@ impl QuadraticArithmeticProgram { let [l, r, o] = self.scale_and_accumulate_variable_polynomials(w, degree, offset); // TODO: Change to a vector of offsetted evaluations of x^N-1 - let mut t = (Polynomial::new_monomial(FrElement::one(), self.num_of_gates()) - - FrElement::one()) - .evaluate_offset_fft(1, Some(degree), offset) + let mut t = Polynomial::evaluate_offset_fft( + &(Polynomial::new_monomial(FrElement::one(), self.num_of_gates()) - FrElement::one()), + 1, + Some(degree), + offset, + ) .unwrap(); FrElement::inplace_batch_inverse(&mut t).unwrap(); @@ -91,7 +94,7 @@ impl QuadraticArithmeticProgram { fn build_variable_polynomials(from_matrix: &[Vec]) -> Vec> { from_matrix .iter() - .map(|row| Polynomial::interpolate_fft(row).unwrap()) + .map(|row| Polynomial::interpolate_fft::(row).unwrap()) .collect() } @@ -105,14 +108,20 @@ impl QuadraticArithmeticProgram { offset: &FrElement, ) -> [Vec; 3] { [&self.l, &self.r, &self.o].map(|var_polynomials| { - var_polynomials - .iter() - .zip(w) - .map(|(poly, coeff)| poly.mul_with_ref(&Polynomial::new_monomial(coeff.clone(), 0))) - .reduce(|poly1, poly2| poly1 + poly2) - .unwrap() - .evaluate_offset_fft(1, Some(degree), offset) - .unwrap() + Polynomial::evaluate_offset_fft( + &(var_polynomials + .iter() + .zip(w) + .map(|(poly, coeff)| { + poly.mul_with_ref(&Polynomial::new_monomial(coeff.clone(), 0)) + }) + .reduce(|poly1, poly2| poly1 + poly2) + .unwrap()), + 1, + Some(degree), + offset, + ) + .unwrap() }) } } diff --git a/provers/plonk/src/prover.rs b/provers/plonk/src/prover.rs index 5e78b70f55..b351d1c382 100644 --- a/provers/plonk/src/prover.rs +++ b/provers/plonk/src/prover.rs @@ -1,6 +1,5 @@ use lambdaworks_crypto::fiat_shamir::transcript::Transcript; use lambdaworks_math::errors::DeserializationError; -use lambdaworks_math::fft::polynomial::FFTPoly; use lambdaworks_math::field::traits::IsFFTField; use lambdaworks_math::traits::{Deserializable, IsRandomFieldElementGenerator, Serializable}; use std::marker::PhantomData; @@ -311,11 +310,11 @@ where witness: &Witness, common_preprocessed_input: &CommonPreprocessedInput, ) -> Round1Result { - let p_a = Polynomial::interpolate_fft(&witness.a) + let p_a = Polynomial::interpolate_fft::(&witness.a) .expect("xs and ys have equal length and xs are unique"); - let p_b = Polynomial::interpolate_fft(&witness.b) + let p_b = Polynomial::interpolate_fft::(&witness.b) .expect("xs and ys have equal length and xs are unique"); - let p_c = Polynomial::interpolate_fft(&witness.c) + let p_c = Polynomial::interpolate_fft::(&witness.c) .expect("xs and ys have equal length and xs are unique"); let z_h = Polynomial::new_monomial(FieldElement::one(), common_preprocessed_input.n) @@ -364,7 +363,7 @@ where coefficients.push(new_term); } - let p_z = Polynomial::interpolate_fft(&coefficients) + let p_z = Polynomial::interpolate_fft::(&coefficients) .expect("xs and ys have equal length and xs are unique"); let z_h = Polynomial::new_monomial(FieldElement::one(), common_preprocessed_input.n) - FieldElement::one(); @@ -392,7 +391,7 @@ where let k2 = &cpi.k1 * &cpi.k1; let one = Polynomial::new_monomial(FieldElement::one(), 0); - let p_x = &Polynomial::new_monomial(FieldElement::one(), 1); + let p_x = &Polynomial::new_monomial(FieldElement::::one(), 1); let zh = Polynomial::new_monomial(FieldElement::one(), cpi.n) - &one; let z_x_omega_coefficients: Vec> = p_z @@ -402,13 +401,13 @@ where .map(|(i, x)| x * &cpi.domain[i % cpi.n]) .collect(); let z_x_omega = Polynomial::new(&z_x_omega_coefficients); - let mut e1 = vec![FieldElement::zero(); cpi.domain.len()]; + let mut e1 = vec![FieldElement::::zero(); cpi.domain.len()]; e1[0] = FieldElement::one(); - let l1 = Polynomial::interpolate_fft(&e1) + let l1 = Polynomial::interpolate_fft::(&e1) .expect("xs and ys have equal length and xs are unique"); let mut p_pi_y = public_input.to_vec(); p_pi_y.append(&mut vec![FieldElement::zero(); cpi.n - public_input.len()]); - let p_pi = Polynomial::interpolate_fft(&p_pi_y) + let p_pi = Polynomial::interpolate_fft::(&p_pi_y) .expect("xs and ys have equal length and xs are unique"); // Compute p @@ -417,24 +416,23 @@ where // TODO: check a factor of 4 is a sensible upper bound let degree = 4 * cpi.n; let offset = &cpi.k1; - let p_a_eval = p_a.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let p_b_eval = p_b.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let p_c_eval = p_c.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let ql_eval = cpi.ql.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let qr_eval = cpi.qr.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let qm_eval = cpi.qm.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let qo_eval = cpi.qo.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let qc_eval = cpi.qc.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let p_pi_eval = p_pi.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let p_x_eval = p_x.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let p_z_eval = p_z.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let p_z_x_omega_eval = z_x_omega - .evaluate_offset_fft(1, Some(degree), offset) - .unwrap(); - let p_s1_eval = cpi.s1.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let p_s2_eval = cpi.s2.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let p_s3_eval = cpi.s3.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let l1_eval = l1.evaluate_offset_fft(1, Some(degree), offset).unwrap(); + let p_a_eval = Polynomial::evaluate_offset_fft(p_a, 1, Some(degree), offset).unwrap(); + let p_b_eval = Polynomial::evaluate_offset_fft(p_b, 1, Some(degree), offset).unwrap(); + let p_c_eval = Polynomial::evaluate_offset_fft(p_c, 1, Some(degree), offset).unwrap(); + let ql_eval = Polynomial::evaluate_offset_fft(&cpi.ql, 1, Some(degree), offset).unwrap(); + let qr_eval = Polynomial::evaluate_offset_fft(&cpi.qr, 1, Some(degree), offset).unwrap(); + let qm_eval = Polynomial::evaluate_offset_fft(&cpi.qm, 1, Some(degree), offset).unwrap(); + let qo_eval = Polynomial::evaluate_offset_fft(&cpi.qo, 1, Some(degree), offset).unwrap(); + let qc_eval = Polynomial::evaluate_offset_fft(&cpi.qc, 1, Some(degree), offset).unwrap(); + let p_pi_eval = Polynomial::evaluate_offset_fft(&p_pi, 1, Some(degree), offset).unwrap(); + let p_x_eval = Polynomial::evaluate_offset_fft(p_x, 1, Some(degree), offset).unwrap(); + let p_z_eval = Polynomial::evaluate_offset_fft(p_z, 1, Some(degree), offset).unwrap(); + let p_z_x_omega_eval = + Polynomial::evaluate_offset_fft(&z_x_omega, 1, Some(degree), offset).unwrap(); + let p_s1_eval = Polynomial::evaluate_offset_fft(&cpi.s1, 1, Some(degree), offset).unwrap(); + let p_s2_eval = Polynomial::evaluate_offset_fft(&cpi.s2, 1, Some(degree), offset).unwrap(); + let p_s3_eval = Polynomial::evaluate_offset_fft(&cpi.s3, 1, Some(degree), offset).unwrap(); + let l1_eval = Polynomial::evaluate_offset_fft(&l1, 1, Some(degree), offset).unwrap(); let p_constraints_eval: Vec<_> = p_a_eval .iter() @@ -496,7 +494,7 @@ where .map(|((p2, p1), co)| (p2 * &alpha + p1) * &alpha + co) .collect(); - let mut zh_eval = zh.evaluate_offset_fft(1, Some(degree), offset).unwrap(); + let mut zh_eval = Polynomial::evaluate_offset_fft(&zh, 1, Some(degree), offset).unwrap(); FieldElement::inplace_batch_inverse(&mut zh_eval).unwrap(); let c: Vec<_> = p_eval .iter() diff --git a/provers/plonk/src/setup.rs b/provers/plonk/src/setup.rs index 974c50e3ff..93d8fc41a5 100644 --- a/provers/plonk/src/setup.rs +++ b/provers/plonk/src/setup.rs @@ -5,7 +5,6 @@ use crate::test_utils::utils::{generate_domain, generate_permutation_coefficient use lambdaworks_crypto::commitments::traits::IsCommitmentScheme; use lambdaworks_crypto::fiat_shamir::default_transcript::DefaultTranscript; use lambdaworks_crypto::fiat_shamir::transcript::Transcript; -use lambdaworks_math::fft::polynomial::FFTPoly; use lambdaworks_math::field::traits::IsFFTField; use lambdaworks_math::field::{element::FieldElement, traits::IsField}; use lambdaworks_math::polynomial::Polynomial; @@ -86,14 +85,14 @@ impl CommonPreprocessedInput { n, omega, k1: order_r_minus_1_root_unity.clone(), - ql: Polynomial::interpolate_fft(&ql).unwrap(), // TODO: Remove unwraps - qr: Polynomial::interpolate_fft(&qr).unwrap(), - qo: Polynomial::interpolate_fft(&qo).unwrap(), - qm: Polynomial::interpolate_fft(&qm).unwrap(), - qc: Polynomial::interpolate_fft(&qc).unwrap(), - s1: Polynomial::interpolate_fft(&s1_lagrange).unwrap(), - s2: Polynomial::interpolate_fft(&s2_lagrange).unwrap(), - s3: Polynomial::interpolate_fft(&s3_lagrange).unwrap(), + ql: Polynomial::interpolate_fft::(&ql).unwrap(), // TODO: Remove unwraps + qr: Polynomial::interpolate_fft::(&qr).unwrap(), + qo: Polynomial::interpolate_fft::(&qo).unwrap(), + qm: Polynomial::interpolate_fft::(&qm).unwrap(), + qc: Polynomial::interpolate_fft::(&qc).unwrap(), + s1: Polynomial::interpolate_fft::(&s1_lagrange).unwrap(), + s2: Polynomial::interpolate_fft::(&s2_lagrange).unwrap(), + s3: Polynomial::interpolate_fft::(&s3_lagrange).unwrap(), s1_lagrange, s2_lagrange, s3_lagrange, diff --git a/provers/plonk/src/test_utils/circuit_1.rs b/provers/plonk/src/test_utils/circuit_1.rs index bc95c89623..929f735839 100644 --- a/provers/plonk/src/test_utils/circuit_1.rs +++ b/provers/plonk/src/test_utils/circuit_1.rs @@ -2,7 +2,6 @@ use super::utils::{ generate_domain, generate_permutation_coefficients, ORDER_R_MINUS_1_ROOT_UNITY, }; use crate::setup::{CommonPreprocessedInput, Witness}; -use lambdaworks_math::fft::polynomial::FFTPoly; use lambdaworks_math::{ elliptic_curve::short_weierstrass::curves::bls12_381::default_types::{FrElement, FrField}, field::{element::FieldElement, traits::IsFFTField}, @@ -40,7 +39,7 @@ pub fn test_common_preprocessed_input_1() -> CommonPreprocessedInput { domain, k1: ORDER_R_MINUS_1_ROOT_UNITY, // domain: domain.clone(), - ql: Polynomial::interpolate_fft(&[ + ql: Polynomial::interpolate_fft::(&[ -FieldElement::one(), -FieldElement::one(), FieldElement::zero(), @@ -48,7 +47,7 @@ pub fn test_common_preprocessed_input_1() -> CommonPreprocessedInput { ]) .unwrap(), - qr: Polynomial::interpolate_fft(&[ + qr: Polynomial::interpolate_fft::(&[ FieldElement::zero(), FieldElement::zero(), FieldElement::zero(), @@ -56,7 +55,7 @@ pub fn test_common_preprocessed_input_1() -> CommonPreprocessedInput { ]) .unwrap(), - qo: Polynomial::interpolate_fft(&[ + qo: Polynomial::interpolate_fft::(&[ FieldElement::zero(), FieldElement::zero(), -FieldElement::one(), @@ -64,7 +63,7 @@ pub fn test_common_preprocessed_input_1() -> CommonPreprocessedInput { ]) .unwrap(), - qm: Polynomial::interpolate_fft(&[ + qm: Polynomial::interpolate_fft::(&[ FieldElement::zero(), FieldElement::zero(), FieldElement::one(), @@ -72,7 +71,7 @@ pub fn test_common_preprocessed_input_1() -> CommonPreprocessedInput { ]) .unwrap(), - qc: Polynomial::interpolate_fft(&[ + qc: Polynomial::interpolate_fft::(&[ FieldElement::from(0_u64), FieldElement::from(0_u64), FieldElement::zero(), @@ -80,9 +79,9 @@ pub fn test_common_preprocessed_input_1() -> CommonPreprocessedInput { ]) .unwrap(), - s1: Polynomial::interpolate_fft(&s1_lagrange).unwrap(), - s2: Polynomial::interpolate_fft(&s2_lagrange).unwrap(), - s3: Polynomial::interpolate_fft(&s3_lagrange).unwrap(), + s1: Polynomial::interpolate_fft::(&s1_lagrange).unwrap(), + s2: Polynomial::interpolate_fft::(&s2_lagrange).unwrap(), + s3: Polynomial::interpolate_fft::(&s3_lagrange).unwrap(), s1_lagrange, s2_lagrange, diff --git a/provers/plonk/src/test_utils/circuit_json.rs b/provers/plonk/src/test_utils/circuit_json.rs index 13aea759e8..43913c844c 100644 --- a/provers/plonk/src/test_utils/circuit_json.rs +++ b/provers/plonk/src/test_utils/circuit_json.rs @@ -2,7 +2,6 @@ use super::utils::{ generate_domain, generate_permutation_coefficients, ORDER_R_MINUS_1_ROOT_UNITY, }; use crate::setup::{CommonPreprocessedInput, Witness}; -use lambdaworks_math::fft::polynomial::FFTPoly; use lambdaworks_math::field::traits::IsFFTField; use lambdaworks_math::{ elliptic_curve::short_weierstrass::curves::bls12_381::default_types::{FrElement, FrField}, @@ -62,19 +61,39 @@ pub fn common_preprocessed_input_from_json( domain, omega, k1: ORDER_R_MINUS_1_ROOT_UNITY, - ql: Polynomial::interpolate_fft(&process_vector(json_input.Ql, &FrElement::zero(), n)) - .unwrap(), - qr: Polynomial::interpolate_fft(&process_vector(json_input.Qr, &FrElement::zero(), n)) - .unwrap(), - qo: Polynomial::interpolate_fft(&process_vector(json_input.Qo, &FrElement::zero(), n)) - .unwrap(), - qm: Polynomial::interpolate_fft(&process_vector(json_input.Qm, &FrElement::zero(), n)) - .unwrap(), - qc: Polynomial::interpolate_fft(&process_vector(json_input.Qc, &FrElement::zero(), n)) - .unwrap(), - s1: Polynomial::interpolate_fft(&s1_lagrange).unwrap(), - s2: Polynomial::interpolate_fft(&s2_lagrange).unwrap(), - s3: Polynomial::interpolate_fft(&s3_lagrange).unwrap(), + ql: Polynomial::interpolate_fft::(&process_vector( + json_input.Ql, + &FrElement::zero(), + n, + )) + .unwrap(), + qr: Polynomial::interpolate_fft::(&process_vector( + json_input.Qr, + &FrElement::zero(), + n, + )) + .unwrap(), + qo: Polynomial::interpolate_fft::(&process_vector( + json_input.Qo, + &FrElement::zero(), + n, + )) + .unwrap(), + qm: Polynomial::interpolate_fft::(&process_vector( + json_input.Qm, + &FrElement::zero(), + n, + )) + .unwrap(), + qc: Polynomial::interpolate_fft::(&process_vector( + json_input.Qc, + &FrElement::zero(), + n, + )) + .unwrap(), + s1: Polynomial::interpolate_fft::(&s1_lagrange).unwrap(), + s2: Polynomial::interpolate_fft::(&s2_lagrange).unwrap(), + s3: Polynomial::interpolate_fft::(&s3_lagrange).unwrap(), s1_lagrange, s2_lagrange, s3_lagrange, diff --git a/provers/stark/src/debug.rs b/provers/stark/src/debug.rs index 1b02007a20..bede4a7a31 100644 --- a/provers/stark/src/debug.rs +++ b/provers/stark/src/debug.rs @@ -3,7 +3,6 @@ use crate::trace::TraceTable; use super::domain::Domain; use super::traits::AIR; -use lambdaworks_math::fft::polynomial::FFTPoly; use lambdaworks_math::{ field::{element::FieldElement, traits::IsFFTField}, polynomial::Polynomial, @@ -23,8 +22,7 @@ pub fn validate_trace>( let trace_columns: Vec<_> = trace_polys .iter() .map(|poly| { - poly.evaluate_fft(1, Some(domain.interpolation_domain_size)) - .unwrap() + Polynomial::evaluate_fft::(poly, 1, Some(domain.interpolation_domain_size)).unwrap() }) .collect(); @@ -34,8 +32,7 @@ pub fn validate_trace>( .get_periodic_column_polynomials() .iter() .map(|poly| { - poly.evaluate_fft(1, Some(domain.interpolation_domain_size)) - .unwrap() + Polynomial::evaluate_fft::(poly, 1, Some(domain.interpolation_domain_size)).unwrap() }) .collect(); diff --git a/provers/stark/src/fri/mod.rs b/provers/stark/src/fri/mod.rs index e562f6bd93..624e16edf9 100644 --- a/provers/stark/src/fri/mod.rs +++ b/provers/stark/src/fri/mod.rs @@ -3,7 +3,6 @@ pub mod fri_decommit; mod fri_functions; use lambdaworks_math::fft::cpu::bit_reversing::in_place_bit_reverse_permute; -use lambdaworks_math::fft::polynomial::FFTPoly; use lambdaworks_math::field::traits::IsFFTField; use lambdaworks_math::traits::Serializable; pub use lambdaworks_math::{ @@ -119,9 +118,8 @@ where F: IsFFTField, FieldElement: Serializable + Sync + Send, { - let mut evaluation = poly - .evaluate_offset_fft(1, Some(domain_size), coset_offset) - .unwrap(); // TODO: return error + let mut evaluation = + Polynomial::evaluate_offset_fft(poly, 1, Some(domain_size), coset_offset).unwrap(); // TODO: return error in_place_bit_reverse_permute(&mut evaluation); diff --git a/provers/stark/src/prover.rs b/provers/stark/src/prover.rs index bc8cd9449c..86dcaa99e8 100644 --- a/provers/stark/src/prover.rs +++ b/provers/stark/src/prover.rs @@ -3,7 +3,7 @@ use std::time::Instant; use lambdaworks_crypto::merkle_tree::proof::Proof; use lambdaworks_math::fft::cpu::bit_reversing::{in_place_bit_reverse_permute, reverse_index}; -use lambdaworks_math::fft::{errors::FFTError, polynomial::FFTPoly}; +use lambdaworks_math::fft::errors::FFTError; use lambdaworks_math::field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField; use lambdaworks_math::traits::Serializable; use lambdaworks_math::{ @@ -88,9 +88,8 @@ pub fn evaluate_polynomial_on_lde_domain( ) -> Result>, FFTError> where F: IsFFTField, - Polynomial>: FFTPoly, { - let evaluations = p.evaluate_offset_fft(blowup_factor, Some(domain_size), offset)?; + let evaluations = Polynomial::evaluate_offset_fft(p, blowup_factor, Some(domain_size), offset)?; let step = evaluations.len() / (domain_size * blowup_factor); match step { 1 => Ok(evaluations), diff --git a/provers/stark/src/trace.rs b/provers/stark/src/trace.rs index 7c8d7a333f..2a05c42ed8 100644 --- a/provers/stark/src/trace.rs +++ b/provers/stark/src/trace.rs @@ -1,6 +1,5 @@ use crate::table::{Table, TableView}; use lambdaworks_math::fft::errors::FFTError; -use lambdaworks_math::fft::polynomial::FFTPoly; use lambdaworks_math::{ field::{element::FieldElement, traits::IsFFTField}, polynomial::Polynomial, @@ -121,7 +120,7 @@ impl<'t, F: IsFFTField> TraceTable { #[cfg(not(feature = "parallel"))] let iter = columns.iter(); - iter.map(|col| Polynomial::interpolate_fft(col)) + iter.map(|col| Polynomial::interpolate_fft::(col)) .collect::>>, FFTError>>() .unwrap() } diff --git a/provers/stark/src/traits.rs b/provers/stark/src/traits.rs index 4755fc12e0..005332a0d5 100644 --- a/provers/stark/src/traits.rs +++ b/provers/stark/src/traits.rs @@ -1,6 +1,6 @@ use itertools::Itertools; use lambdaworks_math::{ - fft::{cpu::roots_of_unity::get_powers_of_primitive_root_coset, polynomial::FFTPoly}, + fft::cpu::roots_of_unity::get_powers_of_primitive_root_coset, field::{element::FieldElement, traits::IsFFTField}, polynomial::Polynomial, }; @@ -137,7 +137,7 @@ pub trait AIR { .take(self.trace_length()) .cloned() .collect(); - let poly = Polynomial::interpolate_fft(&values).unwrap(); + let poly = Polynomial::interpolate_fft::(&values).unwrap(); result.push(poly); } result From 606319702d15482877a05f1c47f98ca9161130b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Garassino?= Date: Tue, 12 Dec 2023 12:29:03 -0300 Subject: [PATCH 15/29] Miden adapter (#681) * Add winterfell adapter in other crate * Add README * Better README * Reestructure files * Remove unnecessary code * todo's at the end * Reorder code * Clippy * cargo format * Improve readme * remove from workspace dependencies * Fix * Fixes * Fix for new frame * Many changes for a proof of concept of miden compatibility * Add periodic columns and a Fibonacci example using a periodic column * Format and better code * Clippy * Cargo fmt * Adding periodic values, adding degree bound for composition polynomial * Add num_transition_exemptions to air adapter * Fix in compute transitions of adapter and add tests for fibnacci miden/cubic air * Fix bug in constraint indexing. Cargo fmt and clippy. * Benches * Use same proof options for winterfell and lambdaworks in benchmark * Update fibonacci size, add benches to readme * put winterfell and miden as optional dependencies in the math and provers lambdaworks packages * add metadata to fromcolumns * add metadata to air adapter * Transcript is not a fixed number * remove trace from air adapter public inputs * move files to examples * move winterfell field to fields dir * use winterfell fork * fix dependencies to use forked miden and winterfell * Delete provers/stark/src/examples/fibonacci_periodic_cols.rs * Update mod.rs * move tests to correct files * fmt * fix benches --------- Co-authored-by: Sergio Chouhy Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> --- Cargo.toml | 11 + math/Cargo.toml | 4 + math/src/field/fields/mod.rs | 5 + math/src/field/fields/winterfell.rs | 120 ++++++++++ math/src/field/mod.rs | 3 +- provers/stark/Cargo.toml | 2 + provers/stark/src/verifier.rs | 7 +- winterfell_adapter/Cargo.toml | 26 ++- winterfell_adapter/README.md | 13 ++ winterfell_adapter/benches/proving.rs | 118 ++++++++++ winterfell_adapter/src/adapter/air.rs | 216 +++++++----------- winterfell_adapter/src/adapter/mod.rs | 62 +++++ .../src/adapter/public_inputs.rs | 33 ++- winterfell_adapter/src/examples/cubic.rs | 120 ++++++++++ .../src/examples/fibonacci_2_terms.rs | 76 ++++-- .../src/examples/fibonacci_rap.rs | 99 ++++++-- winterfell_adapter/src/examples/miden_vm.rs | 191 ++++++++++++++++ winterfell_adapter/src/examples/mod.rs | 2 + .../src/field_element/element.rs | 13 +- winterfell_adapter/src/utils.rs | 43 ++-- 20 files changed, 961 insertions(+), 203 deletions(-) create mode 100644 math/src/field/fields/winterfell.rs create mode 100644 winterfell_adapter/benches/proving.rs create mode 100644 winterfell_adapter/src/examples/cubic.rs create mode 100644 winterfell_adapter/src/examples/miden_vm.rs diff --git a/Cargo.toml b/Cargo.toml index dbcf7cad7c..611dc73ea3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,17 @@ lambdaworks-math = { path = "./math", version = "0.3.0" } stark-platinum-prover = { path = "./provers/stark", version = "0.3.0" } cairo-platinum-prover = { path = "./provers/cairo", version = "0.3.0" } +[patch.crates-io] +winter-air = { git = "https://github.com/lambdaclass/winterfell-for-lambdaworks.git", branch = "derive-clone-v6.4"} +winter-prover = { git = "https://github.com/lambdaclass/winterfell-for-lambdaworks.git", branch = "derive-clone-v6.4"} +winter-math = { git = "https://github.com/lambdaclass/winterfell-for-lambdaworks.git", branch = "derive-clone-v6.4"} +winter-utils = { git = "https://github.com/lambdaclass/winterfell-for-lambdaworks.git", branch = "derive-clone-v6.4"} +winter-crypto = { git = "https://github.com/lambdaclass/winterfell-for-lambdaworks.git", branch = "derive-clone-v6.4"} +miden-air = { git = "https://github.com/lambdaclass/miden-vm" } +miden-core = { git = "https://github.com/lambdaclass/miden-vm" } +miden-assembly = { git = "https://github.com/lambdaclass/miden-vm" } +miden-processor = { git = "https://github.com/lambdaclass/miden-vm" } + [profile.bench] lto = true codegen-units = 1 diff --git a/math/Cargo.toml b/math/Cargo.toml index 7860acea7c..287ebd72c5 100644 --- a/math/Cargo.toml +++ b/math/Cargo.toml @@ -12,6 +12,8 @@ thiserror = { version = "1.0", optional = true } serde = { version = "1.0", features = ["derive"], optional = true } serde_json = { version = "1.0", optional = true } proptest = { version = "1.1.0", optional = true } +winter-math = { package = "winter-math", version = "0.6.4", default-features = false, optional = true } +miden-core = { package = "miden-core" , version = "0.7", default-features = false, optional = true } # rayon rayon = { version = "1.7", optional = true } @@ -41,6 +43,7 @@ std = ["dep:thiserror"] lambdaworks-serde-binary = ["dep:serde", "std"] lambdaworks-serde-string = ["dep:serde", "dep:serde_json", "std"] proptest = ["dep:proptest"] +winter_compatibility = ["winter-math", "miden-core"] # gpu metal = [ @@ -88,3 +91,4 @@ harness = false name = "criterion_metal" harness = false required-features = ["metal"] + diff --git a/math/src/field/fields/mod.rs b/math/src/field/fields/mod.rs index 0bcbd28688..d9d923d6c2 100644 --- a/math/src/field/fields/mod.rs +++ b/math/src/field/fields/mod.rs @@ -11,5 +11,10 @@ pub mod pallas_field; pub mod u64_goldilocks_field; /// Implementation of prime fields over 64 bit unsigned integers. pub mod u64_prime_field; + +/// Winterfell and miden field compatibility +#[cfg(feature = "winter_compatibility")] +pub mod winterfell; + /// Implemenation of Vesta Prime field (p = 2^254 + 45560315531506369815346746415080538113) mod vesta_field; diff --git a/math/src/field/fields/winterfell.rs b/math/src/field/fields/winterfell.rs new file mode 100644 index 0000000000..8d06f7347b --- /dev/null +++ b/math/src/field/fields/winterfell.rs @@ -0,0 +1,120 @@ +use crate::{ + errors::ByteConversionError, + field::{ + element::FieldElement, + errors::FieldError, + traits::{IsFFTField, IsField, IsPrimeField}, + }, + traits::{ByteConversion, Serializable}, + unsigned_integer::element::U256, +}; +pub use miden_core::Felt; +pub use winter_math::fields::f128::BaseElement; +use winter_math::{FieldElement as IsWinterfellFieldElement, StarkField}; + +impl IsFFTField for Felt { + const TWO_ADICITY: u64 = ::TWO_ADICITY as u64; + const TWO_ADIC_PRIMITVE_ROOT_OF_UNITY: Self::BaseType = Felt::TWO_ADIC_ROOT_OF_UNITY; +} + +impl IsPrimeField for Felt { + type RepresentativeType = U256; + + fn representative(_a: &Self::BaseType) -> Self::RepresentativeType { + todo!() + } + + fn from_hex(_hex_string: &str) -> Result { + todo!() + } + + fn field_bit_size() -> usize { + 128 // TODO + } +} + +impl IsField for Felt { + type BaseType = Felt; + + fn add(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + *a + *b + } + + fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + *a * *b + } + + fn sub(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + *a - *b + } + + fn neg(a: &Self::BaseType) -> Self::BaseType { + -*a + } + + fn inv(a: &Self::BaseType) -> Result { + Ok((*a).inv()) + } + + fn div(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + *a / *b + } + + fn eq(a: &Self::BaseType, b: &Self::BaseType) -> bool { + *a == *b + } + + fn zero() -> Self::BaseType { + Self::BaseType::ZERO + } + + fn one() -> Self::BaseType { + Self::BaseType::ONE + } + + fn from_u64(x: u64) -> Self::BaseType { + Self::BaseType::from(x) + } + + fn from_base_type(x: Self::BaseType) -> Self::BaseType { + x + } +} + +impl Serializable for FieldElement { + fn serialize(&self) -> Vec { + Felt::elements_as_bytes(&[*self.value()]).to_vec() + } +} + +impl ByteConversion for Felt { + fn to_bytes_be(&self) -> Vec { + Felt::elements_as_bytes(&[*self]).to_vec() + } + + fn to_bytes_le(&self) -> Vec { + Felt::elements_as_bytes(&[*self]).to_vec() + } + + fn from_bytes_be(bytes: &[u8]) -> Result + where + Self: Sized, + { + unsafe { + let res = Felt::bytes_as_elements(bytes) + .map_err(|_| ByteConversionError::FromBEBytesError)?; + Ok(res[0]) + } + } + + fn from_bytes_le(bytes: &[u8]) -> Result + where + Self: Sized, + { + unsafe { + let res = Felt::bytes_as_elements(bytes) + .map_err(|_| ByteConversionError::FromBEBytesError)?; + Ok(res[0]) + } + } +} diff --git a/math/src/field/mod.rs b/math/src/field/mod.rs index 2fe6c42725..5cb5f2506d 100644 --- a/math/src/field/mod.rs +++ b/math/src/field/mod.rs @@ -1,5 +1,6 @@ /// Implementation of FieldElement, a generic element of a field. pub mod element; +pub mod errors; /// Implementation of quadratic extensions of fields. pub mod extensions; /// Implementation of particular cases of fields. @@ -8,5 +9,3 @@ pub mod fields; pub mod test_fields; /// Common behaviour for field elements. pub mod traits; - -pub mod errors; diff --git a/provers/stark/Cargo.toml b/provers/stark/Cargo.toml index aa1f5c6832..033e1a9c9a 100644 --- a/provers/stark/Cargo.toml +++ b/provers/stark/Cargo.toml @@ -11,6 +11,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] lambdaworks-math = { workspace = true , features = ["lambdaworks-serde-binary"] } lambdaworks-crypto.workspace = true +miden-core = { git="https://github.com/lambdaclass/miden-vm", optional=true} rand = "0.8.5" thiserror = "1.0.38" @@ -47,6 +48,7 @@ instruments = [] # This enables timing prints in prover and ve metal = ["lambdaworks-math/metal"] parallel = ["dep:rayon", "lambdaworks-crypto/parallel"] wasm = ["dep:wasm-bindgen", "dep:serde-wasm-bindgen", "dep:web-sys"] +winter_compatibility = ["miden-core"] [target.'cfg(not(all(target_arch = "wasm32", target_os = "unknown")))'.dev-dependencies] proptest = "1.2.0" diff --git a/provers/stark/src/verifier.rs b/provers/stark/src/verifier.rs index c498578d01..bf2ec9c85c 100644 --- a/provers/stark/src/verifier.rs +++ b/provers/stark/src/verifier.rs @@ -6,6 +6,9 @@ use lambdaworks_crypto::merkle_tree::proof::Proof; #[cfg(not(feature = "test_fiat_shamir"))] use log::error; +use crate::{ + config::Commitment, proof::stark::DeepPolynomialOpening, transcript::IsStarkTranscript, +}; use lambdaworks_math::{ fft::cpu::bit_reversing::reverse_index, field::{ @@ -15,10 +18,6 @@ use lambdaworks_math::{ traits::Serializable, }; -use crate::{ - config::Commitment, proof::stark::DeepPolynomialOpening, transcript::IsStarkTranscript, -}; - use super::{ config::BatchedMerkleTreeBackend, domain::Domain, diff --git a/winterfell_adapter/Cargo.toml b/winterfell_adapter/Cargo.toml index 18d40a14a4..0dcf68df38 100644 --- a/winterfell_adapter/Cargo.toml +++ b/winterfell_adapter/Cargo.toml @@ -5,8 +5,26 @@ edition.workspace = true license.workspace = true [dependencies] -lambdaworks-math = { path = "../math" } -stark-platinum-prover = { path = "../provers/stark" } -winterfell = { git = "https://github.com/facebook/winterfell" } -winter-utils = { git = "https://github.com/facebook/winterfell" } +lambdaworks-math = { path = "../math", features=["winter_compatibility"] } +stark-platinum-prover = { path = "../provers/stark" , features=["winter_compatibility"]} rand = "0.8.5" +winter-air = { package = "winter-air", version = "0.6.4", default-features = false } +winter-prover = { package = "winter-prover", version = "0.6.4", default-features = false } +winter-math = { package = "winter-math", version = "0.6.4", default-features = false } +winter-utils = { package = "winter-utils", version = "0.6.4", default-features = false } +miden-air = { package = "miden-air", version = "0.7", default-features = false } +miden-core = { package = "miden-core" , version = "0.7", default-features = false } +miden-assembly = { package = "miden-assembly", version = "0.7", default-features = false } +miden-processor = { package = "miden-processor", version = "0.7", default-features = false } +sha3 = "0.10" + + +[dev-dependencies] +criterion = { version = "0.4", default-features = false } +miden-prover = { package = "miden-prover", version = "0.7", default-features = false } + + +[[bench]] +name = "proving" +harness = false + diff --git a/winterfell_adapter/README.md b/winterfell_adapter/README.md index 83177559a3..03e6e11f6c 100644 --- a/winterfell_adapter/README.md +++ b/winterfell_adapter/README.md @@ -51,3 +51,16 @@ let proof = Prover::prove::>>( ``` Here `TraceTable` is the Winterfell type that represents your trace table. To check more examples you can see the `examples` folder inside this crate. + +# Benchmarks +To run the fibonacci Miden benchmark run: + +```rust +cargo bench +``` + +To run it with parallelization run: + +```rust +cargo bench --features stark-platinum-prover/parallel,winter-prover/concurrent +``` diff --git a/winterfell_adapter/benches/proving.rs b/winterfell_adapter/benches/proving.rs new file mode 100644 index 0000000000..6a828d6712 --- /dev/null +++ b/winterfell_adapter/benches/proving.rs @@ -0,0 +1,118 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use miden_air::{HashFunction, ProcessorAir, ProvingOptions, PublicInputs}; +use miden_assembly::Assembler; +use miden_core::{Felt, Program, StackInputs}; +use miden_processor::DefaultHost; +use miden_processor::{self as processor}; +use miden_prover::prove; +use processor::ExecutionTrace; +use stark_platinum_prover::{proof::options::ProofOptions, prover::IsStarkProver}; +use winter_air::FieldExtension; +use winter_prover::Trace; +use winterfell_adapter::adapter::public_inputs::AirAdapterPublicInputs; +use winterfell_adapter::adapter::{air::AirAdapter, Prover, Transcript}; +use winterfell_adapter::examples::miden_vm::ExecutionTraceMetadata; + +struct BenchInstance { + program: Program, + stack_inputs: StackInputs, + lambda_proof_options: ProofOptions, +} + +fn create_bench_instance(fibonacci_number: usize) -> BenchInstance { + let program = format!( + "begin + repeat.{} + swap dup.1 add + end + end", + fibonacci_number - 1 + ); + let program = Assembler::default().compile(program).unwrap(); + let stack_inputs = StackInputs::try_from_values([0, 1]).unwrap(); + let mut lambda_proof_options = ProofOptions::default_test_options(); + lambda_proof_options.blowup_factor = 8; + + BenchInstance { + program, + stack_inputs, + lambda_proof_options, + } +} + +pub fn bench_prove_miden_fibonacci(c: &mut Criterion) { + let instance = create_bench_instance(100); + + c.bench_function("winterfell_prover", |b| { + b.iter(|| { + let proving_options = ProvingOptions::new( + instance.lambda_proof_options.fri_number_of_queries, + instance.lambda_proof_options.blowup_factor as usize, + instance.lambda_proof_options.grinding_factor as u32, + FieldExtension::None, + 2, + 0, + HashFunction::Blake3_192, + ); + + let (_outputs, _proof) = black_box( + prove( + &instance.program, + instance.stack_inputs.clone(), + DefaultHost::default(), + proving_options, + ) + .unwrap(), + ); + }) + }); + + c.bench_function("lambda_prover", |b| { + b.iter(|| { + // This is here because the only pub method in miden + // is a prove function that executes AND proves. + // This makes the benchmark a more fair + // in the case that the program execution takes + // too long. + let winter_trace = processor::execute( + &instance.program, + instance.stack_inputs.clone(), + DefaultHost::default(), + *ProvingOptions::default().execution_options(), + ) + .unwrap(); + + let program_info = winter_trace.program_info().clone(); + let stack_outputs = winter_trace.stack_outputs().clone(); + let pub_inputs = AirAdapterPublicInputs::::new( + PublicInputs::new( + program_info, + instance.stack_inputs.clone(), + stack_outputs.clone(), + ), + vec![2; 182], + vec![0, 1], + winter_trace.get_info(), + winter_trace.clone().into(), + ); + + let trace = + AirAdapter::::convert_winterfell_trace_table( + winter_trace.main_segment().clone(), + ); + + let _proof = black_box( + Prover::prove::>( + &trace, + &pub_inputs, + &instance.lambda_proof_options, + Transcript::new(&[]), + ) + .unwrap(), + ); + }) + }); +} + +criterion_group!(benches, bench_prove_miden_fibonacci); +criterion_main!(benches); diff --git a/winterfell_adapter/src/adapter/air.rs b/winterfell_adapter/src/adapter/air.rs index 9ca8014fec..1aaecc3a98 100644 --- a/winterfell_adapter/src/adapter/air.rs +++ b/winterfell_adapter/src/adapter/air.rs @@ -1,69 +1,87 @@ -use crate::field_element::element::AdapterFieldElement; -use crate::utils::{matrix_adapter2field, matrix_field2adapter, vec_field2adapter}; -use lambdaworks_math::field::{ - element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, +use crate::utils::{ + matrix_lambda2winter, matrix_winter2lambda, vec_lambda2winter, vec_winter2lambda, }; +use lambdaworks_math::field::element::FieldElement; +use lambdaworks_math::field::traits::{IsFFTField, IsField}; +use lambdaworks_math::traits::ByteConversion; +use miden_core::Felt; use stark_platinum_prover::{ constraints::boundary::{BoundaryConstraint, BoundaryConstraints}, traits::AIR, }; use std::marker::PhantomData; -use winterfell::{ - Air, AuxTraceRandElements, EvaluationFrame, FieldExtension, ProofOptions, Trace, TraceTable, -}; +use winter_air::{Air, AuxTraceRandElements, EvaluationFrame, FieldExtension, ProofOptions}; +use winter_math::{FieldElement as IsWinterfellFieldElement, StarkField}; +use winter_prover::{ColMatrix, Trace, TraceTable}; use super::public_inputs::AirAdapterPublicInputs; -pub trait FromColumns { - fn from_cols(columns: Vec>) -> Self; +pub trait FromColumns { + fn from_cols(columns: Vec>, metadata: &M) -> Self; } -impl FromColumns for TraceTable { - fn from_cols(columns: Vec>) -> Self { +impl FromColumns for TraceTable { + fn from_cols(columns: Vec>, _: &()) -> Self { TraceTable::init(columns) } } #[derive(Clone)] -pub struct AirAdapter +pub struct AirAdapter where - A: Air, + FE: IsWinterfellFieldElement + StarkField + ByteConversion + Unpin + IsFFTField, + A: Air, A::PublicInputs: Clone, - T: Trace + Clone + FromColumns, + T: Trace + Clone + FromColumns, + M: Clone, { winterfell_air: A, - public_inputs: AirAdapterPublicInputs, + public_inputs: AirAdapterPublicInputs, air_context: stark_platinum_prover::context::AirContext, phantom: PhantomData, } -impl AirAdapter +impl AirAdapter where - A: Air + Clone, + FE: IsWinterfellFieldElement + + StarkField + + ByteConversion + + Unpin + + IsFFTField + + IsField, + A: Air + Clone, A::PublicInputs: Clone, - T: Trace + Clone + FromColumns, + T: Trace + Clone + FromColumns, + M: Clone, { pub fn convert_winterfell_trace_table( - trace: TraceTable, - ) -> stark_platinum_prover::trace::TraceTable { + trace: ColMatrix, + ) -> stark_platinum_prover::trace::TraceTable { let mut columns = Vec::new(); - for i in 0..trace.width() { + for i in 0..trace.num_cols() { columns.push(trace.get_column(i).to_owned()); } - stark_platinum_prover::trace::TraceTable::from_columns(matrix_adapter2field(&columns), 1) + stark_platinum_prover::trace::TraceTable::from_columns(matrix_winter2lambda(&columns), 1) } } -impl AIR for AirAdapter +impl AIR for AirAdapter where - A: Air + Clone, + FE: IsWinterfellFieldElement + + StarkField + + ByteConversion + + Unpin + + IsFFTField + + IsField, + A: Air + Clone, A::PublicInputs: Clone, - T: Trace + Clone + FromColumns, + T: Trace + Clone + FromColumns, + M: Clone, { - type Field = Stark252PrimeField; - type RAPChallenges = Vec; - type PublicInputs = AirAdapterPublicInputs; + type Field = FE; + type RAPChallenges = Vec; + type PublicInputs = AirAdapterPublicInputs; const STEP_SIZE: usize = 1; fn new( @@ -109,19 +127,22 @@ where rap_challenges: &Self::RAPChallenges, ) -> stark_platinum_prover::trace::TraceTable { // We support at most a one-stage RAP. This covers most use cases. - if let Some(winter_trace) = T::from_cols(matrix_field2adapter(&main_trace.columns())) - .build_aux_segment(&[], rap_challenges) + if let Some(winter_trace) = T::from_cols( + matrix_lambda2winter(&main_trace.columns()), + &self.pub_inputs().metadata, + ) + .build_aux_segment(&[], rap_challenges) { let mut columns = Vec::new(); for i in 0..winter_trace.num_cols() { columns.push(winter_trace.get_column(i).to_owned()); } stark_platinum_prover::trace::TraceTable::from_columns( - matrix_adapter2field(&columns), + matrix_winter2lambda(&columns), 1, ) } else { - stark_platinum_prover::trace::TraceTable::empty() + stark_platinum_prover::trace::TraceTable::::empty() } } @@ -137,7 +158,7 @@ where for _ in 0..trace_layout.get_aux_segment_rand_elements(0) { result.push(transcript.sample_field_element()); } - vec_field2adapter(&result) + vec_lambda2winter(&result) } else if num_segments == 0 { Vec::new() } else { @@ -150,13 +171,16 @@ where } fn composition_poly_degree_bound(&self) -> usize { - self.public_inputs.composition_poly_degree_bound + self.winterfell_air + .context() + .num_constraint_composition_columns() + * self.trace_length() } fn compute_transition( &self, frame: &stark_platinum_prover::frame::Frame, - _periodic_values: &[FieldElement], + periodic_values: &[FieldElement], rap_challenges: &Self::RAPChallenges, ) -> Vec> { let num_aux_columns = self.number_auxiliary_rap_columns(); @@ -166,22 +190,27 @@ where let second_step = frame.get_evaluation_step(1); let main_frame = EvaluationFrame::from_rows( - vec_field2adapter(&first_step.get_row(0)[..num_main_columns]), - vec_field2adapter(&second_step.get_row(0)[..num_main_columns]), + vec_lambda2winter(&first_step.get_row(0)[..num_main_columns]), + vec_lambda2winter(&second_step.get_row(0)[..num_main_columns]), ); + let periodic_values = vec_lambda2winter(periodic_values); + let mut main_result = vec![ FieldElement::zero(); self.winterfell_air .context() .num_main_transition_constraints() ]; - self.winterfell_air - .evaluate_transition::( - &main_frame, - &[], - &mut vec_field2adapter(&main_result), - ); // Periodic values not supported + + let mut main_result_winter = vec_lambda2winter(&main_result); + self.winterfell_air.evaluate_transition::( + &main_frame, + &periodic_values, + &mut main_result_winter, + ); // Periodic values not supported + + main_result = vec_winter2lambda(&main_result_winter); if self.winterfell_air.trace_layout().num_aux_segments() == 1 { let mut rand_elements = AuxTraceRandElements::new(); @@ -191,24 +220,25 @@ where let second_step = frame.get_evaluation_step(1); let aux_frame = EvaluationFrame::from_rows( - vec_field2adapter(&first_step.get_row(0)[num_main_columns..]), - vec_field2adapter(&second_step.get_row(0)[num_main_columns..]), + vec_lambda2winter(&first_step.get_row(0)[num_main_columns..]), + vec_lambda2winter(&second_step.get_row(0)[num_main_columns..]), ); - let aux_result = vec![ + let mut aux_result = vec![ FieldElement::zero(); self.winterfell_air .context() .num_aux_transition_constraints() ]; + let mut winter_aux_result = vec_lambda2winter(&aux_result); self.winterfell_air.evaluate_aux_transition( &main_frame, &aux_frame, - &[], + &periodic_values, &rand_elements, - &mut vec_field2adapter(&aux_result), + &mut winter_aux_result, ); - + aux_result = vec_winter2lambda(&winter_aux_result); main_result.extend_from_slice(&aux_result); } main_result @@ -217,14 +247,17 @@ where fn boundary_constraints( &self, rap_challenges: &Self::RAPChallenges, - ) -> stark_platinum_prover::constraints::boundary::BoundaryConstraints { + ) -> stark_platinum_prover::constraints::boundary::BoundaryConstraints { + let num_aux_columns = self.number_auxiliary_rap_columns(); + let num_main_columns = self.context().trace_columns - num_aux_columns; + let mut result = Vec::new(); for assertion in self.winterfell_air.get_assertions() { assert!(assertion.is_single()); result.push(BoundaryConstraint::new( assertion.column(), assertion.first_step(), - assertion.values()[0].0, + FieldElement::::const_from_raw(assertion.values()[0]), )); } @@ -234,9 +267,9 @@ where for assertion in self.winterfell_air.get_aux_assertions(&rand_elements) { assert!(assertion.is_single()); result.push(BoundaryConstraint::new( - assertion.column(), + assertion.column() + num_main_columns, assertion.first_step(), - assertion.values()[0].0, + FieldElement::::const_from_raw(assertion.values()[0]), )); } @@ -254,81 +287,8 @@ where fn pub_inputs(&self) -> &Self::PublicInputs { &self.public_inputs } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::examples::fibonacci_2_terms::{self, FibAir2Terms}; - use crate::examples::fibonacci_rap::{self, FibonacciRAP, RapTraceTable}; - use stark_platinum_prover::{ - proof::options::ProofOptions, - prover::{IsStarkProver, Prover}, - transcript::StoneProverTranscript, - verifier::{IsStarkVerifier, Verifier}, - }; - use winterfell::{TraceInfo, TraceLayout}; - - #[test] - fn prove_and_verify_a_winterfell_fibonacci_2_terms_air() { - let lambda_proof_options = ProofOptions::default_test_options(); - let trace = AirAdapter::>::convert_winterfell_trace_table( - fibonacci_2_terms::build_trace(16), - ); - let pub_inputs = AirAdapterPublicInputs { - winterfell_public_inputs: AdapterFieldElement(trace.columns()[1][7]), - transition_exemptions: vec![1, 1], - transition_offsets: vec![0, 1], - composition_poly_degree_bound: 8, - trace_info: TraceInfo::new(2, 8), - }; - - let proof = Prover::prove::>>( - &trace, - &pub_inputs, - &lambda_proof_options, - StoneProverTranscript::new(&[]), - ) - .unwrap(); - assert!(Verifier::verify::>>( - &proof, - &pub_inputs, - &lambda_proof_options, - StoneProverTranscript::new(&[]), - )); - } - #[test] - fn prove_and_verify_a_winterfell_fibonacci_rap_air() { - let lambda_proof_options = ProofOptions::default_test_options(); - let trace = AirAdapter::>::convert_winterfell_trace_table( - fibonacci_rap::build_trace(16), - ); - let trace_layout = TraceLayout::new(3, [1], [1]); - let trace_info = TraceInfo::new_multi_segment(trace_layout, 16, vec![]); - let fibonacci_result = trace.columns()[1][15]; - let pub_inputs = AirAdapterPublicInputs { - winterfell_public_inputs: AdapterFieldElement(fibonacci_result), - transition_exemptions: vec![1, 1, 1], - transition_offsets: vec![0, 1], - composition_poly_degree_bound: 32, - trace_info, - }; - - let proof = Prover::prove::>>( - &trace, - &pub_inputs, - &lambda_proof_options, - StoneProverTranscript::new(&[]), - ) - .unwrap(); - assert!( - Verifier::verify::>>( - &proof, - &pub_inputs, - &lambda_proof_options, - StoneProverTranscript::new(&[]), - ) - ); + fn get_periodic_column_values(&self) -> Vec>> { + matrix_winter2lambda(&self.winterfell_air.get_periodic_column_values()) } } diff --git a/winterfell_adapter/src/adapter/mod.rs b/winterfell_adapter/src/adapter/mod.rs index 3987d97069..5975ea734d 100644 --- a/winterfell_adapter/src/adapter/mod.rs +++ b/winterfell_adapter/src/adapter/mod.rs @@ -1,2 +1,64 @@ +use lambdaworks_math::traits::ByteConversion; +use miden_core::Felt; +use sha3::{Digest, Keccak256}; +use stark_platinum_prover::{ + fri::FieldElement, prover::IsStarkProver, transcript::IsStarkTranscript, + verifier::IsStarkVerifier, +}; +use winter_math::StarkField; + pub mod air; pub mod public_inputs; + +pub struct Prover; +impl IsStarkProver for Prover { + type Field = Felt; +} + +pub struct Verifier {} +impl IsStarkVerifier for Verifier { + type Field = Felt; +} + +pub struct Transcript { + hasher: Keccak256, +} + +impl Transcript { + pub fn new(data: &[u8]) -> Self { + let mut res = Self { + hasher: Keccak256::new(), + }; + res.append_bytes(data); + res + } +} + +impl IsStarkTranscript for Transcript { + fn append_field_element(&mut self, element: &FieldElement) { + self.append_bytes(&element.value().to_bytes_be()); + } + + fn append_bytes(&mut self, new_bytes: &[u8]) { + self.hasher.update(&mut new_bytes.to_owned()); + } + + fn state(&self) -> [u8; 32] { + self.hasher.clone().finalize().into() + } + + fn sample_field_element(&mut self) -> FieldElement { + let mut bytes = self.state()[..8].try_into().unwrap(); + let mut x = u64::from_be_bytes(bytes); + while x >= Felt::MODULUS { + self.append_bytes(&bytes); + bytes = self.state()[..8].try_into().unwrap(); + x = u64::from_be_bytes(bytes); + } + FieldElement::const_from_raw(Felt::new(x)) + } + + fn sample_u64(&mut self, upper_bound: u64) -> u64 { + u64::from_be_bytes(self.state()[..8].try_into().unwrap()) % upper_bound + } +} diff --git a/winterfell_adapter/src/adapter/public_inputs.rs b/winterfell_adapter/src/adapter/public_inputs.rs index fc463ebb53..fc5e49ba9f 100644 --- a/winterfell_adapter/src/adapter/public_inputs.rs +++ b/winterfell_adapter/src/adapter/public_inputs.rs @@ -1,15 +1,38 @@ -use crate::field_element::element::AdapterFieldElement; -use winterfell::{Air, TraceInfo}; +use winter_air::{Air, TraceInfo}; #[derive(Clone)] -pub struct AirAdapterPublicInputs +pub struct AirAdapterPublicInputs where - A: Air, + A: Air, A::PublicInputs: Clone, + M: Clone, { pub(crate) winterfell_public_inputs: A::PublicInputs, pub(crate) transition_exemptions: Vec, pub(crate) transition_offsets: Vec, pub(crate) trace_info: TraceInfo, - pub(crate) composition_poly_degree_bound: usize, + pub(crate) metadata: M, +} + +impl AirAdapterPublicInputs +where + A: Air, + A::PublicInputs: Clone, + M: Clone, +{ + pub fn new( + winterfell_public_inputs: A::PublicInputs, + transition_exemptions: Vec, + transition_offsets: Vec, + trace_info: TraceInfo, + metadata: M, + ) -> Self { + Self { + winterfell_public_inputs, + transition_exemptions, + transition_offsets, + trace_info, + metadata, + } + } } diff --git a/winterfell_adapter/src/examples/cubic.rs b/winterfell_adapter/src/examples/cubic.rs new file mode 100644 index 0000000000..ea8320c507 --- /dev/null +++ b/winterfell_adapter/src/examples/cubic.rs @@ -0,0 +1,120 @@ +use miden_core::Felt; +use winter_air::{ + Air, AirContext, Assertion, EvaluationFrame, ProofOptions, TraceInfo, + TransitionConstraintDegree, +}; +use winter_math::FieldElement as IsWinterfellFieldElement; +use winter_prover::TraceTable; + +/// A fibonacci winterfell AIR example. Two terms are computed +/// at each step. This was taken from the original winterfell +/// repository and adapted to work with lambdaworks. +#[derive(Clone)] +pub struct Cubic { + context: AirContext, + result: Felt, +} + +impl Air for Cubic { + type BaseField = Felt; + type PublicInputs = Felt; + + fn new(trace_info: TraceInfo, pub_inputs: Self::BaseField, options: ProofOptions) -> Self { + let degrees = vec![TransitionConstraintDegree::new(3)]; + Cubic { + context: AirContext::new(trace_info, degrees, 2, options), + result: pub_inputs, + } + } + + fn context(&self) -> &AirContext { + &self.context + } + + fn evaluate_transition>( + &self, + frame: &EvaluationFrame, + _periodic_values: &[E], + result: &mut [E], + ) { + let current = frame.current(); + let next = frame.next(); + + // s_i = (s_{i-1})Β³ + result[0] = next[0] - (current[0] * current[0] * current[0]); + } + + fn get_assertions(&self) -> Vec> { + // A valid Fibonacci sequence should start with two ones and terminate with + // the expected result + let last_step = self.trace_length() - 1; + vec![ + Assertion::single(0, 0, Self::BaseField::from(2u16)), + Assertion::single(0, last_step, self.result), + ] + } +} + +pub fn build_trace(sequence_length: usize) -> TraceTable { + assert!( + sequence_length.is_power_of_two(), + "sequence length must be a power of 2" + ); + + let mut accum = Felt::from(2u16); + let mut column = vec![accum]; + while column.len() < sequence_length { + accum = accum * accum * accum; + column.push(accum); + } + TraceTable::init(vec![column]) +} + +#[cfg(test)] +mod tests { + use miden_core::Felt; + use stark_platinum_prover::{ + proof::options::ProofOptions, prover::IsStarkProver, verifier::IsStarkVerifier, + }; + use winter_air::TraceInfo; + use winter_prover::{Trace, TraceTable}; + + use crate::{ + adapter::{ + air::AirAdapter, public_inputs::AirAdapterPublicInputs, Prover, Transcript, Verifier, + }, + examples::cubic::{self, Cubic}, + }; + + #[test] + fn prove_and_verify_a_winterfell_cubic_air() { + let lambda_proof_options = ProofOptions::default_test_options(); + let winter_trace = cubic::build_trace(16); + let trace = AirAdapter::, Felt, ()>::convert_winterfell_trace_table( + winter_trace.main_segment().clone(), + ); + let pub_inputs = AirAdapterPublicInputs { + winterfell_public_inputs: *trace.columns()[0][15].value(), + transition_exemptions: vec![1], + transition_offsets: vec![0, 1], + trace_info: TraceInfo::new(1, 16), + metadata: (), + }; + + let proof = Prover::prove::, Felt, _>>( + &trace, + &pub_inputs, + &lambda_proof_options, + Transcript::new(&[]), + ) + .unwrap(); + assert!( + Verifier::verify::, Felt, _>>( + &proof, + &pub_inputs, + &lambda_proof_options, + Transcript::new(&[]), + ) + ); + } +} diff --git a/winterfell_adapter/src/examples/fibonacci_2_terms.rs b/winterfell_adapter/src/examples/fibonacci_2_terms.rs index c86296ee22..babd4f9de3 100644 --- a/winterfell_adapter/src/examples/fibonacci_2_terms.rs +++ b/winterfell_adapter/src/examples/fibonacci_2_terms.rs @@ -1,24 +1,23 @@ -use lambdaworks_math::field::element::FieldElement; -use winterfell::math::FieldElement as IsWinterfellFieldElement; -use winterfell::{ - Air, AirContext, Assertion, EvaluationFrame, ProofOptions, TraceInfo, TraceTable, +use miden_core::Felt; +use winter_air::{ + Air, AirContext, Assertion, EvaluationFrame, ProofOptions, TraceInfo, TransitionConstraintDegree, }; - -use crate::field_element::element::AdapterFieldElement; +use winter_math::FieldElement as IsWinterfellFieldElement; +use winter_prover::TraceTable; /// A fibonacci winterfell AIR example. Two terms are computed /// at each step. This was taken from the original winterfell /// repository and adapted to work with lambdaworks. #[derive(Clone)] pub struct FibAir2Terms { - context: AirContext, - result: AdapterFieldElement, + context: AirContext, + result: Felt, } impl Air for FibAir2Terms { - type BaseField = AdapterFieldElement; - type PublicInputs = AdapterFieldElement; + type BaseField = Felt; + type PublicInputs = Felt; fn new(trace_info: TraceInfo, pub_inputs: Self::BaseField, options: ProofOptions) -> Self { let degrees = vec![ @@ -63,7 +62,7 @@ impl Air for FibAir2Terms { } } -pub fn build_trace(sequence_length: usize) -> TraceTable { +pub fn build_trace(sequence_length: usize) -> TraceTable { assert!( sequence_length.is_power_of_two(), "sequence length must be a power of 2" @@ -72,8 +71,8 @@ pub fn build_trace(sequence_length: usize) -> TraceTable { let mut trace = TraceTable::new(2, sequence_length / 2); trace.fill( |state| { - state[0] = AdapterFieldElement(FieldElement::one()); - state[1] = AdapterFieldElement(FieldElement::one()); + state[0] = Felt::ONE; + state[1] = Felt::ONE; }, |_, state| { state[0] += state[1]; @@ -83,3 +82,54 @@ pub fn build_trace(sequence_length: usize) -> TraceTable { trace } + +#[cfg(test)] +mod tests { + use miden_core::Felt; + use stark_platinum_prover::{ + proof::options::ProofOptions, prover::IsStarkProver, verifier::IsStarkVerifier, + }; + use winter_air::TraceInfo; + use winter_prover::{Trace, TraceTable}; + + use crate::{ + adapter::{ + air::AirAdapter, public_inputs::AirAdapterPublicInputs, Prover, Transcript, Verifier, + }, + examples::fibonacci_2_terms::{self, FibAir2Terms}, + }; + + #[test] + fn prove_and_verify_a_winterfell_fibonacci_2_terms_air() { + let lambda_proof_options = ProofOptions::default_test_options(); + let winter_trace = fibonacci_2_terms::build_trace(16); + let trace = + AirAdapter::, Felt, ()>::convert_winterfell_trace_table( + winter_trace.main_segment().clone(), + ); + let pub_inputs = AirAdapterPublicInputs { + winterfell_public_inputs: *trace.columns()[1][7].value(), + transition_exemptions: vec![1, 1], + transition_offsets: vec![0, 1], + trace_info: TraceInfo::new(2, 8), + metadata: (), + }; + + let proof = Prover::prove::, Felt, _>>( + &trace, + &pub_inputs, + &lambda_proof_options, + Transcript::new(&[]), + ) + .unwrap(); + + assert!(Verifier::verify::< + AirAdapter, Felt, _>, + >( + &proof, + &pub_inputs, + &lambda_proof_options, + Transcript::new(&[]), + )); + } +} diff --git a/winterfell_adapter/src/examples/fibonacci_rap.rs b/winterfell_adapter/src/examples/fibonacci_rap.rs index 3b9155e6d2..f46e4e94ea 100644 --- a/winterfell_adapter/src/examples/fibonacci_rap.rs +++ b/winterfell_adapter/src/examples/fibonacci_rap.rs @@ -1,16 +1,14 @@ use crate::adapter::air::FromColumns; -use crate::field_element::element::AdapterFieldElement; -use crate::utils::vec_field2adapter; -use lambdaworks_math::field::element::FieldElement; +use miden_core::Felt; use rand::seq::SliceRandom; use rand::thread_rng; -use winter_utils::{collections::Vec, uninit_vector}; -use winterfell::math::FieldElement as IsWinterfellFieldElement; -use winterfell::{math::StarkField, matrix::ColMatrix, Trace, TraceLayout}; -use winterfell::{ - Air, AirContext, Assertion, EvaluationFrame, ProofOptions, TraceInfo, TraceTable, - TransitionConstraintDegree, +use winter_air::{ + Air, AirContext, Assertion, AuxTraceRandElements, EvaluationFrame, ProofOptions, TraceInfo, + TraceLayout, TransitionConstraintDegree, }; +use winter_math::{ExtensionOf, FieldElement as IsWinterfellFieldElement, StarkField}; +use winter_prover::{ColMatrix, Trace, TraceTable}; +use winter_utils::{collections::Vec, uninit_vector}; #[derive(Clone)] pub struct RapTraceTable { @@ -132,21 +130,21 @@ impl Trace for RapTraceTable { } } -impl FromColumns for RapTraceTable { - fn from_cols(columns: Vec>) -> Self { +impl FromColumns for RapTraceTable { + fn from_cols(columns: Vec>, _: &()) -> Self { RapTraceTable::init(columns) } } #[derive(Clone)] pub struct FibonacciRAP { - context: AirContext, - result: AdapterFieldElement, + context: AirContext, + result: Felt, } impl Air for FibonacciRAP { - type BaseField = AdapterFieldElement; - type PublicInputs = AdapterFieldElement; + type BaseField = Felt; + type PublicInputs = Felt; fn new(trace_info: TraceInfo, pub_inputs: Self::BaseField, options: ProofOptions) -> Self { let degrees = vec![ @@ -183,11 +181,11 @@ impl Air for FibonacciRAP { main_frame: &EvaluationFrame, aux_frame: &EvaluationFrame, _periodic_values: &[F], - aux_rand_elements: &winterfell::AuxTraceRandElements, + aux_rand_elements: &AuxTraceRandElements, result: &mut [E], ) where F: IsWinterfellFieldElement, - E: IsWinterfellFieldElement + winterfell::math::ExtensionOf, + E: IsWinterfellFieldElement + ExtensionOf, { let gamma = aux_rand_elements.get_segment_elements(0)[0]; let curr_aux = aux_frame.current(); @@ -211,20 +209,20 @@ impl Air for FibonacciRAP { fn get_aux_assertions>( &self, - _aux_rand_elements: &winterfell::AuxTraceRandElements, + _aux_rand_elements: &AuxTraceRandElements, ) -> Vec> { let last_step = self.trace_length() - 1; - vec![Assertion::single(3, last_step, Self::BaseField::ONE.into())] + vec![Assertion::single(0, last_step, Self::BaseField::ONE.into())] } } -pub fn build_trace(sequence_length: usize) -> TraceTable { +pub fn build_trace(sequence_length: usize) -> TraceTable { assert!( sequence_length.is_power_of_two(), "sequence length must be a power of 2" ); - let mut fibonacci = vec![FieldElement::one(), FieldElement::one()]; + let mut fibonacci = vec![Felt::ONE, Felt::ONE]; for i in 2..(sequence_length + 1) { fibonacci.push(fibonacci[i - 2] + fibonacci[i - 1]) } @@ -234,8 +232,61 @@ pub fn build_trace(sequence_length: usize) -> TraceTable { permuted.shuffle(&mut rng); TraceTable::init(vec![ - vec_field2adapter(&fibonacci[..fibonacci.len() - 1]), - vec_field2adapter(&fibonacci[1..]), - vec_field2adapter(&permuted), + fibonacci[..fibonacci.len() - 1].to_vec(), + fibonacci[1..].to_vec(), + permuted, ]) } + +#[cfg(test)] +mod tests { + use miden_core::Felt; + use stark_platinum_prover::{ + proof::options::ProofOptions, prover::IsStarkProver, verifier::IsStarkVerifier, + }; + use winter_air::{TraceInfo, TraceLayout}; + use winter_prover::Trace; + + use crate::{ + adapter::{ + air::AirAdapter, public_inputs::AirAdapterPublicInputs, Prover, Transcript, Verifier, + }, + examples::fibonacci_rap::{self, FibonacciRAP, RapTraceTable}, + }; + + #[test] + fn prove_and_verify_a_winterfell_fibonacci_rap_air() { + let lambda_proof_options = ProofOptions::default_test_options(); + let winter_trace = fibonacci_rap::build_trace(16); + let trace = + AirAdapter::, Felt, ()>::convert_winterfell_trace_table( + winter_trace.main_segment().clone(), + ); + let trace_layout = TraceLayout::new(3, [1], [1]); + let trace_info = TraceInfo::new_multi_segment(trace_layout, 16, vec![]); + let fibonacci_result = trace.columns()[1][15]; + let pub_inputs = AirAdapterPublicInputs:: { + winterfell_public_inputs: *fibonacci_result.value(), + transition_exemptions: vec![1, 1, 1], + transition_offsets: vec![0, 1], + trace_info, + metadata: (), + }; + + let proof = Prover::prove::, Felt, _>>( + &trace, + &pub_inputs, + &lambda_proof_options, + Transcript::new(&[]), + ) + .unwrap(); + assert!(Verifier::verify::< + AirAdapter, Felt, _>, + >( + &proof, + &pub_inputs, + &lambda_proof_options, + Transcript::new(&[]), + )); + } +} diff --git a/winterfell_adapter/src/examples/miden_vm.rs b/winterfell_adapter/src/examples/miden_vm.rs new file mode 100644 index 0000000000..0f6da58f68 --- /dev/null +++ b/winterfell_adapter/src/examples/miden_vm.rs @@ -0,0 +1,191 @@ +use miden_core::{Felt, ProgramInfo, StackOutputs}; +use miden_processor::{AuxTraceHints, ExecutionTrace, TraceLenSummary}; +use winter_air::TraceLayout; +use winter_prover::ColMatrix; + +use crate::adapter::air::FromColumns; + +#[derive(Clone)] +pub struct ExecutionTraceMetadata { + meta: Vec, + layout: TraceLayout, + aux_trace_hints: AuxTraceHints, + program_info: ProgramInfo, + stack_outputs: StackOutputs, + trace_len_summary: TraceLenSummary, +} + +impl From for ExecutionTraceMetadata { + fn from(value: ExecutionTrace) -> Self { + Self { + meta: value.meta, + layout: value.layout, + aux_trace_hints: value.aux_trace_hints, + program_info: value.program_info, + stack_outputs: value.stack_outputs, + trace_len_summary: value.trace_len_summary, + } + } +} + +impl FromColumns for ExecutionTrace { + fn from_cols(columns: Vec>, metadata: &ExecutionTraceMetadata) -> Self { + ExecutionTrace { + meta: metadata.meta.clone(), + layout: metadata.layout.clone(), + main_trace: ColMatrix::new(columns), + aux_trace_hints: metadata.aux_trace_hints.clone(), + program_info: metadata.program_info.clone(), + stack_outputs: metadata.stack_outputs.clone(), + trace_len_summary: metadata.trace_len_summary, + } + } +} + +#[cfg(test)] +mod tests { + use crate::adapter::air::AirAdapter; + use crate::adapter::public_inputs::AirAdapterPublicInputs; + use crate::adapter::{Prover, Transcript, Verifier}; + use crate::examples::fibonacci_2_terms::FibAir2Terms; + use miden_air::{ProcessorAir, ProvingOptions, PublicInputs}; + use miden_assembly::Assembler; + use miden_core::{Felt, StackInputs}; + use miden_processor::DefaultHost; + use miden_processor::{self as processor}; + use processor::ExecutionTrace; + use stark_platinum_prover::{ + proof::options::ProofOptions, prover::IsStarkProver, verifier::IsStarkVerifier, + }; + use winter_math::{FieldElement, StarkField}; + use winter_prover::Trace; + + #[test] + fn prove_and_verify_miden_readme_example() { + let mut lambda_proof_options = ProofOptions::default_test_options(); + lambda_proof_options.blowup_factor = 32; + let assembler = Assembler::default(); + + let program = assembler.compile("begin push.3 push.5 add end").unwrap(); + + let winter_trace = processor::execute( + &program, + StackInputs::default(), + DefaultHost::default(), + *ProvingOptions::default().execution_options(), + ) + .unwrap(); + let program_info = winter_trace.program_info().clone(); + let stack_outputs = winter_trace.stack_outputs().clone(); + + let pub_inputs = PublicInputs::new(program_info, StackInputs::default(), stack_outputs); + + let pub_inputs = AirAdapterPublicInputs { + winterfell_public_inputs: pub_inputs, + transition_exemptions: vec![2; 182], + transition_offsets: vec![0, 1], + trace_info: winter_trace.get_info(), + metadata: winter_trace.clone().into(), + }; + + let trace = + AirAdapter::::convert_winterfell_trace_table( + winter_trace.main_segment().clone(), + ); + + let proof = Prover::prove::>( + &trace, + &pub_inputs, + &lambda_proof_options, + Transcript::new(&[]), + ) + .unwrap(); + + assert!(Verifier::verify::< + AirAdapter, + >( + &proof, + &pub_inputs, + &lambda_proof_options, + Transcript::new(&[]), + )); + } + + fn compute_fibonacci(n: usize) -> Felt { + let mut t0 = Felt::ZERO; + let mut t1 = Felt::ONE; + + for _ in 0..n { + t1 = t0 + t1; + core::mem::swap(&mut t0, &mut t1); + } + t0 + } + + #[test] + fn prove_and_verify_miden_fibonacci() { + let fibonacci_number = 16; + let program = format!( + "begin + repeat.{} + swap dup.1 add + end + end", + fibonacci_number - 1 + ); + let program = Assembler::default().compile(program).unwrap(); + let expected_result = vec![compute_fibonacci(fibonacci_number).as_int()]; + let stack_inputs = StackInputs::try_from_values([0, 1]).unwrap(); + + let mut lambda_proof_options = ProofOptions::default_test_options(); + lambda_proof_options.blowup_factor = 8; + + let winter_trace = processor::execute( + &program, + stack_inputs.clone(), + DefaultHost::default(), + *ProvingOptions::default().execution_options(), + ) + .unwrap(); + let program_info = winter_trace.program_info().clone(); + let stack_outputs = winter_trace.stack_outputs().clone(); + + let pub_inputs = PublicInputs::new(program_info, stack_inputs, stack_outputs.clone()); + + assert_eq!( + expected_result, + stack_outputs.clone().stack_truncated(1), + "Program result was computed incorrectly" + ); + + let pub_inputs = AirAdapterPublicInputs { + winterfell_public_inputs: pub_inputs, + transition_exemptions: vec![2; 182], + transition_offsets: vec![0, 1], + trace_info: winter_trace.get_info(), + metadata: winter_trace.clone().into(), + }; + + let trace = + AirAdapter::::convert_winterfell_trace_table( + winter_trace.main_segment().clone(), + ); + + let proof = Prover::prove::>( + &trace, + &pub_inputs, + &lambda_proof_options, + Transcript::new(&[]), + ) + .unwrap(); + + assert!(Verifier::verify::< + AirAdapter, + >( + &proof, + &pub_inputs, + &lambda_proof_options, + Transcript::new(&[]), + )); + } +} diff --git a/winterfell_adapter/src/examples/mod.rs b/winterfell_adapter/src/examples/mod.rs index 9a74354be6..117c0609af 100644 --- a/winterfell_adapter/src/examples/mod.rs +++ b/winterfell_adapter/src/examples/mod.rs @@ -1,2 +1,4 @@ +pub mod cubic; pub mod fibonacci_2_terms; pub mod fibonacci_rap; +pub mod miden_vm; diff --git a/winterfell_adapter/src/field_element/element.rs b/winterfell_adapter/src/field_element/element.rs index dda01351e5..8137587d45 100644 --- a/winterfell_adapter/src/field_element/element.rs +++ b/winterfell_adapter/src/field_element/element.rs @@ -1,3 +1,4 @@ +use crate::field_element::positive_integer::AdapterPositiveInteger; use core::fmt; use core::{ mem, @@ -12,14 +13,8 @@ use lambdaworks_math::{ traits::ByteConversion, }; use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub}; -use winter_utils::{AsBytes, DeserializationError, Randomizable}; -use winterfell::math::ExtensibleField; -use winterfell::{ - math::{FieldElement as IsWinterfellFieldElement, StarkField}, - Deserializable, Serializable, -}; - -use crate::field_element::positive_integer::AdapterPositiveInteger; +use winter_math::{ExtensibleField, FieldElement as IsWinterfellFieldElement, StarkField}; +use winter_utils::{AsBytes, Deserializable, DeserializationError, Randomizable, Serializable}; #[derive(Debug, Copy, Clone, Default)] pub struct AdapterFieldElement(pub FieldElement); @@ -68,7 +63,7 @@ impl IsWinterfellFieldElement for AdapterFieldElement { unsafe { slice::from_raw_parts(p as *const u8, len) } } - unsafe fn bytes_as_elements(bytes: &[u8]) -> Result<&[Self], winterfell::DeserializationError> { + unsafe fn bytes_as_elements(bytes: &[u8]) -> Result<&[Self], DeserializationError> { if bytes.len() % Self::ELEMENT_BYTES != 0 { return Err(DeserializationError::InvalidValue(format!( "number of bytes ({}) does not divide into whole number of field elements", diff --git a/winterfell_adapter/src/utils.rs b/winterfell_adapter/src/utils.rs index 40e082c831..8fa8422c38 100644 --- a/winterfell_adapter/src/utils.rs +++ b/winterfell_adapter/src/utils.rs @@ -1,23 +1,38 @@ -use crate::field_element::element::AdapterFieldElement; -use lambdaworks_math::field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField; +use lambdaworks_math::{field::traits::IsField, traits::ByteConversion}; use stark_platinum_prover::fri::FieldElement; +use winter_math::FieldElement as IsWinterfellFieldElement; -pub fn vec_field2adapter(input: &[FieldElement]) -> Vec { - input.iter().map(|&e| AdapterFieldElement(e)).collect() +pub fn vec_lambda2winter< + FE: IsField + IsWinterfellFieldElement + ByteConversion + Unpin, +>( + input: &[FieldElement], +) -> Vec { + input.iter().map(|&e| *e.value()).collect() } -pub fn vec_adapter2field(input: &[AdapterFieldElement]) -> Vec> { - input.iter().map(|&e| e.0).collect() +pub fn vec_winter2lambda< + FE: IsField + IsWinterfellFieldElement + ByteConversion + Unpin, +>( + input: &[FE], +) -> Vec> { + input + .iter() + .map(|&e| FieldElement::::const_from_raw(e)) + .collect() } -pub fn matrix_field2adapter( - input: &[Vec>], -) -> Vec> { - input.iter().map(|v| vec_field2adapter(v)).collect() +pub fn matrix_lambda2winter< + FE: IsField + IsWinterfellFieldElement + ByteConversion + Unpin, +>( + input: &[Vec>], +) -> Vec> { + input.iter().map(|v| vec_lambda2winter(v)).collect() } -pub fn matrix_adapter2field( - input: &[Vec], -) -> Vec>> { - input.iter().map(|v| vec_adapter2field(v)).collect() +pub fn matrix_winter2lambda< + FE: IsField + IsWinterfellFieldElement + ByteConversion + Unpin, +>( + input: &[Vec], +) -> Vec>> { + input.iter().map(|v| vec_winter2lambda(v)).collect() } From 8704cfc0dd17e930af77be5d7cad71de47513e1e Mon Sep 17 00:00:00 2001 From: Marcos Villagra <71292159+mdvillagra@users.noreply.github.com> Date: Mon, 18 Dec 2023 11:45:48 -0300 Subject: [PATCH 16/29] Add Pallas curve basic functionality (#698) * added pallas field * cargo fmt * starting with pallas * curve created * cargo fmt * adding tests * adding unit tests * added unit tests * one test is failing * pallas curve basic functionality --- .../short_weierstrass/curves/mod.rs | 1 + .../short_weierstrass/curves/pallas/curve.rs | 136 ++++++++++++++++++ .../short_weierstrass/curves/pallas/mod.rs | 1 + 3 files changed, 138 insertions(+) create mode 100644 math/src/elliptic_curve/short_weierstrass/curves/pallas/curve.rs create mode 100644 math/src/elliptic_curve/short_weierstrass/curves/pallas/mod.rs diff --git a/math/src/elliptic_curve/short_weierstrass/curves/mod.rs b/math/src/elliptic_curve/short_weierstrass/curves/mod.rs index 9c53071b27..f10e096850 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/mod.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/mod.rs @@ -1,5 +1,6 @@ pub mod bls12_377; pub mod bls12_381; +pub mod pallas; pub mod stark_curve; pub mod test_curve_1; pub mod test_curve_2; diff --git a/math/src/elliptic_curve/short_weierstrass/curves/pallas/curve.rs b/math/src/elliptic_curve/short_weierstrass/curves/pallas/curve.rs new file mode 100644 index 0000000000..d932d7522e --- /dev/null +++ b/math/src/elliptic_curve/short_weierstrass/curves/pallas/curve.rs @@ -0,0 +1,136 @@ +use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint; +use crate::elliptic_curve::traits::IsEllipticCurve; +use crate::field::fields::pallas_field::Pallas255PrimeField; +use crate::{ + elliptic_curve::short_weierstrass::traits::IsShortWeierstrass, field::element::FieldElement, +}; + +#[derive(Clone, Debug)] +pub struct PallasCurve; + +impl IsEllipticCurve for PallasCurve { + type BaseField = Pallas255PrimeField; + type PointRepresentation = ShortWeierstrassProjectivePoint; + + fn generator() -> Self::PointRepresentation { + Self::PointRepresentation::new([ + -FieldElement::::one(), + FieldElement::::from(2), + FieldElement::one(), + ]) + } +} + +impl IsShortWeierstrass for PallasCurve { + fn a() -> FieldElement { + FieldElement::from(0) + } + + fn b() -> FieldElement { + FieldElement::from(5) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + cyclic_group::IsGroup, elliptic_curve::traits::EllipticCurveError, + field::element::FieldElement, + }; + + use super::PallasCurve; + + #[allow(clippy::upper_case_acronyms)] + type FE = FieldElement; + + fn point_1() -> ShortWeierstrassProjectivePoint { + let x = FE::from_hex_unchecked( + "bd1e740e6b1615ae4c508148ca0c53dbd43f7b2e206195ab638d7f45d51d6b5", + ); + let y = FE::from_hex_unchecked( + "13aacd107ca10b7f8aab570da1183b91d7d86dd723eaa2306b0ef9c5355b91d8", + ); + PallasCurve::create_point_from_affine(x, y).unwrap() + } + + fn point_1_times_5() -> ShortWeierstrassProjectivePoint { + let x = FE::from_hex_unchecked( + "17a21304fffd6749d6173d4e0acd9724d98a97453b3491c0e5a53b06cf039b13", + ); + let y = FE::from_hex_unchecked( + "2f9bde429091a1089e52a6cc5dc789e1a58eeded0cf72dccc33b7af685a982d", + ); + PallasCurve::create_point_from_affine(x, y).unwrap() + } + + #[test] + fn adding_five_times_point_1_works() { + let point_1 = point_1(); + let point_1_times_5 = point_1_times_5(); + assert_eq!(point_1.operate_with_self(5_u16), point_1_times_5); + } + + #[test] + fn create_valid_point_works() { + let p = point_1(); + assert_eq!( + *p.x(), + FE::from_hex_unchecked( + "bd1e740e6b1615ae4c508148ca0c53dbd43f7b2e206195ab638d7f45d51d6b5" + ) + ); + assert_eq!( + *p.y(), + FE::from_hex_unchecked( + "13aacd107ca10b7f8aab570da1183b91d7d86dd723eaa2306b0ef9c5355b91d8" + ) + ); + assert_eq!(*p.z(), FE::from_hex_unchecked("1")); + } + + #[test] + fn create_invalid_points_returns_an_error() { + assert_eq!( + PallasCurve::create_point_from_affine(FE::from(0), FE::from(1)), + Err(EllipticCurveError::InvalidPoint) + ); + } + + #[test] + fn equality_works() { + let g = PallasCurve::generator(); + let g2 = g.operate_with_self(2_u16); + let g2_other = g.operate_with(&g); + assert_ne!(&g2, &g); + assert_eq!(&g, &g); + assert_eq!(&g2, &g2_other); + } + + #[test] + fn g_operated_with_g_satifies_ec_equation() { + let g = PallasCurve::generator(); + let g2 = g.operate_with_self(2_u16); + + // get x and y from affine coordinates + let g2_affine = g2.to_affine(); + let x = g2_affine.x(); + let y = g2_affine.y(); + + // calculate both sides of Pallas curve equation + let five = PallasCurve::b(); + let y_sq_0 = x.pow(3_u16) + five; + let y_sq_1 = y.pow(2_u16); + + assert_eq!(y_sq_0, y_sq_1); + } + + #[test] + fn operate_with_self_works_1() { + let g = PallasCurve::generator(); + assert_eq!( + g.operate_with(&g).operate_with(&g), + g.operate_with_self(3_u16) + ); + } +} diff --git a/math/src/elliptic_curve/short_weierstrass/curves/pallas/mod.rs b/math/src/elliptic_curve/short_weierstrass/curves/pallas/mod.rs new file mode 100644 index 0000000000..201a862ce5 --- /dev/null +++ b/math/src/elliptic_curve/short_weierstrass/curves/pallas/mod.rs @@ -0,0 +1 @@ +pub mod curve; From b284c340704300c538675720cd2fa0728f453e03 Mon Sep 17 00:00:00 2001 From: mb-dci <149032956+mb-dci@users.noreply.github.com> Date: Mon, 18 Dec 2023 09:46:01 -0500 Subject: [PATCH 17/29] Affine serialization (#687) * uncompressed * cargo fmt --------- Co-authored-by: Mauro Toscano <12560266+MauroToscano@users.noreply.github.com> --- .../elliptic_curve/short_weierstrass/point.rs | 235 ++++++++++++++---- 1 file changed, 185 insertions(+), 50 deletions(-) diff --git a/math/src/elliptic_curve/short_weierstrass/point.rs b/math/src/elliptic_curve/short_weierstrass/point.rs index 13acb3ca78..3881909946 100644 --- a/math/src/elliptic_curve/short_weierstrass/point.rs +++ b/math/src/elliptic_curve/short_weierstrass/point.rs @@ -212,8 +212,7 @@ impl IsGroup for ShortWeierstrassProjectivePoint { #[derive(PartialEq)] pub enum PointFormat { Projective, - // TO DO: - // Uncompressed, + Uncompressed, // Compressed, } @@ -233,7 +232,7 @@ where { /// Serialize the points in the given format #[cfg(feature = "std")] - pub fn serialize(&self, _point_format: PointFormat, endianness: Endianness) -> Vec { + pub fn serialize(&self, point_format: PointFormat, endianness: Endianness) -> Vec { // TODO: Add more compact serialization formats // Uncompressed affine / Compressed @@ -242,59 +241,101 @@ where let y_bytes: Vec; let z_bytes: Vec; - let [x, y, z] = self.coordinates(); - if endianness == Endianness::BigEndian { - x_bytes = x.to_bytes_be(); - y_bytes = y.to_bytes_be(); - z_bytes = z.to_bytes_be(); - } else { - x_bytes = x.to_bytes_le(); - y_bytes = y.to_bytes_le(); - z_bytes = z.to_bytes_le(); + match point_format { + PointFormat::Projective => { + let [x, y, z] = self.coordinates(); + if endianness == Endianness::BigEndian { + x_bytes = x.to_bytes_be(); + y_bytes = y.to_bytes_be(); + z_bytes = z.to_bytes_be(); + } else { + x_bytes = x.to_bytes_le(); + y_bytes = y.to_bytes_le(); + z_bytes = z.to_bytes_le(); + } + bytes.extend(&x_bytes); + bytes.extend(&y_bytes); + bytes.extend(&z_bytes); + } + PointFormat::Uncompressed => { + let affine_representation = self.to_affine(); + let [x, y, _z] = affine_representation.coordinates(); + if endianness == Endianness::BigEndian { + x_bytes = x.to_bytes_be(); + y_bytes = y.to_bytes_be(); + } else { + x_bytes = x.to_bytes_le(); + y_bytes = y.to_bytes_le(); + } + bytes.extend(&x_bytes); + bytes.extend(&y_bytes); + } } - - bytes.extend(&x_bytes); - bytes.extend(&y_bytes); - bytes.extend(&z_bytes); - bytes } pub fn deserialize( bytes: &[u8], - _point_format: PointFormat, + point_format: PointFormat, endianness: Endianness, ) -> Result { - if bytes.len() % 3 != 0 { - return Err(DeserializationError::InvalidAmountOfBytes); - } + match point_format { + PointFormat::Projective => { + if bytes.len() % 3 != 0 { + return Err(DeserializationError::InvalidAmountOfBytes); + } - let len = bytes.len() / 3; - let x: FieldElement; - let y: FieldElement; - let z: FieldElement; + let len = bytes.len() / 3; + let x: FieldElement; + let y: FieldElement; + let z: FieldElement; - if endianness == Endianness::BigEndian { - x = ByteConversion::from_bytes_be(&bytes[..len])?; - y = ByteConversion::from_bytes_be(&bytes[len..len * 2])?; - z = ByteConversion::from_bytes_be(&bytes[len * 2..])?; - } else { - x = ByteConversion::from_bytes_le(&bytes[..len])?; - y = ByteConversion::from_bytes_le(&bytes[len..len * 2])?; - z = ByteConversion::from_bytes_le(&bytes[len * 2..])?; - } + if endianness == Endianness::BigEndian { + x = ByteConversion::from_bytes_be(&bytes[..len])?; + y = ByteConversion::from_bytes_be(&bytes[len..len * 2])?; + z = ByteConversion::from_bytes_be(&bytes[len * 2..])?; + } else { + x = ByteConversion::from_bytes_le(&bytes[..len])?; + y = ByteConversion::from_bytes_le(&bytes[len..len * 2])?; + z = ByteConversion::from_bytes_le(&bytes[len * 2..])?; + } - if z == FieldElement::zero() { - let point = Self::new([x, y, z]); - if point.is_neutral_element() { - Ok(point) - } else { - Err(DeserializationError::FieldFromBytesError) + if z == FieldElement::zero() { + let point = Self::new([x, y, z]); + if point.is_neutral_element() { + Ok(point) + } else { + Err(DeserializationError::FieldFromBytesError) + } + } else if E::defining_equation(&(&x / &z), &(&y / &z)) == FieldElement::zero() { + Ok(Self::new([x, y, z])) + } else { + Err(DeserializationError::FieldFromBytesError) + } + } + PointFormat::Uncompressed => { + if bytes.len() % 2 != 0 { + return Err(DeserializationError::InvalidAmountOfBytes); + } + + let len = bytes.len() / 2; + let x: FieldElement; + let y: FieldElement; + + if endianness == Endianness::BigEndian { + x = ByteConversion::from_bytes_be(&bytes[..len])?; + y = ByteConversion::from_bytes_be(&bytes[len..])?; + } else { + x = ByteConversion::from_bytes_le(&bytes[..len])?; + y = ByteConversion::from_bytes_le(&bytes[len..])?; + } + + if E::defining_equation(&x, &y) == FieldElement::zero() { + Ok(Self::new([x, y, FieldElement::one()])) + } else { + Err(DeserializationError::FieldFromBytesError) + } } - } else if E::defining_equation(&(&x / &z), &(&y / &z)) == FieldElement::zero() { - Ok(Self::new([x, y, z])) - } else { - Err(DeserializationError::FieldFromBytesError) } } } @@ -347,7 +388,7 @@ mod tests { #[cfg(feature = "std")] #[test] - fn byte_conversion_from_and_to_be() { + fn byte_conversion_from_and_to_be_projective() { let expected_point = point(); let bytes_be = expected_point.serialize(PointFormat::Projective, Endianness::BigEndian); @@ -361,7 +402,20 @@ mod tests { #[cfg(feature = "std")] #[test] - fn byte_conversion_from_and_to_le() { + fn byte_conversion_from_and_to_be_uncompressed() { + let expected_point = point(); + let bytes_be = expected_point.serialize(PointFormat::Uncompressed, Endianness::BigEndian); + let result = ShortWeierstrassProjectivePoint::deserialize( + &bytes_be, + PointFormat::Uncompressed, + Endianness::BigEndian, + ); + assert_eq!(expected_point, result.unwrap()); + } + + #[cfg(feature = "std")] + #[test] + fn byte_conversion_from_and_to_le_projective() { let expected_point = point(); let bytes_be = expected_point.serialize(PointFormat::Projective, Endianness::LittleEndian); @@ -375,7 +429,22 @@ mod tests { #[cfg(feature = "std")] #[test] - fn byte_conversion_from_and_to_with_mixed_le_and_be_does_not_work() { + fn byte_conversion_from_and_to_le_uncompressed() { + let expected_point = point(); + let bytes_be = + expected_point.serialize(PointFormat::Uncompressed, Endianness::LittleEndian); + + let result = ShortWeierstrassProjectivePoint::deserialize( + &bytes_be, + PointFormat::Uncompressed, + Endianness::LittleEndian, + ); + assert_eq!(expected_point, result.unwrap()); + } + + #[cfg(feature = "std")] + #[test] + fn byte_conversion_from_and_to_with_mixed_le_and_be_does_not_work_projective() { let bytes = point().serialize(PointFormat::Projective, Endianness::LittleEndian); let result = ShortWeierstrassProjectivePoint::::deserialize( @@ -392,7 +461,24 @@ mod tests { #[cfg(feature = "std")] #[test] - fn byte_conversion_from_and_to_with_mixed_be_and_le_does_not_work() { + fn byte_conversion_from_and_to_with_mixed_le_and_be_does_not_work_uncompressed() { + let bytes = point().serialize(PointFormat::Uncompressed, Endianness::LittleEndian); + + let result = ShortWeierstrassProjectivePoint::::deserialize( + &bytes, + PointFormat::Uncompressed, + Endianness::BigEndian, + ); + + assert_eq!( + result.unwrap_err(), + DeserializationError::FieldFromBytesError + ); + } + + #[cfg(feature = "std")] + #[test] + fn byte_conversion_from_and_to_with_mixed_be_and_le_does_not_work_projective() { let bytes = point().serialize(PointFormat::Projective, Endianness::BigEndian); let result = ShortWeierstrassProjectivePoint::::deserialize( @@ -407,8 +493,25 @@ mod tests { ); } + #[cfg(feature = "std")] + #[test] + fn byte_conversion_from_and_to_with_mixed_be_and_le_does_not_work_uncompressed() { + let bytes = point().serialize(PointFormat::Uncompressed, Endianness::BigEndian); + + let result = ShortWeierstrassProjectivePoint::::deserialize( + &bytes, + PointFormat::Uncompressed, + Endianness::LittleEndian, + ); + + assert_eq!( + result.unwrap_err(), + DeserializationError::FieldFromBytesError + ); + } + #[test] - fn cannot_create_point_from_wrong_number_of_bytes_le() { + fn cannot_create_point_from_wrong_number_of_bytes_le_projective() { let bytes = &[0_u8; 13]; let result = ShortWeierstrassProjectivePoint::::deserialize( @@ -424,7 +527,23 @@ mod tests { } #[test] - fn cannot_create_point_from_wrong_number_of_bytes_be() { + fn cannot_create_point_from_wrong_number_of_bytes_le_uncompressed() { + let bytes = &[0_u8; 13]; + + let result = ShortWeierstrassProjectivePoint::::deserialize( + bytes, + PointFormat::Uncompressed, + Endianness::LittleEndian, + ); + + assert_eq!( + result.unwrap_err(), + DeserializationError::InvalidAmountOfBytes + ); + } + + #[test] + fn cannot_create_point_from_wrong_number_of_bytes_be_projective() { let bytes = &[0_u8; 13]; let result = ShortWeierstrassProjectivePoint::::deserialize( @@ -438,4 +557,20 @@ mod tests { DeserializationError::InvalidAmountOfBytes ); } + + #[test] + fn cannot_create_point_from_wrong_number_of_bytes_be_uncompressed() { + let bytes = &[0_u8; 13]; + + let result = ShortWeierstrassProjectivePoint::::deserialize( + bytes, + PointFormat::Uncompressed, + Endianness::BigEndian, + ); + + assert_eq!( + result.unwrap_err(), + DeserializationError::InvalidAmountOfBytes + ); + } } From 703db8a535793ecc820e044b06c1b735764f0881 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> Date: Mon, 18 Dec 2023 11:46:30 -0300 Subject: [PATCH 18/29] Stark: Prover and Verifier over field extensions (#716) * wip * cast to fieldextension * fmt * clippy * fmt * fix parallel * reverse in place Co-authored-by: Mario Rugiero --------- Co-authored-by: Mario Rugiero --- math/src/polynomial.rs | 437 +++++++++++++----- provers/cairo/src/air.rs | 1 + provers/cairo/src/tests/integration_tests.rs | 4 +- provers/plonk/src/prover.rs | 15 +- provers/stark/src/constraints/evaluator.rs | 77 +-- provers/stark/src/debug.rs | 21 +- provers/stark/src/examples/dummy_air.rs | 3 +- .../src/examples/fibonacci_2_cols_shifted.rs | 3 +- .../stark/src/examples/fibonacci_2_columns.rs | 3 +- provers/stark/src/examples/fibonacci_rap.rs | 1 + provers/stark/src/examples/quadratic_air.rs | 3 +- .../stark/src/examples/simple_fibonacci.rs | 3 +- .../src/examples/simple_periodic_cols.rs | 3 +- provers/stark/src/frame.rs | 6 +- provers/stark/src/fri/fri_commitment.rs | 7 +- provers/stark/src/fri/fri_functions.rs | 7 +- provers/stark/src/fri/mod.rs | 40 +- provers/stark/src/proof/stark.rs | 10 +- provers/stark/src/prover.rs | 206 +++++---- provers/stark/src/table.rs | 10 +- provers/stark/src/trace.rs | 27 +- provers/stark/src/traits.rs | 34 +- provers/stark/src/transcript.rs | 16 +- provers/stark/src/verifier.rs | 148 +++--- winterfell_adapter/src/adapter/air.rs | 1 + winterfell_adapter/src/adapter/mod.rs | 2 + 26 files changed, 689 insertions(+), 399 deletions(-) diff --git a/math/src/polynomial.rs b/math/src/polynomial.rs index d479f8881d..4a4008abe6 100644 --- a/math/src/polynomial.rs +++ b/math/src/polynomial.rs @@ -90,12 +90,16 @@ impl Polynomial> { Ok(result) } - pub fn evaluate(&self, x: &FieldElement) -> FieldElement { + pub fn evaluate(&self, x: &FieldElement) -> FieldElement + where + E: IsField, + F: IsSubFieldOf, + { self.coefficients .iter() .rev() .fold(FieldElement::zero(), |acc, coeff| { - acc * x.to_owned() + coeff + coeff + acc * x.to_owned() }) } @@ -131,23 +135,6 @@ impl Polynomial> { self.coefficients().len() } - pub fn pad_with_zero_coefficients_to_length(pa: &mut Self, n: usize) { - pa.coefficients.resize(n, FieldElement::zero()); - } - - /// Pads polynomial representations with minimum number of zeros to match lengths. - pub fn pad_with_zero_coefficients(pa: &Self, pb: &Self) -> (Self, Self) { - let mut pa = pa.clone(); - let mut pb = pb.clone(); - - if pa.coefficients.len() > pb.coefficients.len() { - Self::pad_with_zero_coefficients_to_length(&mut pb, pa.coefficients.len()); - } else { - Self::pad_with_zero_coefficients_to_length(&mut pa, pb.coefficients.len()); - } - (pa, pb) - } - /// Computes quotient with `x - b` in place. pub fn ruffini_division_inplace(&mut self, b: &FieldElement) { let mut c = FieldElement::zero(); @@ -158,6 +145,25 @@ impl Polynomial> { self.coefficients.pop(); } + pub fn ruffini_division(&self, b: &FieldElement) -> Polynomial> + where + L: IsField, + F: IsSubFieldOf, + { + if let Some(c) = self.coefficients.last() { + let mut c = c.clone().to_extension(); + let mut coefficients = Vec::with_capacity(self.degree()); + for coeff in self.coefficients.iter().rev().skip(1) { + coefficients.push(c.clone()); + c = coeff + c * b; + } + coefficients.reverse(); + Polynomial::new(&coefficients) + } else { + Polynomial::zero() + } + } + /// Computes quotient and remainder of polynomial division. /// /// Output: (quotient, remainder) @@ -246,6 +252,42 @@ impl Polynomial> { } parts } + + pub fn to_extension(self) -> Polynomial> + where + F: IsSubFieldOf, + { + Polynomial { + coefficients: self + .coefficients + .into_iter() + .map(|x| x.to_extension::()) + .collect(), + } + } +} + +pub fn pad_with_zero_coefficients_to_length( + pa: &mut Polynomial>, + n: usize, +) { + pa.coefficients.resize(n, FieldElement::zero()); +} + +/// Pads polynomial representations with minimum number of zeros to match lengths. +pub fn pad_with_zero_coefficients>( + pa: &Polynomial>, + pb: &Polynomial>, +) -> (Polynomial>, Polynomial>) { + let mut pa = pa.clone(); + let mut pb = pb.clone(); + + if pa.coefficients.len() > pb.coefficients.len() { + pad_with_zero_coefficients_to_length(&mut pb, pa.coefficients.len()); + } else { + pad_with_zero_coefficients_to_length(&mut pa, pb.coefficients.len()); + } + (pa, pb) } pub fn compose( @@ -274,39 +316,55 @@ where .expect("xs and ys have equal length and xs are unique") } -impl ops::Add<&Polynomial>> for &Polynomial> { - type Output = Polynomial>; +impl ops::Add<&Polynomial>> for &Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn add(self, a_polynomial: &Polynomial>) -> Self::Output { - let (pa, pb) = Polynomial::pad_with_zero_coefficients(self, a_polynomial); + fn add(self, a_polynomial: &Polynomial>) -> Self::Output { + let (pa, pb) = pad_with_zero_coefficients(self, a_polynomial); let iter_coeff_pa = pa.coefficients.iter(); let iter_coeff_pb = pb.coefficients.iter(); let new_coefficients = iter_coeff_pa.zip(iter_coeff_pb).map(|(x, y)| x + y); - let new_coefficients_vec = new_coefficients.collect::>>(); + let new_coefficients_vec = new_coefficients.collect::>>(); Polynomial::new(&new_coefficients_vec) } } -impl ops::Add>> for Polynomial> { - type Output = Polynomial>; +impl ops::Add>> for Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn add(self, a_polynomial: Polynomial>) -> Polynomial> { + fn add(self, a_polynomial: Polynomial>) -> Polynomial> { &self + &a_polynomial } } -impl ops::Add<&Polynomial>> for Polynomial> { - type Output = Polynomial>; +impl ops::Add<&Polynomial>> for Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn add(self, a_polynomial: &Polynomial>) -> Polynomial> { + fn add(self, a_polynomial: &Polynomial>) -> Polynomial> { &self + a_polynomial } } -impl ops::Add>> for &Polynomial> { - type Output = Polynomial>; +impl ops::Add>> for &Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn add(self, a_polynomial: Polynomial>) -> Polynomial> { + fn add(self, a_polynomial: Polynomial>) -> Polynomial> { self + &a_polynomial } } @@ -332,39 +390,58 @@ impl ops::Neg for Polynomial> { } } -impl ops::Sub>> for Polynomial> { - type Output = Polynomial>; +impl ops::Sub<&Polynomial>> for &Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn sub(self, substrahend: Polynomial>) -> Polynomial> { - &self - &substrahend + fn sub(self, substrahend: &Polynomial>) -> Polynomial> { + self + (-substrahend) } } -impl ops::Sub<&Polynomial>> for Polynomial> { - type Output = Polynomial>; +impl ops::Sub>> for Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn sub(self, substrahend: &Polynomial>) -> Polynomial> { - &self - substrahend + fn sub(self, substrahend: Polynomial>) -> Polynomial> { + &self - &substrahend } } -impl ops::Sub>> for &Polynomial> { - type Output = Polynomial>; +impl ops::Sub<&Polynomial>> for Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn sub(self, substrahend: Polynomial>) -> Polynomial> { - self - &substrahend + fn sub(self, substrahend: &Polynomial>) -> Polynomial> { + &self - substrahend } } -impl ops::Sub<&Polynomial>> for &Polynomial> { - type Output = Polynomial>; +impl ops::Sub>> for &Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn sub(self, substrahend: &Polynomial>) -> Polynomial> { - self + (-substrahend) + fn sub(self, substrahend: Polynomial>) -> Polynomial> { + self - &substrahend } } -impl ops::Div>> for Polynomial> { +impl ops::Div>> for Polynomial> +where + F: IsField, +{ type Output = Polynomial>; fn div(self, dividend: Polynomial>) -> Polynomial> { @@ -402,14 +479,18 @@ impl ops::Mul<&Polynomial>> for Polynomial ops::Mul> for Polynomial> { - type Output = Polynomial>; +impl ops::Mul> for Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn mul(self, multiplicand: FieldElement) -> Polynomial> { + fn mul(self, multiplicand: FieldElement) -> Polynomial> { let new_coefficients = self .coefficients .iter() - .map(|value| value * &multiplicand) + .map(|value| &multiplicand * value) .collect(); Polynomial { coefficients: new_coefficients, @@ -417,191 +498,283 @@ impl ops::Mul> for Polynomial> { } } -impl ops::Mul<&FieldElement> for &Polynomial> { - type Output = Polynomial>; +impl ops::Mul<&FieldElement> for &Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn mul(self, multiplicand: &FieldElement) -> Polynomial> { + fn mul(self, multiplicand: &FieldElement) -> Polynomial> { self.clone() * multiplicand.clone() } } -impl ops::Mul> for &Polynomial> { - type Output = Polynomial>; +impl ops::Mul> for &Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn mul(self, multiplicand: FieldElement) -> Polynomial> { + fn mul(self, multiplicand: FieldElement) -> Polynomial> { self * &multiplicand } } -impl ops::Mul<&FieldElement> for Polynomial> { - type Output = Polynomial>; +impl ops::Mul<&FieldElement> for Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn mul(self, multiplicand: &FieldElement) -> Polynomial> { + fn mul(self, multiplicand: &FieldElement) -> Polynomial> { &self * multiplicand } } /* Multiplication field element at right */ -impl ops::Mul<&Polynomial>> for &FieldElement { - type Output = Polynomial>; +impl ops::Mul<&Polynomial>> for &FieldElement +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn mul(self, multiplicand: &Polynomial>) -> Polynomial> { + fn mul(self, multiplicand: &Polynomial>) -> Polynomial> { multiplicand * self } } -impl ops::Mul>> for &FieldElement { - type Output = Polynomial>; +impl ops::Mul>> for &FieldElement +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn mul(self, multiplicand: Polynomial>) -> Polynomial> { + fn mul(self, multiplicand: Polynomial>) -> Polynomial> { &multiplicand * self } } -impl ops::Mul<&Polynomial>> for FieldElement { - type Output = Polynomial>; +impl ops::Mul<&Polynomial>> for FieldElement +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn mul(self, multiplicand: &Polynomial>) -> Polynomial> { + fn mul(self, multiplicand: &Polynomial>) -> Polynomial> { multiplicand * self } } -impl ops::Mul>> for FieldElement { - type Output = Polynomial>; +impl ops::Mul>> for FieldElement +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn mul(self, multiplicand: Polynomial>) -> Polynomial> { + fn mul(self, multiplicand: Polynomial>) -> Polynomial> { &multiplicand * &self } } /* Addition field element at left */ -impl ops::Add<&FieldElement> for &Polynomial> { - type Output = Polynomial>; +impl ops::Add<&FieldElement> for &Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn add(self, other: &FieldElement) -> Polynomial> { + fn add(self, other: &FieldElement) -> Polynomial> { Polynomial::new_monomial(other.clone(), 0) + self } } -impl ops::Add> for Polynomial> { - type Output = Polynomial>; +impl ops::Add> for Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn add(self, other: FieldElement) -> Polynomial> { + fn add(self, other: FieldElement) -> Polynomial> { &self + &other } } -impl ops::Add> for &Polynomial> { - type Output = Polynomial>; +impl ops::Add> for &Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn add(self, other: FieldElement) -> Polynomial> { + fn add(self, other: FieldElement) -> Polynomial> { self + &other } } -impl ops::Add<&FieldElement> for Polynomial> { - type Output = Polynomial>; +impl ops::Add<&FieldElement> for Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn add(self, other: &FieldElement) -> Polynomial> { + fn add(self, other: &FieldElement) -> Polynomial> { &self + other } } /* Addition field element at right */ -impl ops::Add<&Polynomial>> for &FieldElement { - type Output = Polynomial>; +impl ops::Add<&Polynomial>> for &FieldElement +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn add(self, other: &Polynomial>) -> Polynomial> { + fn add(self, other: &Polynomial>) -> Polynomial> { Polynomial::new_monomial(self.clone(), 0) + other } } -impl ops::Add>> for FieldElement { - type Output = Polynomial>; +impl ops::Add>> for FieldElement +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn add(self, other: Polynomial>) -> Polynomial> { + fn add(self, other: Polynomial>) -> Polynomial> { &self + &other } } -impl ops::Add>> for &FieldElement { - type Output = Polynomial>; +impl ops::Add>> for &FieldElement +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn add(self, other: Polynomial>) -> Polynomial> { + fn add(self, other: Polynomial>) -> Polynomial> { self + &other } } -impl ops::Add<&Polynomial>> for FieldElement { - type Output = Polynomial>; +impl ops::Add<&Polynomial>> for FieldElement +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn add(self, other: &Polynomial>) -> Polynomial> { + fn add(self, other: &Polynomial>) -> Polynomial> { &self + other } } /* Substraction field element at left */ -impl ops::Sub<&FieldElement> for &Polynomial> { - type Output = Polynomial>; +impl ops::Sub<&FieldElement> for &Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn sub(self, other: &FieldElement) -> Polynomial> { - self - Polynomial::new_monomial(other.clone(), 0) + fn sub(self, other: &FieldElement) -> Polynomial> { + -Polynomial::new_monomial(other.clone(), 0) + self } } -impl ops::Sub> for Polynomial> { - type Output = Polynomial>; +impl ops::Sub> for Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn sub(self, other: FieldElement) -> Polynomial> { + fn sub(self, other: FieldElement) -> Polynomial> { &self - &other } } -impl ops::Sub> for &Polynomial> { - type Output = Polynomial>; +impl ops::Sub> for &Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn sub(self, other: FieldElement) -> Polynomial> { + fn sub(self, other: FieldElement) -> Polynomial> { self - &other } } -impl ops::Sub<&FieldElement> for Polynomial> { - type Output = Polynomial>; +impl ops::Sub<&FieldElement> for Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn sub(self, other: &FieldElement) -> Polynomial> { + fn sub(self, other: &FieldElement) -> Polynomial> { &self - other } } /* Substraction field element at right */ -impl ops::Sub<&Polynomial>> for &FieldElement { - type Output = Polynomial>; +impl ops::Sub<&Polynomial>> for &FieldElement +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn sub(self, other: &Polynomial>) -> Polynomial> { + fn sub(self, other: &Polynomial>) -> Polynomial> { Polynomial::new_monomial(self.clone(), 0) - other } } -impl ops::Sub>> for FieldElement { - type Output = Polynomial>; +impl ops::Sub>> for FieldElement +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn sub(self, other: Polynomial>) -> Polynomial> { + fn sub(self, other: Polynomial>) -> Polynomial> { &self - &other } } -impl ops::Sub>> for &FieldElement { - type Output = Polynomial>; +impl ops::Sub>> for &FieldElement +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn sub(self, other: Polynomial>) -> Polynomial> { + fn sub(self, other: Polynomial>) -> Polynomial> { self - &other } } -impl ops::Sub<&Polynomial>> for FieldElement { - type Output = Polynomial>; +impl ops::Sub<&Polynomial>> for FieldElement +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn sub(self, other: &Polynomial>) -> Polynomial> { + fn sub(self, other: &Polynomial>) -> Polynomial> { &self - other } } @@ -711,7 +884,7 @@ mod tests { let p2 = Polynomial::new(&[FE::new(3)]); assert_eq!(p2.coefficients, &[FE::new(3)]); - let (pp1, pp2) = Polynomial::pad_with_zero_coefficients(&p1, &p2); + let (pp1, pp2) = pad_with_zero_coefficients(&p1, &p2); assert_eq!(pp1, p1); assert_eq!(pp2.coefficients, &[FE::new(3), FE::new(0)]); } @@ -909,7 +1082,7 @@ mod tests { use proptest::prelude::*; proptest! { #[test] - fn ruffini_equals_division(p in any::>(), b in any::()) { + fn ruffini_inplace_equals_division(p in any::>(), b in any::()) { let p: Vec<_> = p.into_iter().map(FE::from).collect(); let mut p = Polynomial::new(&p); let b = FE::from(b); @@ -921,4 +1094,16 @@ mod tests { prop_assert_eq!(p, p_ref / m); } } + + proptest! { + #[test] + fn ruffini_inplace_equals_ruffini(p in any::>(), b in any::()) { + let p: Vec<_> = p.into_iter().map(FE::from).collect(); + let mut p = Polynomial::new(&p); + let b = FE::from(b); + let q = p.ruffini_division(&b); + p.ruffini_division_inplace(&b); + prop_assert_eq!(q, p); + } + } } diff --git a/provers/cairo/src/air.rs b/provers/cairo/src/air.rs index a85952d6ea..c06c4d0e5f 100644 --- a/provers/cairo/src/air.rs +++ b/provers/cairo/src/air.rs @@ -615,6 +615,7 @@ fn generate_range_check_permutation_argument_column( impl AIR for CairoAIR { type Field = Stark252PrimeField; + type FieldExtension = Stark252PrimeField; type RAPChallenges = CairoRAPChallenges; type PublicInputs = PublicInputs; diff --git a/provers/cairo/src/tests/integration_tests.rs b/provers/cairo/src/tests/integration_tests.rs index 0744c22314..5bcbe3b828 100644 --- a/provers/cairo/src/tests/integration_tests.rs +++ b/provers/cairo/src/tests/integration_tests.rs @@ -141,7 +141,7 @@ fn check_simple_cairo_trace_evaluates_to_zero() { let program_content = std::fs::read(cairo0_program_path("simple_program.json")).unwrap(); let (main_trace, public_input) = generate_prover_args(&program_content, CairoLayout::Plain).unwrap(); - let mut trace_polys = main_trace.compute_trace_polys(); + let mut trace_polys = main_trace.compute_trace_polys::(); let mut transcript = StoneProverTranscript::new(&[]); let proof_options = ProofOptions::default_test_options(); @@ -149,7 +149,7 @@ fn check_simple_cairo_trace_evaluates_to_zero() { let rap_challenges = cairo_air.build_rap_challenges(&mut transcript); let aux_trace = cairo_air.build_auxiliary_trace(&main_trace, &rap_challenges); - let aux_polys = aux_trace.compute_trace_polys(); + let aux_polys = aux_trace.compute_trace_polys::(); trace_polys.extend_from_slice(&aux_polys); diff --git a/provers/plonk/src/prover.rs b/provers/plonk/src/prover.rs index b351d1c382..77d4eb7f63 100644 --- a/provers/plonk/src/prover.rs +++ b/provers/plonk/src/prover.rs @@ -9,7 +9,10 @@ use crate::setup::{ new_strong_fiat_shamir_transcript, CommonPreprocessedInput, VerificationKey, Witness, }; use lambdaworks_crypto::commitments::traits::IsCommitmentScheme; -use lambdaworks_math::{field::element::FieldElement, polynomial::Polynomial}; +use lambdaworks_math::{ + field::element::FieldElement, + polynomial::{self, Polynomial}, +}; use lambdaworks_math::{field::traits::IsField, traits::ByteConversion}; /// Plonk proof. @@ -318,7 +321,7 @@ where .expect("xs and ys have equal length and xs are unique"); let z_h = Polynomial::new_monomial(FieldElement::one(), common_preprocessed_input.n) - - FieldElement::one(); + - FieldElement::::one(); let p_a = self.blind_polynomial(&p_a, &z_h, 2); let p_b = self.blind_polynomial(&p_b, &z_h, 2); let p_c = self.blind_polynomial(&p_c, &z_h, 2); @@ -366,7 +369,7 @@ where let p_z = Polynomial::interpolate_fft::(&coefficients) .expect("xs and ys have equal length and xs are unique"); let z_h = Polynomial::new_monomial(FieldElement::one(), common_preprocessed_input.n) - - FieldElement::one(); + - FieldElement::::one(); let p_z = self.blind_polynomial(&p_z, &z_h, 3); let z_1 = self.commitment_scheme.commit(&p_z); Round2Result { @@ -392,7 +395,7 @@ where let one = Polynomial::new_monomial(FieldElement::one(), 0); let p_x = &Polynomial::new_monomial(FieldElement::::one(), 1); - let zh = Polynomial::new_monomial(FieldElement::one(), cpi.n) - &one; + let zh = Polynomial::new_monomial(FieldElement::::one(), cpi.n) - &one; let z_x_omega_coefficients: Vec> = p_z .coefficients() @@ -503,7 +506,7 @@ where .collect(); let mut t = Polynomial::interpolate_offset_fft(&c, offset).unwrap(); - Polynomial::pad_with_zero_coefficients_to_length(&mut t, 3 * (&cpi.n + 2)); + polynomial::pad_with_zero_coefficients_to_length(&mut t, 3 * (&cpi.n + 2)); let p_t_lo = Polynomial::new(&t.coefficients[..&cpi.n + 2]); let p_t_mid = Polynomial::new(&t.coefficients[&cpi.n + 2..2 * (&cpi.n + 2)]); let p_t_hi = Polynomial::new(&t.coefficients[2 * (&cpi.n + 2)..3 * (&cpi.n + 2)]); @@ -573,7 +576,7 @@ where let l1_zeta = (&r4.zeta.pow(cpi.n as u64) - FieldElement::::one()) / (&r4.zeta - FieldElement::::one()) - / FieldElement::from(cpi.n as u64); + / FieldElement::::from(cpi.n as u64); let mut p_non_constant = &cpi.qm * &r4.a_zeta * &r4.b_zeta + &r4.a_zeta * &cpi.ql diff --git a/provers/stark/src/constraints/evaluator.rs b/provers/stark/src/constraints/evaluator.rs index 51926e098b..6850f53145 100644 --- a/provers/stark/src/constraints/evaluator.rs +++ b/provers/stark/src/constraints/evaluator.rs @@ -1,7 +1,10 @@ use itertools::Itertools; use lambdaworks_math::{ fft::{cpu::roots_of_unity::get_powers_of_primitive_root_coset, errors::FFTError}, - field::{element::FieldElement, traits::IsFFTField}, + field::{ + element::FieldElement, + traits::{IsFFTField, IsField, IsSubFieldOf}, + }, polynomial::Polynomial, traits::Serializable, }; @@ -19,11 +22,11 @@ use crate::trace::TraceTable; use crate::traits::AIR; use crate::{frame::Frame, prover::evaluate_polynomial_on_lde_domain}; -pub struct ConstraintEvaluator { - boundary_constraints: BoundaryConstraints, +pub struct ConstraintEvaluator { + boundary_constraints: BoundaryConstraints, } -impl ConstraintEvaluator { - pub fn new>(air: &A, rap_challenges: &A::RAPChallenges) -> Self { +impl ConstraintEvaluator { + pub fn new(air: &A, rap_challenges: &A::RAPChallenges) -> Self { let boundary_constraints = air.boundary_constraints(rap_challenges); Self { @@ -31,23 +34,24 @@ impl ConstraintEvaluator { } } - pub fn evaluate>( + pub fn evaluate( &self, air: &A, - lde_trace: &TraceTable, - domain: &Domain, - transition_coefficients: &[FieldElement], - boundary_coefficients: &[FieldElement], + lde_trace: &TraceTable, + domain: &Domain, + transition_coefficients: &[FieldElement], + boundary_coefficients: &[FieldElement], rap_challenges: &A::RAPChallenges, - ) -> Vec> + ) -> Vec> where - FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, A: Send + Sync, A::RAPChallenges: Send + Sync, { let boundary_constraints = &self.boundary_constraints; let number_of_b_constraints = boundary_constraints.constraints.len(); - let boundary_zerofiers_inverse_evaluations: Vec>> = + let boundary_zerofiers_inverse_evaluations: Vec>> = boundary_constraints .constraints .iter() @@ -57,16 +61,16 @@ impl ConstraintEvaluator { .lde_roots_of_unity_coset .iter() .map(|v| v.clone() - point) - .collect::>>(); + .collect::>>(); FieldElement::inplace_batch_inverse(&mut evals).unwrap(); evals }) - .collect::>>>(); + .collect::>>>(); let trace_length = air.trace_length(); #[cfg(all(debug_assertions, not(feature = "parallel")))] - let boundary_polys: Vec>> = Vec::new(); + let boundary_polys: Vec>> = Vec::new(); let lde_periodic_columns = air .get_periodic_column_polynomials() @@ -79,7 +83,7 @@ impl ConstraintEvaluator { &domain.coset_offset, ) }) - .collect::>>, FFTError>>() + .collect::>>, FFTError>>() .unwrap(); let n_col = lde_trace.n_cols(); @@ -96,10 +100,10 @@ impl ConstraintEvaluator { .skip(col) .step_by(n_col) .take(n_elem) - .map(|v| v - &constraint.value) - .collect::>>() + .map(|v| -&constraint.value + v) + .collect::>>() }) - .collect::>>>(); + .collect::>>>(); #[cfg(feature = "parallel")] let boundary_eval_iter = (0..domain.lde_roots_of_unity_coset.len()).into_par_iter(); @@ -117,7 +121,7 @@ impl ConstraintEvaluator { * &boundary_polys_evaluations[constraint_index][domain_index] }) }) - .collect::>>(); + .collect::>>(); #[cfg(all(debug_assertions, not(feature = "parallel")))] let boundary_zerofiers = Vec::new(); @@ -138,10 +142,10 @@ impl ConstraintEvaluator { let blowup_factor_order = u64::from(blowup_factor.trailing_zeros()); - let offset = FieldElement::::from(air.context().proof_options.coset_offset); + let offset = FieldElement::::from(air.context().proof_options.coset_offset); let offset_pow = offset.pow(trace_length); - let one = FieldElement::::one(); - let mut zerofier_evaluations = get_powers_of_primitive_root_coset( + let one = FieldElement::one(); + let mut zerofier_evaluations = get_powers_of_primitive_root_coset::( blowup_factor_order, blowup_factor as usize, &offset_pow, @@ -187,7 +191,7 @@ impl ConstraintEvaluator { let periodic_values: Vec<_> = lde_periodic_columns .iter() - .map(|col| col[i].clone()) + .map(|col| col[i].clone().to_extension()) .collect(); // Compute all the transition constraints at this @@ -212,14 +216,14 @@ impl ConstraintEvaluator { // If there's no exemption, then // the zerofier remains as it was. if *exemption == 0 { - acc + zerofier * beta * eval + acc + eval * zerofier * beta } else { //TODO: change how exemptions are indexed! if num_exemptions == 1 { - acc + zerofier - * beta - * eval + acc + eval * &transition_exemptions_evaluations[0][i] + * beta + * zerofier } else { // This case is not used for Cairo Programs, it can be improved in the future let vector = air @@ -235,10 +239,10 @@ impl ConstraintEvaluator { .position(|elem_2| elem_2 == exemption) .expect("is there"); - acc + zerofier - * beta - * eval + acc + eval * &transition_exemptions_evaluations[index][i] + * zerofier + * beta } } }); @@ -246,18 +250,19 @@ impl ConstraintEvaluator { acc_transition + boundary }) - .collect::>>(); + .collect::>>(); evaluations_t } } -fn evaluate_transition_exemptions( - transition_exemptions: Vec>>, +fn evaluate_transition_exemptions, E: IsField>( + transition_exemptions: Vec>>, domain: &Domain, -) -> Vec>> +) -> Vec>> where FieldElement: Send + Sync + Serializable, + FieldElement: Send + Sync + Serializable, Polynomial>: Send + Sync, { #[cfg(feature = "parallel")] diff --git a/provers/stark/src/debug.rs b/provers/stark/src/debug.rs index bede4a7a31..f9d264298e 100644 --- a/provers/stark/src/debug.rs +++ b/provers/stark/src/debug.rs @@ -4,15 +4,18 @@ use crate::trace::TraceTable; use super::domain::Domain; use super::traits::AIR; use lambdaworks_math::{ - field::{element::FieldElement, traits::IsFFTField}, + field::{ + element::FieldElement, + traits::{IsFFTField, IsField}, + }, polynomial::Polynomial, }; use log::{error, info}; /// Validates that the trace is valid with respect to the supplied AIR constraints -pub fn validate_trace>( +pub fn validate_trace( air: &A, - trace_polys: &[Polynomial>], + trace_polys: &[Polynomial>], domain: &Domain, rap_challenges: &A::RAPChallenges, ) -> bool { @@ -22,7 +25,8 @@ pub fn validate_trace>( let trace_columns: Vec<_> = trace_polys .iter() .map(|poly| { - Polynomial::evaluate_fft::(poly, 1, Some(domain.interpolation_domain_size)).unwrap() + Polynomial::evaluate_fft::(poly, 1, Some(domain.interpolation_domain_size)) + .unwrap() }) .collect(); @@ -32,7 +36,8 @@ pub fn validate_trace>( .get_periodic_column_polynomials() .iter() .map(|poly| { - Polynomial::evaluate_fft::(poly, 1, Some(domain.interpolation_domain_size)).unwrap() + Polynomial::evaluate_fft::(poly, 1, Some(domain.interpolation_domain_size)) + .unwrap() }) .collect(); @@ -46,7 +51,7 @@ pub fn validate_trace>( let boundary_value = constraint.value.clone(); let trace_value = trace.get(step, col); - if &boundary_value != trace_value { + if &boundary_value.clone().to_extension() != trace_value { ret = false; error!("Boundary constraint inconsistency - Expected value {:?} in step {} and column {}, found: {:?}", boundary_value, step, col, trace_value); } @@ -78,7 +83,7 @@ pub fn validate_trace>( evaluations.iter().enumerate().for_each(|(i, eval)| { // Check that all the transition constraint evaluations of the trace are zero. // We don't take into account the transition exemptions. - if step < exemption_steps[i] && eval != &FieldElement::::zero() { + if step < exemption_steps[i] && eval != &FieldElement::zero() { ret = false; error!( "Inconsistent evaluation of transition {} in step {} - expected 0, got {:?}", @@ -111,7 +116,7 @@ pub fn check_boundary_polys_divisibility( /// array, returning a true when valid and false when not. pub fn validate_2d_structure(data: &[FieldElement], width: usize) -> bool where - F: IsFFTField, + F: IsField, { let rows: Vec>> = data.chunks(width).map(|c| c.to_vec()).collect(); rows.iter().all(|r| r.len() == rows[0].len()) diff --git a/provers/stark/src/examples/dummy_air.rs b/provers/stark/src/examples/dummy_air.rs index 9986e49657..d273533314 100644 --- a/provers/stark/src/examples/dummy_air.rs +++ b/provers/stark/src/examples/dummy_air.rs @@ -21,6 +21,7 @@ pub struct DummyAIR { impl AIR for DummyAIR { type Field = Stark252PrimeField; + type FieldExtension = Stark252PrimeField; type RAPChallenges = (); type PublicInputs = (); @@ -55,7 +56,7 @@ impl AIR for DummyAIR { fn build_rap_challenges( &self, - _transcript: &mut impl IsStarkTranscript, + _transcript: &mut impl IsStarkTranscript, ) -> Self::RAPChallenges { } fn compute_transition( diff --git a/provers/stark/src/examples/fibonacci_2_cols_shifted.rs b/provers/stark/src/examples/fibonacci_2_cols_shifted.rs index 5b24fdf0cd..c50e6b937b 100644 --- a/provers/stark/src/examples/fibonacci_2_cols_shifted.rs +++ b/provers/stark/src/examples/fibonacci_2_cols_shifted.rs @@ -53,6 +53,7 @@ where F: IsFFTField, { type Field = F; + type FieldExtension = F; type RAPChallenges = (); type PublicInputs = PublicInputs; @@ -88,7 +89,7 @@ where fn build_rap_challenges( &self, - _transcript: &mut impl IsStarkTranscript, + _transcript: &mut impl IsStarkTranscript, ) -> Self::RAPChallenges { } diff --git a/provers/stark/src/examples/fibonacci_2_columns.rs b/provers/stark/src/examples/fibonacci_2_columns.rs index 1d4b7712d6..0534fededd 100644 --- a/provers/stark/src/examples/fibonacci_2_columns.rs +++ b/provers/stark/src/examples/fibonacci_2_columns.rs @@ -29,6 +29,7 @@ where F: IsFFTField, { type Field = F; + type FieldExtension = F; type RAPChallenges = (); type PublicInputs = FibonacciPublicInputs; @@ -64,7 +65,7 @@ where fn build_rap_challenges( &self, - _transcript: &mut impl IsStarkTranscript, + _transcript: &mut impl IsStarkTranscript, ) -> Self::RAPChallenges { } diff --git a/provers/stark/src/examples/fibonacci_rap.rs b/provers/stark/src/examples/fibonacci_rap.rs index 643966725c..c2dbb6738c 100644 --- a/provers/stark/src/examples/fibonacci_rap.rs +++ b/provers/stark/src/examples/fibonacci_rap.rs @@ -42,6 +42,7 @@ where FieldElement: ByteConversion, { type Field = F; + type FieldExtension = F; type RAPChallenges = FieldElement; type PublicInputs = FibonacciRAPPublicInputs; diff --git a/provers/stark/src/examples/quadratic_air.rs b/provers/stark/src/examples/quadratic_air.rs index 5d8e70f244..5dff7bd580 100644 --- a/provers/stark/src/examples/quadratic_air.rs +++ b/provers/stark/src/examples/quadratic_air.rs @@ -33,6 +33,7 @@ where F: IsFFTField, { type Field = F; + type FieldExtension = F; type RAPChallenges = (); type PublicInputs = QuadraticPublicInputs; @@ -68,7 +69,7 @@ where fn build_rap_challenges( &self, - _transcript: &mut impl IsStarkTranscript, + _transcript: &mut impl IsStarkTranscript, ) -> Self::RAPChallenges { } diff --git a/provers/stark/src/examples/simple_fibonacci.rs b/provers/stark/src/examples/simple_fibonacci.rs index c90e8296cf..eb6bcde6a8 100644 --- a/provers/stark/src/examples/simple_fibonacci.rs +++ b/provers/stark/src/examples/simple_fibonacci.rs @@ -34,6 +34,7 @@ where F: IsFFTField, { type Field = F; + type FieldExtension = F; type RAPChallenges = (); type PublicInputs = FibonacciPublicInputs; @@ -73,7 +74,7 @@ where fn build_rap_challenges( &self, - _transcript: &mut impl IsStarkTranscript, + _transcript: &mut impl IsStarkTranscript, ) -> Self::RAPChallenges { } diff --git a/provers/stark/src/examples/simple_periodic_cols.rs b/provers/stark/src/examples/simple_periodic_cols.rs index 9347dd9028..5db81a6f10 100644 --- a/provers/stark/src/examples/simple_periodic_cols.rs +++ b/provers/stark/src/examples/simple_periodic_cols.rs @@ -48,6 +48,7 @@ where F: IsFFTField, { type Field = F; + type FieldExtension = F; type RAPChallenges = (); type PublicInputs = SimplePeriodicPublicInputs; @@ -87,7 +88,7 @@ where fn build_rap_challenges( &self, - _transcript: &mut impl IsStarkTranscript, + _transcript: &mut impl IsStarkTranscript, ) -> Self::RAPChallenges { } diff --git a/provers/stark/src/frame.rs b/provers/stark/src/frame.rs index 6b3442c401..cca79d0441 100644 --- a/provers/stark/src/frame.rs +++ b/provers/stark/src/frame.rs @@ -1,16 +1,16 @@ use super::trace::TraceTable; use crate::trace::StepView; -use lambdaworks_math::field::traits::IsFFTField; +use lambdaworks_math::field::traits::IsField; /// A frame represents a collection of trace steps. /// The collected steps are all the necessary steps for /// all transition costraints over a trace to be evaluated. #[derive(Clone, Debug, PartialEq)] -pub struct Frame<'t, F: IsFFTField> { +pub struct Frame<'t, F: IsField> { steps: Vec>, } -impl<'t, F: IsFFTField> Frame<'t, F> { +impl<'t, F: IsField> Frame<'t, F> { pub fn new(steps: Vec>) -> Self { Self { steps } } diff --git a/provers/stark/src/fri/fri_commitment.rs b/provers/stark/src/fri/fri_commitment.rs index b39f42f3f7..83f24cbadd 100644 --- a/provers/stark/src/fri/fri_commitment.rs +++ b/provers/stark/src/fri/fri_commitment.rs @@ -1,9 +1,6 @@ use lambdaworks_crypto::merkle_tree::{merkle::MerkleTree, traits::IsMerkleTreeBackend}; use lambdaworks_math::{ - field::{ - element::FieldElement, - traits::{IsFFTField, IsField}, - }, + field::{element::FieldElement, traits::IsField}, traits::Serializable, }; @@ -22,7 +19,7 @@ where impl FriLayer where - F: IsField + IsFFTField, + F: IsField, FieldElement: Serializable, B: IsMerkleTreeBackend, { diff --git a/provers/stark/src/fri/fri_functions.rs b/provers/stark/src/fri/fri_functions.rs index 500fcecf9d..33e8a8b431 100644 --- a/provers/stark/src/fri/fri_functions.rs +++ b/provers/stark/src/fri/fri_functions.rs @@ -1,5 +1,8 @@ use super::Polynomial; -use lambdaworks_math::field::{element::FieldElement, traits::IsField}; +use lambdaworks_math::{ + field::{element::FieldElement, traits::IsField}, + polynomial, +}; pub fn fold_polynomial( poly: &Polynomial>, @@ -19,7 +22,7 @@ where .map(|v| (v.clone()) * beta) .collect(); - let (even_poly, odd_poly) = Polynomial::pad_with_zero_coefficients( + let (even_poly, odd_poly) = polynomial::pad_with_zero_coefficients( &Polynomial::new(&even_coef), &Polynomial::new(&odd_coef_mul_beta), ); diff --git a/provers/stark/src/fri/mod.rs b/provers/stark/src/fri/mod.rs index 624e16edf9..548adc836d 100644 --- a/provers/stark/src/fri/mod.rs +++ b/provers/stark/src/fri/mod.rs @@ -2,9 +2,11 @@ pub mod fri_commitment; pub mod fri_decommit; mod fri_functions; -use lambdaworks_math::fft::cpu::bit_reversing::in_place_bit_reverse_permute; -use lambdaworks_math::field::traits::IsFFTField; +use lambdaworks_math::field::traits::{IsFFTField, IsField}; use lambdaworks_math::traits::Serializable; +use lambdaworks_math::{ + fft::cpu::bit_reversing::in_place_bit_reverse_permute, field::traits::IsSubFieldOf, +}; pub use lambdaworks_math::{ field::{element::FieldElement, fields::u64_prime_field::U64PrimeField}, polynomial::Polynomial, @@ -17,23 +19,24 @@ use self::fri_commitment::FriLayer; use self::fri_decommit::FriDecommitment; use self::fri_functions::fold_polynomial; -pub fn commit_phase( +pub fn commit_phase, E: IsField>( number_layers: usize, - p_0: Polynomial>, - transcript: &mut impl IsStarkTranscript, + p_0: Polynomial>, + transcript: &mut impl IsStarkTranscript, coset_offset: &FieldElement, domain_size: usize, ) -> ( - FieldElement, - Vec>>, + FieldElement, + Vec>>, ) where FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { let mut domain_size = domain_size; let mut fri_layer_list = Vec::with_capacity(number_layers); - let mut current_layer: FriLayer>; + let mut current_layer: FriLayer>; let mut current_poly = p_0; let mut coset_offset = coset_offset.clone(); @@ -45,7 +48,7 @@ where domain_size /= 2; // Compute layer polynomial and domain - current_poly = fold_polynomial(¤t_poly, &zeta) * FieldElement::from(2); + current_poly = FieldElement::::from(2) * fold_polynomial(¤t_poly, &zeta); current_layer = new_fri_layer(¤t_poly, &coset_offset, domain_size); let new_data = ¤t_layer.merkle_tree.root; fri_layer_list.push(current_layer.clone()); // TODO: remove this clone @@ -57,7 +60,7 @@ where // <<<< Receive challenge: πœβ‚™β‚‹β‚ let zeta = transcript.sample_field_element(); - let last_poly = fold_polynomial(¤t_poly, &zeta) * FieldElement::from(2); + let last_poly = FieldElement::::from(2) * fold_polynomial(¤t_poly, &zeta); let last_value = last_poly .coefficients() @@ -71,7 +74,7 @@ where (last_value, fri_layer_list) } -pub fn query_phase( +pub fn query_phase( fri_layers: &Vec>>, iotas: &[usize], ) -> Vec> @@ -109,14 +112,14 @@ where } } -pub fn new_fri_layer( - poly: &Polynomial>, +pub fn new_fri_layer, E: IsField>( + poly: &Polynomial>, coset_offset: &FieldElement, domain_size: usize, -) -> crate::fri::fri_commitment::FriLayer> +) -> crate::fri::fri_commitment::FriLayer> where - F: IsFFTField, FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { let mut evaluation = Polynomial::evaluate_offset_fft(poly, 1, Some(domain_size), coset_offset).unwrap(); // TODO: return error @@ -130,5 +133,10 @@ where let merkle_tree = BatchedMerkleTree::build(&to_commit); - FriLayer::new(&evaluation, merkle_tree, coset_offset.clone(), domain_size) + FriLayer::new( + &evaluation, + merkle_tree, + coset_offset.clone().to_extension(), + domain_size, + ) } diff --git a/provers/stark/src/proof/stark.rs b/provers/stark/src/proof/stark.rs index 318f337b26..4a0a57ddb0 100644 --- a/provers/stark/src/proof/stark.rs +++ b/provers/stark/src/proof/stark.rs @@ -4,7 +4,7 @@ use lambdaworks_crypto::merkle_tree::proof::Proof; use lambdaworks_math::{ field::{ element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - traits::IsFFTField, + traits::IsField, }, traits::Serializable, }; @@ -22,7 +22,7 @@ use crate::{ use super::options::ProofOptions; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub struct DeepPolynomialOpening { +pub struct DeepPolynomialOpening { pub lde_composition_poly_proof: Proof, pub lde_composition_poly_parts_evaluation: Vec>, pub lde_trace_merkle_proofs: Vec>, @@ -32,7 +32,7 @@ pub struct DeepPolynomialOpening { pub type DeepPolynomialOpenings = Vec>; #[derive(Debug, serde::Serialize, serde::Deserialize)] -pub struct StarkProof { +pub struct StarkProof { // Length of the execution trace pub trace_length: usize, // Commitments of the trace columns @@ -69,7 +69,7 @@ impl StoneCompatibleSerializer { options: &ProofOptions, ) -> Vec where - A: AIR, + A: AIR, A::PublicInputs: Serializable, { let mut output = Vec::new(); @@ -409,7 +409,7 @@ impl StoneCompatibleSerializer { proof_options: &ProofOptions, ) -> Vec where - A: AIR, + A: AIR, A::PublicInputs: Serializable, { let mut transcript = StoneProverTranscript::new(&public_inputs.serialize()); diff --git a/provers/stark/src/prover.rs b/provers/stark/src/prover.rs index 86dcaa99e8..ac275fd761 100644 --- a/provers/stark/src/prover.rs +++ b/provers/stark/src/prover.rs @@ -5,6 +5,7 @@ use lambdaworks_crypto::merkle_tree::proof::Proof; use lambdaworks_math::fft::cpu::bit_reversing::{in_place_bit_reverse_permute, reverse_index}; use lambdaworks_math::fft::errors::FFTError; use lambdaworks_math::field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField; +use lambdaworks_math::field::traits::{IsField, IsSubFieldOf}; use lambdaworks_math::traits::Serializable; use lambdaworks_math::{ field::{element::FieldElement, traits::IsFFTField}, @@ -36,6 +37,7 @@ pub struct Prover; impl IsStarkProver for Prover { type Field = Stark252PrimeField; + type FieldExtension = Stark252PrimeField; } #[derive(Debug)] @@ -43,22 +45,21 @@ pub enum ProvingError { WrongParameter(String), } -pub struct Round1 +pub struct Round1 where - F: IsFFTField, - A: AIR, - FieldElement: Serializable + Sync + Send, + A: AIR, + FieldElement: Serializable + Sync + Send, { - pub(crate) trace_polys: Vec>>, - pub(crate) lde_trace: TraceTable, - pub(crate) lde_trace_merkle_trees: Vec>, + pub(crate) trace_polys: Vec>>, + pub(crate) lde_trace: TraceTable, + pub(crate) lde_trace_merkle_trees: Vec>, pub(crate) lde_trace_merkle_roots: Vec, pub(crate) rap_challenges: A::RAPChallenges, } pub struct Round2 where - F: IsFFTField, + F: IsField, FieldElement: Serializable + Sync + Send, { pub(crate) composition_poly_parts: Vec>>, @@ -67,12 +68,12 @@ where pub(crate) composition_poly_root: Commitment, } -pub struct Round3 { +pub struct Round3 { trace_ood_evaluations: Vec>>, composition_poly_parts_ood_evaluation: Vec>, } -pub struct Round4 { +pub struct Round4 { fri_last_value: FieldElement, fri_layers_merkle_roots: Vec, deep_poly_openings: DeepPolynomialOpenings, @@ -80,14 +81,15 @@ pub struct Round4 { query_list: Vec>, nonce: Option, } -pub fn evaluate_polynomial_on_lde_domain( - p: &Polynomial>, +pub fn evaluate_polynomial_on_lde_domain( + p: &Polynomial>, blowup_factor: usize, domain_size: usize, offset: &FieldElement, -) -> Result>, FFTError> +) -> Result>, FFTError> where - F: IsFFTField, + F: IsFFTField + IsSubFieldOf, + E: IsField, { let evaluations = Polynomial::evaluate_offset_fft(p, blowup_factor, Some(domain_size), offset)?; let step = evaluations.len() / (domain_size * blowup_factor); @@ -98,35 +100,38 @@ where } pub trait IsStarkProver { - type Field: IsFFTField; + type Field: IsFFTField + IsSubFieldOf; + type FieldExtension: IsField; fn batch_commit( - vectors: &[Vec>], - ) -> (BatchedMerkleTree, Commitment) + vectors: &[Vec>], + ) -> (BatchedMerkleTree, Commitment) where FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { - let tree = BatchedMerkleTree::::build(vectors); + let tree = BatchedMerkleTree::::build(vectors); let commitment = tree.root; (tree, commitment) } #[allow(clippy::type_complexity)] fn interpolate_and_commit( - trace: &TraceTable, + trace: &TraceTable, domain: &Domain, - transcript: &mut impl IsStarkTranscript, + transcript: &mut impl IsStarkTranscript, ) -> ( - Vec>>, - Vec>>, - BatchedMerkleTree, + Vec>>, + Vec>>, + BatchedMerkleTree, Commitment, ) where A: AIR, FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, { - let trace_polys = trace.compute_trace_polys(); + let trace_polys = trace.compute_trace_polys::(); // Evaluate those polynomials t_j on the large domain D_LDE. let lde_trace_evaluations = Self::compute_lde_trace_evaluations(&trace_polys, domain); @@ -153,11 +158,12 @@ pub trait IsStarkProver { } fn compute_lde_trace_evaluations( - trace_polys: &[Polynomial>], + trace_polys: &[Polynomial>], domain: &Domain, - ) -> Vec>> + ) -> Vec>> where FieldElement: Send + Sync, + FieldElement: Send + Sync, { #[cfg(not(feature = "parallel"))] let trace_polys_iter = trace_polys.iter(); @@ -173,19 +179,20 @@ pub trait IsStarkProver { &domain.coset_offset, ) }) - .collect::>>, FFTError>>() + .collect::>>, FFTError>>() .unwrap() } fn round_1_randomized_air_with_preprocessing( air: &A, - main_trace: &TraceTable, + main_trace: &TraceTable, domain: &Domain, - transcript: &mut impl IsStarkTranscript, - ) -> Result, ProvingError> + transcript: &mut impl IsStarkTranscript, + ) -> Result, ProvingError> where - A: AIR, + A: AIR, FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, { let (mut trace_polys, mut evaluations, main_merkle_tree, main_merkle_root) = Self::interpolate_and_commit::(main_trace, domain, transcript); @@ -218,10 +225,11 @@ pub trait IsStarkProver { } fn commit_composition_polynomial( - lde_composition_poly_parts_evaluations: &[Vec>], - ) -> (BatchedMerkleTree, Commitment) + lde_composition_poly_parts_evaluations: &[Vec>], + ) -> (BatchedMerkleTree, Commitment) where FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { // TODO: Remove clones let mut lde_composition_poly_evaluations = Vec::new(); @@ -248,14 +256,15 @@ pub trait IsStarkProver { fn round_2_compute_composition_polynomial( air: &A, domain: &Domain, - round_1_result: &Round1, - transition_coefficients: &[FieldElement], - boundary_coefficients: &[FieldElement], - ) -> Round2 + round_1_result: &Round1, + transition_coefficients: &[FieldElement], + boundary_coefficients: &[FieldElement], + ) -> Round2 where - A: AIR + Send + Sync, + A: AIR + Send + Sync, A::RAPChallenges: Send + Sync, FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, { // Create evaluation table let evaluator = ConstraintEvaluator::new(air, &round_1_result.rap_challenges); @@ -301,15 +310,17 @@ pub trait IsStarkProver { } } - fn round_3_evaluate_polynomials_in_out_of_domain_element>( + fn round_3_evaluate_polynomials_in_out_of_domain_element( air: &A, domain: &Domain, - round_1_result: &Round1, - round_2_result: &Round2, - z: &FieldElement, - ) -> Round3 + round_1_result: &Round1, + round_2_result: &Round2, + z: &FieldElement, + ) -> Round3 where FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, + A: AIR, { let z_power = z.pow(round_2_result.composition_poly_parts.len()); @@ -341,17 +352,19 @@ pub trait IsStarkProver { } } - fn round_4_compute_and_run_fri_on_the_deep_composition_polynomial>( + fn round_4_compute_and_run_fri_on_the_deep_composition_polynomial( air: &A, domain: &Domain, - round_1_result: &Round1, - round_2_result: &Round2, - round_3_result: &Round3, - z: &FieldElement, - transcript: &mut impl IsStarkTranscript, - ) -> Round4 + round_1_result: &Round1, + round_2_result: &Round2, + round_3_result: &Round3, + z: &FieldElement, + transcript: &mut impl IsStarkTranscript, + ) -> Round4 where FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, + A: AIR, { let coset_offset_u64 = air.context().proof_options.coset_offset; let coset_offset = FieldElement::::from(coset_offset_u64); @@ -388,7 +401,7 @@ pub trait IsStarkProver { let domain_size = domain.lde_roots_of_unity_coset.len(); // FRI commit and query phases - let (fri_last_value, fri_layers) = fri::commit_phase( + let (fri_last_value, fri_layers) = fri::commit_phase::( domain.root_order as usize, deep_composition_poly, transcript, @@ -431,7 +444,7 @@ pub trait IsStarkProver { fn sample_query_indexes( number_of_queries: usize, domain: &Domain, - transcript: &mut impl IsStarkTranscript, + transcript: &mut impl IsStarkTranscript, ) -> Vec { let domain_size = domain.lde_roots_of_unity_coset.len() as u64; (0..number_of_queries) @@ -445,17 +458,18 @@ pub trait IsStarkProver { #[allow(clippy::too_many_arguments)] fn compute_deep_composition_poly( air: &A, - trace_polys: &[Polynomial>], - round_2_result: &Round2, - round_3_result: &Round3, - z: &FieldElement, + trace_polys: &[Polynomial>], + round_2_result: &Round2, + round_3_result: &Round3, + z: &FieldElement, primitive_root: &FieldElement, - composition_poly_gammas: &[FieldElement], - trace_terms_gammas: &[FieldElement], - ) -> Polynomial> + composition_poly_gammas: &[FieldElement], + trace_terms_gammas: &[FieldElement], + ) -> Polynomial> where - A: AIR, + A: AIR, FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, { let z_power = z.pow(round_2_result.composition_poly_parts.len()); @@ -523,16 +537,20 @@ pub trait IsStarkProver { } fn compute_trace_term( - trace_terms: &Polynomial>, - (i, t_j): (usize, &Polynomial>), + trace_terms: &Polynomial>, + (i, t_j): (usize, &Polynomial>), trace_frame_length: usize, - trace_terms_gammas: &[FieldElement], - trace_frame_evaluations: &[Vec>], + trace_terms_gammas: &[FieldElement], + trace_frame_evaluations: &[Vec>], transition_offsets: &[usize], - (z, primitive_root): (&FieldElement, &FieldElement), - ) -> Polynomial> + (z, primitive_root): ( + &FieldElement, + &FieldElement, + ), + ) -> Polynomial> where FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, { let i_times_trace_frame_evaluation = i * trace_frame_length; let iter_trace_gammas = trace_terms_gammas @@ -548,7 +566,7 @@ pub trait IsStarkProver { // @@@ we can avoid this clone let t_j_z = &eval[i]; // @@@ this can be pre-computed - let z_shifted = z * primitive_root.pow(*offset); + let z_shifted = primitive_root.pow(*offset) * z; let mut poly = t_j - t_j_z; poly.ruffini_division_inplace(&z_shifted); trace_agg + poly * trace_gamma @@ -559,12 +577,13 @@ pub trait IsStarkProver { } fn open_composition_poly( - composition_poly_merkle_tree: &BatchedMerkleTree, - lde_composition_poly_evaluations: &[Vec>], + composition_poly_merkle_tree: &BatchedMerkleTree, + lde_composition_poly_evaluations: &[Vec>], index: usize, - ) -> (Proof, Vec>) + ) -> (Proof, Vec>) where FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { let proof = composition_poly_merkle_tree .get_proof_by_pos(index) @@ -585,12 +604,16 @@ pub trait IsStarkProver { fn open_trace_polys( domain: &Domain, - lde_trace_merkle_trees: &[BatchedMerkleTree], - lde_trace: &TraceTable, + lde_trace_merkle_trees: &[BatchedMerkleTree], + lde_trace: &TraceTable, index: usize, - ) -> (Vec>, Vec>) + ) -> ( + Vec>, + Vec>, + ) where FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { let domain_size = domain.lde_roots_of_unity_coset.len(); let lde_trace_evaluations = lde_trace @@ -612,17 +635,19 @@ pub trait IsStarkProver { /// Open the deep composition polynomial on a list of indexes /// and their symmetric elements. - fn open_deep_composition_poly>( + fn open_deep_composition_poly( domain: &Domain, - round_1_result: &Round1, - round_2_result: &Round2, + round_1_result: &Round1, + round_2_result: &Round2, indexes_to_open: &[usize], ) -> ( - DeepPolynomialOpenings, - DeepPolynomialOpenings, + DeepPolynomialOpenings, + DeepPolynomialOpenings, ) where - FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, + A: AIR, { let mut openings = Vec::new(); let mut openings_symmetric = Vec::new(); @@ -680,12 +705,13 @@ pub trait IsStarkProver { main_trace: &TraceTable, pub_inputs: &A::PublicInputs, proof_options: &ProofOptions, - mut transcript: impl IsStarkTranscript, - ) -> Result, ProvingError> + mut transcript: impl IsStarkTranscript, + ) -> Result, ProvingError> where - A: AIR + Send + Sync, + A: AIR + Send + Sync, A::RAPChallenges: Send + Sync, FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, { info!("Started proof generation..."); #[cfg(feature = "instruments")] @@ -693,6 +719,16 @@ pub trait IsStarkProver { #[cfg(feature = "instruments")] let timer0 = Instant::now(); + let main_trace = TraceTable::::from_columns( + main_trace + .columns() + .clone() + .into_iter() + .map(|col| col.into_iter().map(|x| x.to_extension()).collect()) + .collect(), + A::STEP_SIZE, + ); + let air = A::new(main_trace.n_rows(), pub_inputs, proof_options); let domain = Domain::new(&air); @@ -712,7 +748,7 @@ pub trait IsStarkProver { let round_1_result = Self::round_1_randomized_air_with_preprocessing::( &air, - main_trace, + &main_trace, &domain, &mut transcript, )?; @@ -864,7 +900,7 @@ pub trait IsStarkProver { round_1_result.trace_polys.len(), ); - Ok(StarkProof { + Ok(StarkProof:: { // [tβ±Ό] lde_trace_merkle_roots: round_1_result.lde_trace_merkle_roots, // tβ±Ό(zgᡏ) @@ -972,7 +1008,7 @@ mod tests { fn test_evaluate_polynomial_on_lde_domain_on_trace_polys() { let trace = simple_fibonacci::fibonacci_trace([Felt252::from(1), Felt252::from(1)], 8); let trace_length = trace.n_rows(); - let trace_polys = trace.compute_trace_polys(); + let trace_polys = trace.compute_trace_polys::(); let coset_offset = Felt252::from(3); let blowup_factor: usize = 2; let domain_size = 8; diff --git a/provers/stark/src/table.rs b/provers/stark/src/table.rs index c285740892..ab9a102bcc 100644 --- a/provers/stark/src/table.rs +++ b/provers/stark/src/table.rs @@ -1,4 +1,4 @@ -use lambdaworks_math::field::{element::FieldElement, traits::IsFFTField}; +use lambdaworks_math::field::{element::FieldElement, traits::IsField}; use crate::{frame::Frame, trace::StepView}; @@ -8,13 +8,13 @@ use crate::{frame::Frame, trace::StepView}; /// Since this struct is a representation of a two-dimensional table, all rows should have the same /// length. #[derive(Clone, Default, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -pub struct Table { +pub struct Table { pub data: Vec>, pub width: usize, pub height: usize, } -impl<'t, F: IsFFTField> Table { +impl<'t, F: IsField> Table { /// Crates a new Table instance from a one-dimensional array in row major order /// and the intended width of the table. pub fn new(data: Vec>, width: usize) -> Self { @@ -141,14 +141,14 @@ impl<'t, F: IsFFTField> Table { /// A view of a contiguos subset of rows of a table. #[derive(Clone, Debug, PartialEq, Eq)] -pub struct TableView<'t, F: IsFFTField> { +pub struct TableView<'t, F: IsField> { pub data: &'t [FieldElement], pub table_row_idx: usize, pub width: usize, pub height: usize, } -impl<'t, F: IsFFTField> TableView<'t, F> { +impl<'t, F: IsField> TableView<'t, F> { pub fn new( data: &'t [FieldElement], table_row_idx: usize, diff --git a/provers/stark/src/trace.rs b/provers/stark/src/trace.rs index 2a05c42ed8..a3cabafd00 100644 --- a/provers/stark/src/trace.rs +++ b/provers/stark/src/trace.rs @@ -1,5 +1,6 @@ use crate::table::{Table, TableView}; use lambdaworks_math::fft::errors::FFTError; +use lambdaworks_math::field::traits::{IsField, IsSubFieldOf}; use lambdaworks_math::{ field::{element::FieldElement, traits::IsFFTField}, polynomial::Polynomial, @@ -15,12 +16,12 @@ use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; /// STARK protocol, such as the step size (number of consecutive rows of the table) /// of the computation being proven. #[derive(Clone, Default, Debug, PartialEq, Eq)] -pub struct TraceTable { +pub struct TraceTable { pub table: Table, pub step_size: usize, } -impl<'t, F: IsFFTField> TraceTable { +impl<'t, F: IsField> TraceTable { pub fn new(data: Vec>, n_columns: usize, step_size: usize) -> Self { let table = Table::new(data, n_columns); Self { table, step_size } @@ -110,7 +111,9 @@ impl<'t, F: IsFFTField> TraceTable { self.table.get(row, col) } - pub fn compute_trace_polys(&self) -> Vec>> + pub fn compute_trace_polys>( + &self, + ) -> Vec>> where FieldElement: Send + Sync, { @@ -120,7 +123,7 @@ impl<'t, F: IsFFTField> TraceTable { #[cfg(not(feature = "parallel"))] let iter = columns.iter(); - iter.map(|col| Polynomial::interpolate_fft::(col)) + iter.map(|col| Polynomial::interpolate_fft::(col)) .collect::>>, FFTError>>() .unwrap() } @@ -167,12 +170,12 @@ impl<'t, F: IsFFTField> TraceTable { /// access the steps in a trace, in order to grab elements to calculate /// constraint evaluations. #[derive(Debug, Clone, PartialEq)] -pub struct StepView<'t, F: IsFFTField> { +pub struct StepView<'t, F: IsField> { pub table_view: TableView<'t, F>, pub step_idx: usize, } -impl<'t, F: IsFFTField> StepView<'t, F> { +impl<'t, F: IsField> StepView<'t, F> { pub fn new(table_view: TableView<'t, F>, step_idx: usize) -> Self { StepView { table_view, @@ -196,20 +199,20 @@ impl<'t, F: IsFFTField> StepView<'t, F> { /// compute a transition. /// Example: For a simple Fibonacci computation, if t(x) is the trace polynomial of /// the computation, this will output evaluations t(x), t(g * x), t(g^2 * z). -pub fn get_trace_evaluations( - trace_polys: &[Polynomial>], - x: &FieldElement, +pub fn get_trace_evaluations>( + trace_polys: &[Polynomial>], + x: &FieldElement, frame_offsets: &[usize], primitive_root: &FieldElement, -) -> Vec>> { +) -> Vec>> { frame_offsets .iter() - .map(|offset| x * primitive_root.pow(*offset)) + .map(|offset| primitive_root.pow(*offset) * x) .map(|eval_point| { trace_polys .iter() .map(|poly| poly.evaluate(&eval_point)) - .collect::>>() + .collect::>>() }) .collect() } diff --git a/provers/stark/src/traits.rs b/provers/stark/src/traits.rs index 005332a0d5..cb2ffdfb8b 100644 --- a/provers/stark/src/traits.rs +++ b/provers/stark/src/traits.rs @@ -1,7 +1,10 @@ use itertools::Itertools; use lambdaworks_math::{ fft::cpu::roots_of_unity::get_powers_of_primitive_root_coset, - field::{element::FieldElement, traits::IsFFTField}, + field::{ + element::FieldElement, + traits::{IsFFTField, IsField, IsSubFieldOf}, + }, polynomial::Polynomial, }; @@ -14,7 +17,8 @@ use super::{ /// AIR is a representation of the Constraints pub trait AIR { - type Field: IsFFTField; + type Field: IsFFTField + IsSubFieldOf; + type FieldExtension: IsField; type RAPChallenges; type PublicInputs; @@ -28,13 +32,13 @@ pub trait AIR { fn build_auxiliary_trace( &self, - main_trace: &TraceTable, + main_trace: &TraceTable, rap_challenges: &Self::RAPChallenges, - ) -> TraceTable; + ) -> TraceTable; fn build_rap_challenges( &self, - transcript: &mut impl IsStarkTranscript, + transcript: &mut impl IsStarkTranscript, ) -> Self::RAPChallenges; fn number_auxiliary_rap_columns(&self) -> usize; @@ -43,17 +47,17 @@ pub trait AIR { fn compute_transition( &self, - frame: &Frame, - periodic_values: &[FieldElement], + frame: &Frame, + periodic_values: &[FieldElement], rap_challenges: &Self::RAPChallenges, ) -> Vec>; fn boundary_constraints( &self, rap_challenges: &Self::RAPChallenges, - ) -> BoundaryConstraints; + ) -> BoundaryConstraints; - fn transition_exemptions(&self) -> Vec>> { + fn transition_exemptions(&self) -> Vec>> { let trace_length = self.trace_length(); let roots_of_unity_order = trace_length.trailing_zeros(); let roots_of_unity = get_powers_of_primitive_root_coset( @@ -105,8 +109,9 @@ pub trait AIR { fn transition_exemptions_verifier( &self, root: &FieldElement, - ) -> Vec>> { - let x = Polynomial::new_monomial(FieldElement::one(), 1); + ) -> Vec>> { + let x = + Polynomial::>::new_monomial(FieldElement::one(), 1); let max = self .context() @@ -118,7 +123,7 @@ pub trait AIR { .map(|index| { (1..=index).fold( Polynomial::new_monomial(FieldElement::one(), 0), - |acc, k| acc * (&x - root.pow(k)), + |acc, k| acc * (&x - root.pow(k).to_extension()), ) }) .collect() @@ -128,7 +133,9 @@ pub trait AIR { vec![] } - fn get_periodic_column_polynomials(&self) -> Vec>> { + fn get_periodic_column_polynomials( + &self, + ) -> Vec>> { let mut result = Vec::new(); for periodic_column in self.get_periodic_column_values() { let values: Vec<_> = periodic_column @@ -136,6 +143,7 @@ pub trait AIR { .cycle() .take(self.trace_length()) .cloned() + .map(|x| x.to_extension()) .collect(); let poly = Polynomial::interpolate_fft::(&values).unwrap(); result.push(poly); diff --git a/provers/stark/src/transcript.rs b/provers/stark/src/transcript.rs index 4349b30929..15700848a0 100644 --- a/provers/stark/src/transcript.rs +++ b/provers/stark/src/transcript.rs @@ -2,7 +2,7 @@ use lambdaworks_math::{ field::{ element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - traits::{IsFFTField, IsField}, + traits::{IsFFTField, IsField, IsSubFieldOf}, }, traits::{ByteConversion, Serializable}, unsigned_integer::element::U256, @@ -15,18 +15,22 @@ pub trait IsStarkTranscript { fn state(&self) -> [u8; 32]; fn sample_field_element(&mut self) -> FieldElement; fn sample_u64(&mut self, upper_bound: u64) -> u64; - fn sample_z_ood( + fn sample_z_ood>( &mut self, - lde_roots_of_unity_coset: &[FieldElement], - trace_roots_of_unity: &[FieldElement], + lde_roots_of_unity_coset: &[FieldElement], + trace_roots_of_unity: &[FieldElement], ) -> FieldElement where FieldElement: Serializable, { loop { let value: FieldElement = self.sample_field_element(); - if !lde_roots_of_unity_coset.iter().any(|x| x == &value) - && !trace_roots_of_unity.iter().any(|x| x == &value) + if !lde_roots_of_unity_coset + .iter() + .any(|x| x.clone().to_extension() == value) + && !trace_roots_of_unity + .iter() + .any(|x| x.clone().to_extension() == value) { return value; } diff --git a/provers/stark/src/verifier.rs b/provers/stark/src/verifier.rs index bf2ec9c85c..084ef5b42e 100644 --- a/provers/stark/src/verifier.rs +++ b/provers/stark/src/verifier.rs @@ -12,8 +12,9 @@ use crate::{ use lambdaworks_math::{ fft::cpu::bit_reversing::reverse_index, field::{ - element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - traits::IsFFTField, + element::FieldElement, + fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, + traits::{IsFFTField, IsField, IsSubFieldOf}, }, traits::Serializable, }; @@ -31,12 +32,13 @@ pub struct Verifier {} impl IsStarkVerifier for Verifier { type Field = Stark252PrimeField; + type FieldExtension = Stark252PrimeField; } pub struct Challenges where - F: IsFFTField, - A: AIR, + F: IsField, + A: AIR, { pub z: FieldElement, pub boundary_coeffs: Vec>, @@ -52,12 +54,13 @@ where pub type DeepPolynomialEvaluations = (Vec>, Vec>); pub trait IsStarkVerifier { - type Field: IsFFTField; + type Field: IsFFTField + IsSubFieldOf; + type FieldExtension: IsField; fn sample_query_indexes( number_of_queries: usize, domain: &Domain, - transcript: &mut impl IsStarkTranscript, + transcript: &mut impl IsStarkTranscript, ) -> Vec { let domain_size = domain.lde_roots_of_unity_coset.len() as u64; (0..number_of_queries) @@ -67,13 +70,14 @@ pub trait IsStarkVerifier { fn step_1_replay_rounds_and_recover_challenges( air: &A, - proof: &StarkProof, + proof: &StarkProof, domain: &Domain, - transcript: &mut impl IsStarkTranscript, - ) -> Challenges + transcript: &mut impl IsStarkTranscript, + ) -> Challenges where FieldElement: Serializable, - A: AIR, + FieldElement: Serializable, + A: AIR, { // =================================== // ==========| Round 1 |========== @@ -164,7 +168,7 @@ pub trait IsStarkVerifier { transcript.append_bytes(root); element }) - .collect::>>(); + .collect::>>(); // >>>> Send challenge πœβ‚™β‚‹β‚ zetas.push(transcript.sample_field_element()); @@ -202,12 +206,12 @@ pub trait IsStarkVerifier { fn step_2_verify_claimed_composition_polynomial( air: &A, - proof: &StarkProof, + proof: &StarkProof, domain: &Domain, - challenges: &Challenges, + challenges: &Challenges, ) -> bool where - A: AIR, + A: AIR, { let boundary_constraints = air.boundary_constraints(&challenges.rap_challenges); @@ -216,18 +220,18 @@ pub trait IsStarkVerifier { #[allow(clippy::type_complexity)] let (boundary_c_i_evaluations_num, mut boundary_c_i_evaluations_den): ( - Vec>, - Vec>, + Vec>, + Vec>, ) = (0..number_of_b_constraints) .map(|index| { let step = boundary_constraints.constraints[index].step; let point = &domain.trace_primitive_root.pow(step as u64); let trace_idx = boundary_constraints.constraints[index].col; let trace_evaluation = &proof.trace_ood_evaluations.get_row(0)[trace_idx]; - let boundary_zerofier_challenges_z_den = &challenges.z - point; + let boundary_zerofier_challenges_z_den = -point + &challenges.z; let boundary_quotient_ood_evaluation_num = - trace_evaluation - &boundary_constraints.constraints[index].value; + -&boundary_constraints.constraints[index].value + trace_evaluation; ( boundary_quotient_ood_evaluation_num, @@ -240,19 +244,21 @@ pub trait IsStarkVerifier { FieldElement::inplace_batch_inverse(&mut boundary_c_i_evaluations_den).unwrap(); - let boundary_quotient_ood_evaluation: FieldElement = + let boundary_quotient_ood_evaluation: FieldElement = boundary_c_i_evaluations_num .iter() .zip(&boundary_c_i_evaluations_den) .zip(&challenges.boundary_coeffs) .map(|((num, den), beta)| num * den * beta) - .fold(FieldElement::::zero(), |acc, x| acc + x); + .fold(FieldElement::::zero(), |acc, x| { + acc + x + }); let periodic_values = air .get_periodic_column_polynomials() .iter() .map(|poly| poly.evaluate(&challenges.z)) - .collect::>>(); + .collect::>>(); let transition_ood_frame_evaluations = air.compute_transition( &(proof.trace_ood_evaluations).into_frame(A::STEP_SIZE), @@ -260,7 +266,7 @@ pub trait IsStarkVerifier { &challenges.rap_challenges, ); - let denominator = (&challenges.z.pow(trace_length) - FieldElement::::one()) + let denominator = (-FieldElement::::one() + &challenges.z.pow(trace_length)) .inv() .unwrap(); @@ -270,7 +276,7 @@ pub trait IsStarkVerifier { ) .iter() .map(|poly| poly.evaluate(&challenges.z)) - .collect::>>(); + .collect::>>(); let unity = &FieldElement::one(); let transition_c_i_evaluations_sum = transition_ood_frame_evaluations @@ -282,7 +288,7 @@ pub trait IsStarkVerifier { .checked_sub(1) .map(|i| &exemption[i]) .unwrap_or(unity); - acc + &denominator * eval * beta * except + acc + eval * &denominator * beta * except }); let composition_poly_ood_evaluation = @@ -300,13 +306,14 @@ pub trait IsStarkVerifier { } fn step_3_verify_fri( - proof: &StarkProof, + proof: &StarkProof, domain: &Domain, - challenges: &Challenges, + challenges: &Challenges, ) -> bool where FieldElement: Serializable + Sync + Send, - A: AIR, + FieldElement: Serializable + Sync + Send, + A: AIR, { let (deep_poly_evaluations, deep_poly_evaluations_sym) = Self::reconstruct_deep_composition_poly_evaluations_for_all_queries( @@ -363,25 +370,31 @@ pub trait IsStarkVerifier { proof: &Proof, root: &Commitment, index: usize, - value: &[FieldElement], + value: &[FieldElement], ) -> bool where FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { - proof.verify::>(root, index, &value.to_owned()) + proof.verify::>( + root, + index, + &value.to_owned(), + ) } /// Verify opening Open(tβ±Ό(D_LDE), 𝜐) and Open(tβ±Ό(D_LDE), -𝜐) for all trace polynomials tβ±Ό, /// where 𝜐 and -𝜐 are the elements corresponding to the index challenge `iota`. fn verify_trace_openings( num_main_columns: usize, - proof: &StarkProof, - deep_poly_openings: &DeepPolynomialOpening, - deep_poly_openings_sym: &DeepPolynomialOpening, + proof: &StarkProof, + deep_poly_openings: &DeepPolynomialOpening, + deep_poly_openings_sym: &DeepPolynomialOpening, iota: usize, ) -> bool where FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { let lde_trace_evaluations = vec![ deep_poly_openings.lde_trace_evaluations[..num_main_columns].to_vec(), @@ -418,13 +431,14 @@ pub trait IsStarkVerifier { /// Verify opening Open(Hα΅’(D_LDE), 𝜐) and Open(Hα΅’(D_LDE), -𝜐) for all parts Hα΅’of the composition /// polynomial, where 𝜐 and -𝜐 are the elements corresponding to the index challenge `iota`. fn verify_composition_poly_opening( - deep_poly_openings: &DeepPolynomialOpening, - deep_poly_openings_sym: &DeepPolynomialOpening, + deep_poly_openings: &DeepPolynomialOpening, + deep_poly_openings_sym: &DeepPolynomialOpening, composition_poly_merkle_root: &Commitment, iota: &usize, ) -> bool where FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { let mut value = deep_poly_openings .lde_composition_poly_parts_evaluation @@ -433,20 +447,23 @@ pub trait IsStarkVerifier { deep_poly_openings .lde_composition_poly_proof - .verify::>( + .verify::>( composition_poly_merkle_root, *iota, &value, ) } - fn step_4_verify_trace_and_composition_openings>( + fn step_4_verify_trace_and_composition_openings< + A: AIR, + >( air: &A, - proof: &StarkProof, - challenges: &Challenges, + proof: &StarkProof, + challenges: &Challenges, ) -> bool where FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { challenges .iotas @@ -480,12 +497,13 @@ pub trait IsStarkVerifier { fn verify_fri_layer_openings( merkle_root: &Commitment, auth_path_sym: &Proof, - evaluation: &FieldElement, - evaluation_sym: &FieldElement, + evaluation: &FieldElement, + evaluation_sym: &FieldElement, iota: usize, ) -> bool where FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { let evaluations = if iota % 2 == 1 { vec![evaluation_sym.clone(), evaluation.clone()] @@ -493,7 +511,7 @@ pub trait IsStarkVerifier { vec![evaluation.clone(), evaluation_sym.clone()] }; - auth_path_sym.verify::>( + auth_path_sym.verify::>( merkle_root, iota >> 1, &evaluations, @@ -509,16 +527,17 @@ pub trait IsStarkVerifier { /// `deep_composition_evaluation`: precomputed value of pβ‚€(𝜐), where pβ‚€ is the deep composition polynomial. /// `deep_composition_evaluation_sym`: precomputed value of pβ‚€(-𝜐), where pβ‚€ is the deep composition polynomial. fn verify_query_and_sym_openings( - proof: &StarkProof, - zetas: &[FieldElement], + proof: &StarkProof, + zetas: &[FieldElement], iota: usize, - fri_decommitment: &FriDecommitment, + fri_decommitment: &FriDecommitment, evaluation_point_inv: FieldElement, - deep_composition_evaluation: &FieldElement, - deep_composition_evaluation_sym: &FieldElement, + deep_composition_evaluation: &FieldElement, + deep_composition_evaluation_sym: &FieldElement, ) -> bool where FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { let fri_layers_merkle_roots = &proof.fri_layers_merkle_roots; let evaluation_point_vec: Vec> = @@ -533,7 +552,7 @@ pub trait IsStarkVerifier { // Reconstruct p₁(𝜐²) let mut v = - (p0_eval + p0_eval_sym) + &zetas[0] * (p0_eval - p0_eval_sym) * evaluation_point_inv; + (p0_eval + p0_eval_sym) + evaluation_point_inv * &zetas[0] * (p0_eval - p0_eval_sym); let mut index = iota; // For each FRI layer, starting from the layer 1: use the proof to verify the validity of values pα΅’(βˆ’πœ^(2ⁱ)) (given by the prover) and @@ -564,7 +583,7 @@ pub trait IsStarkVerifier { ); // Update `v` with next value pα΅’β‚Šβ‚(𝜐^(2ⁱ⁺¹)). - v = (&v + evaluation_sym) + &zetas[i + 1] * (&v - evaluation_sym) * evaluation_point_inv; + v = (&v + evaluation_sym) + evaluation_point_inv * &zetas[i + 1] * (&v - evaluation_sym); // Update index for next iteration. The index of the squares in the next layer // is obtained by halving the current index. This is due to the bit-reverse @@ -582,12 +601,12 @@ pub trait IsStarkVerifier { } fn reconstruct_deep_composition_poly_evaluations_for_all_queries( - challenges: &Challenges, + challenges: &Challenges, domain: &Domain, - proof: &StarkProof, - ) -> DeepPolynomialEvaluations + proof: &StarkProof, + ) -> DeepPolynomialEvaluations where - A: AIR, + A: AIR, { let mut deep_poly_evaluations = Vec::new(); let mut deep_poly_evaluations_sym = Vec::new(); @@ -618,17 +637,19 @@ pub trait IsStarkVerifier { (deep_poly_evaluations, deep_poly_evaluations_sym) } - fn reconstruct_deep_composition_poly_evaluation>( - proof: &StarkProof, + fn reconstruct_deep_composition_poly_evaluation< + A: AIR, + >( + proof: &StarkProof, evaluation_point: &FieldElement, primitive_root: &FieldElement, - challenges: &Challenges, - lde_trace_evaluations: &[FieldElement], - lde_composition_poly_parts_evaluation: &[FieldElement], - ) -> FieldElement { + challenges: &Challenges, + lde_trace_evaluations: &[FieldElement], + lde_composition_poly_parts_evaluation: &[FieldElement], + ) -> FieldElement { let mut denoms_trace = (0..proof.trace_ood_evaluations.height) - .map(|row_idx| evaluation_point - &challenges.z * primitive_root.pow(row_idx as u64)) - .collect::>>(); + .map(|row_idx| evaluation_point - primitive_root.pow(row_idx as u64) * &challenges.z) + .collect::>>(); FieldElement::inplace_batch_inverse(&mut denoms_trace).unwrap(); let trace_term = (0..proof.trace_ood_evaluations.width) @@ -662,14 +683,15 @@ pub trait IsStarkVerifier { } fn verify( - proof: &StarkProof, + proof: &StarkProof, pub_input: &A::PublicInputs, proof_options: &ProofOptions, - mut transcript: impl IsStarkTranscript, + mut transcript: impl IsStarkTranscript, ) -> bool where - A: AIR, + A: AIR, FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { // Verify there are enough queries if proof.query_list.len() < proof_options.fri_number_of_queries { diff --git a/winterfell_adapter/src/adapter/air.rs b/winterfell_adapter/src/adapter/air.rs index 1aaecc3a98..d38e2c6b15 100644 --- a/winterfell_adapter/src/adapter/air.rs +++ b/winterfell_adapter/src/adapter/air.rs @@ -80,6 +80,7 @@ where M: Clone, { type Field = FE; + type FieldExtension = FE; type RAPChallenges = Vec; type PublicInputs = AirAdapterPublicInputs; const STEP_SIZE: usize = 1; diff --git a/winterfell_adapter/src/adapter/mod.rs b/winterfell_adapter/src/adapter/mod.rs index 5975ea734d..0b26961528 100644 --- a/winterfell_adapter/src/adapter/mod.rs +++ b/winterfell_adapter/src/adapter/mod.rs @@ -13,11 +13,13 @@ pub mod public_inputs; pub struct Prover; impl IsStarkProver for Prover { type Field = Felt; + type FieldExtension = Felt; } pub struct Verifier {} impl IsStarkVerifier for Verifier { type Field = Felt; + type FieldExtension = Felt; } pub struct Transcript { From fb4431992eb9a47f3adc9d5a45c184933970297d Mon Sep 17 00:00:00 2001 From: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> Date: Wed, 20 Dec 2023 14:28:37 -0300 Subject: [PATCH 19/29] Stark: Prover and Verifier over field extensions v2 (#717) * wip * cast to fieldextension * fmt * clippy * fmt * fix parallel * prover with generic air argument * verifier with generic argument * clippy, fmt * wip, prover refactor * wip * fix stone serialization * add safe scope for unwraps * minor refactor * clippy, fmt * remove commented code. Fix typo in docs * fix compilation error * fmt * fix parallel * remove unnecessary trait bound * fix wasm * fix wasm * change wasm proof bytes * fix number of bytes * fix wasm proof * fix number of bytes --- provers/cairo/benches/criterion_verifier.rs | 9 +- .../cairo/benches/criterion_verifier_70k.rs | 9 +- provers/cairo/src/air.rs | 11 +- provers/cairo/src/main.rs | 14 +- provers/cairo/src/tests/integration_tests.rs | 14 +- provers/cairo/src/wasm_wrappers.rs | 4 +- provers/cairo/tests/wasm.rs | 2378 +++++++++-------- provers/stark/src/proof/stark.rs | 159 +- provers/stark/src/prover.rs | 549 ++-- provers/stark/src/tests/integration_tests.rs | 36 +- provers/stark/src/traits.rs | 2 +- provers/stark/src/verifier.rs | 362 ++- winterfell_adapter/benches/proving.rs | 7 +- winterfell_adapter/src/adapter/mod.rs | 17 +- winterfell_adapter/src/examples/cubic.rs | 12 +- .../src/examples/fibonacci_2_terms.rs | 26 +- .../src/examples/fibonacci_rap.rs | 14 +- winterfell_adapter/src/examples/miden_vm.rs | 40 +- 18 files changed, 1874 insertions(+), 1789 deletions(-) diff --git a/provers/cairo/benches/criterion_verifier.rs b/provers/cairo/benches/criterion_verifier.rs index 50fefcd17e..de8379a53a 100644 --- a/provers/cairo/benches/criterion_verifier.rs +++ b/provers/cairo/benches/criterion_verifier.rs @@ -12,12 +12,17 @@ use stark_platinum_prover::proof::{ pub mod functions; -fn load_proof_and_pub_inputs(input_path: &str) -> (StarkProof, PublicInputs) { +fn load_proof_and_pub_inputs( + input_path: &str, +) -> ( + StarkProof, + PublicInputs, +) { let program_content = std::fs::read(input_path).unwrap(); let mut bytes = program_content.as_slice(); let proof_len = usize::from_be_bytes(bytes[0..8].try_into().unwrap()); bytes = &bytes[8..]; - let proof: StarkProof = + let proof: StarkProof = serde_cbor::from_slice(&bytes[0..proof_len]).unwrap(); bytes = &bytes[proof_len..]; diff --git a/provers/cairo/benches/criterion_verifier_70k.rs b/provers/cairo/benches/criterion_verifier_70k.rs index ab2cc6e69a..f4aafd58d1 100644 --- a/provers/cairo/benches/criterion_verifier_70k.rs +++ b/provers/cairo/benches/criterion_verifier_70k.rs @@ -13,12 +13,17 @@ use stark_platinum_prover::proof::{ pub mod functions; -fn load_proof_and_pub_inputs(input_path: &str) -> (StarkProof, PublicInputs) { +fn load_proof_and_pub_inputs( + input_path: &str, +) -> ( + StarkProof, + PublicInputs, +) { let program_content = std::fs::read(input_path).unwrap(); let mut bytes = program_content.as_slice(); let proof_len = usize::from_be_bytes(bytes[0..8].try_into().unwrap()); bytes = &bytes[8..]; - let proof: StarkProof = + let proof: StarkProof = serde_cbor::from_slice(&bytes[0..proof_len]).unwrap(); bytes = &bytes[proof_len..]; diff --git a/provers/cairo/src/air.rs b/provers/cairo/src/air.rs index c06c4d0e5f..4ce45d1305 100644 --- a/provers/cairo/src/air.rs +++ b/provers/cairo/src/air.rs @@ -1275,8 +1275,8 @@ pub fn generate_cairo_proof( trace: &TraceTable, pub_input: &PublicInputs, proof_options: &ProofOptions, -) -> Result, ProvingError> { - Prover::prove::( +) -> Result, ProvingError> { + Prover::::prove( trace, pub_input, proof_options, @@ -1288,11 +1288,11 @@ pub fn generate_cairo_proof( /// concrete types. /// The field is set to Stark252PrimeField and the AIR to CairoAIR. pub fn verify_cairo_proof( - proof: &StarkProof, + proof: &StarkProof, pub_input: &PublicInputs, proof_options: &ProofOptions, ) -> bool { - Verifier::verify::( + Verifier::::verify( proof, pub_input, proof_options, @@ -1481,7 +1481,8 @@ mod prop_test { // At this point, the verifier only knows about the serialized proof, the proof options // and the public inputs. - let proof: StarkProof = serde_cbor::from_slice(&proof_bytes).unwrap(); + let proof: StarkProof = + serde_cbor::from_slice(&proof_bytes).unwrap(); // The proof is verified successfully. assert!(verify_cairo_proof(&proof, &pub_inputs, &proof_options)); diff --git a/provers/cairo/src/main.rs b/provers/cairo/src/main.rs index b0ae341b4c..3e1c421823 100644 --- a/provers/cairo/src/main.rs +++ b/provers/cairo/src/main.rs @@ -108,7 +108,10 @@ fn try_compile(program_path: &String, out_file_path: &String) -> Result<(), Erro fn generate_proof( input_path: &String, proof_options: &ProofOptions, -) -> Option<(StarkProof, PublicInputs)> { +) -> Option<( + StarkProof, + PublicInputs, +)> { let timer = Instant::now(); let Ok(program_content) = std::fs::read(input_path) else { @@ -145,7 +148,10 @@ fn generate_proof_from_trace( trace_bin_path: &str, memory_bin_path: &str, proof_options: &ProofOptions, -) -> Option<(StarkProof, PublicInputs)> { +) -> Option<( + StarkProof, + PublicInputs, +)> { // ## Generating the prover args let timer = Instant::now(); let Ok((main_trace, pub_inputs)) = @@ -172,7 +178,7 @@ fn generate_proof_from_trace( } fn verify_proof( - proof: StarkProof, + proof: StarkProof, pub_inputs: PublicInputs, proof_options: &ProofOptions, ) -> bool { @@ -192,7 +198,7 @@ fn verify_proof( } fn write_proof( - proof: StarkProof, + proof: StarkProof, pub_inputs: PublicInputs, proof_path: String, ) { diff --git a/provers/cairo/src/tests/integration_tests.rs b/provers/cairo/src/tests/integration_tests.rs index 5bcbe3b828..4c7a41046e 100644 --- a/provers/cairo/src/tests/integration_tests.rs +++ b/provers/cairo/src/tests/integration_tests.rs @@ -52,16 +52,19 @@ fn test_verifier_rejects_wrong_authentication_paths() { // Change order of authentication path hashes let query = 0; - let merkle_tree = 0; - let mut original_path = proof.deep_poly_openings[query].lde_trace_merkle_proofs[merkle_tree] + let mut original_path = proof.deep_poly_openings[query] + .main_trace_polys + .proof .merkle_path .clone(); original_path.swap(0, 1); // For the test to make sense, we have to make sure // that the two hashes are different. assert_ne!(original_path[0], original_path[1]); - proof.deep_poly_openings[query].lde_trace_merkle_proofs[merkle_tree].merkle_path = - original_path; + proof.deep_poly_openings[query] + .main_trace_polys + .proof + .merkle_path = original_path; // Verifier should reject the proof assert!(!verify_cairo_proof(&proof, &pub_inputs, &proof_options)); @@ -182,7 +185,8 @@ fn deserialize_and_verify() { // At this point, the verifier only knows about the serialized proof, the proof options // and the public inputs. - let proof: StarkProof = serde_cbor::from_slice(&proof_bytes).unwrap(); + let proof: StarkProof = + serde_cbor::from_slice(&proof_bytes).unwrap(); // The proof is verified successfully. assert!(verify_cairo_proof(&proof, &pub_inputs, &proof_options)); diff --git a/provers/cairo/src/wasm_wrappers.rs b/provers/cairo/src/wasm_wrappers.rs index de785e3a33..bb850fdc75 100644 --- a/provers/cairo/src/wasm_wrappers.rs +++ b/provers/cairo/src/wasm_wrappers.rs @@ -10,7 +10,7 @@ use std::collections::HashMap; use wasm_bindgen::prelude::wasm_bindgen; #[wasm_bindgen] -pub struct Stark252PrimeFieldProof(StarkProof); +pub struct Stark252PrimeFieldProof(StarkProof); #[wasm_bindgen] #[derive(Debug, Clone, Copy, Serialize, Deserialize, Eq, PartialEq, Hash)] @@ -45,7 +45,7 @@ pub fn verify_cairo_proof_wasm(proof_bytes: &[u8], proof_options: &ProofOptions) return false; }; - Verifier::verify::( + Verifier::::verify( &proof, &pub_inputs, proof_options, diff --git a/provers/cairo/tests/wasm.rs b/provers/cairo/tests/wasm.rs index 909620ea68..0eba688985 100644 --- a/provers/cairo/tests/wasm.rs +++ b/provers/cairo/tests/wasm.rs @@ -19,1205 +19,1207 @@ fn test_prove_cairo1_program_wasm() { // Test case is fibo5, with default test options #[cfg(feature = "wasm")] -static PROOF: [u8; 25396] = [ - 195, 90, 0, 0, 64, 2, 252, 245, 204, 166, 166, 99, 192, 158, 197, 112, 127, 229, 91, 155, 221, - 204, 192, 231, 39, 132, 16, 113, 234, 72, 205, 118, 5, 214, 64, 133, 109, 72, 221, 209, 118, - 219, 43, 25, 209, 138, 192, 61, 240, 47, 23, 58, 20, 98, 32, 220, 185, 74, 100, 168, 105, 233, - 128, 164, 181, 241, 136, 114, 32, 118, 118, 32, 1, 69, 175, 164, 230, 156, 45, 26, 157, 26, - 149, 15, 8, 187, 118, 184, 79, 28, 9, 128, 92, 187, 64, 32, 106, 47, 176, 82, 200, 128, 34, - 224, 32, 5, 6, 92, 203, 162, 164, 163, 162, 68, 16, 0, 82, 11, 135, 230, 228, 7, 226, 171, 88, - 92, 95, 196, 6, 255, 18, 101, 66, 114, 123, 229, 195, 32, 2, 16, 138, 53, 88, 63, 13, 207, 100, - 96, 215, 81, 252, 113, 252, 104, 146, 133, 103, 81, 255, 81, 71, 237, 60, 226, 49, 50, 43, 99, - 201, 28, 32, 4, 17, 93, 58, 198, 31, 191, 184, 221, 129, 28, 32, 181, 119, 68, 155, 9, 145, 17, - 96, 129, 132, 185, 193, 117, 34, 81, 34, 35, 249, 18, 252, 32, 5, 43, 221, 50, 152, 189, 122, - 229, 150, 52, 80, 184, 111, 92, 174, 115, 114, 150, 148, 107, 18, 60, 247, 11, 163, 214, 67, - 193, 177, 218, 87, 190, 32, 4, 105, 167, 227, 252, 176, 234, 185, 120, 85, 181, 60, 107, 207, - 4, 173, 11, 46, 224, 195, 53, 199, 203, 144, 17, 244, 206, 39, 42, 200, 47, 66, 32, 0, 181, - 103, 37, 5, 59, 182, 223, 220, 236, 121, 39, 67, 114, 252, 125, 59, 76, 247, 72, 25, 176, 7, - 148, 65, 41, 243, 220, 21, 207, 164, 117, 32, 4, 90, 179, 146, 130, 157, 219, 120, 110, 118, - 60, 147, 161, 185, 126, 62, 157, 166, 123, 164, 12, 216, 3, 202, 32, 148, 249, 238, 10, 231, - 210, 59, 32, 6, 41, 232, 112, 140, 202, 118, 51, 220, 123, 53, 166, 57, 49, 28, 15, 38, 91, 82, - 198, 90, 129, 41, 61, 131, 232, 94, 55, 48, 14, 125, 77, 32, 4, 194, 222, 149, 32, 43, 20, 102, - 178, 214, 19, 169, 65, 198, 17, 117, 26, 67, 131, 15, 155, 186, 106, 26, 94, 12, 140, 5, 134, - 218, 231, 218, 32, 2, 85, 140, 87, 77, 176, 112, 174, 22, 191, 211, 127, 99, 48, 55, 105, 151, - 207, 3, 10, 37, 61, 81, 188, 143, 3, 180, 142, 143, 9, 213, 218, 32, 4, 98, 89, 68, 108, 101, - 194, 71, 23, 92, 110, 131, 242, 69, 47, 57, 180, 92, 230, 84, 85, 144, 77, 103, 228, 111, 188, - 25, 126, 144, 73, 198, 32, 5, 217, 198, 79, 35, 57, 220, 145, 137, 0, 69, 236, 207, 235, 178, - 184, 77, 12, 119, 13, 56, 244, 30, 218, 17, 70, 236, 98, 26, 145, 76, 89, 32, 3, 95, 135, 88, - 10, 176, 51, 90, 130, 39, 75, 205, 113, 71, 208, 101, 151, 242, 41, 224, 203, 88, 169, 131, 75, - 74, 119, 160, 27, 34, 208, 2, 32, 1, 172, 82, 83, 80, 211, 162, 28, 102, 83, 189, 67, 32, 248, - 69, 34, 163, 129, 41, 228, 185, 193, 124, 26, 25, 67, 29, 16, 56, 43, 252, 48, 32, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 6, 189, - 193, 243, 52, 81, 38, 25, 72, 47, 0, 133, 5, 229, 118, 64, 187, 49, 202, 147, 210, 244, 196, - 91, 175, 180, 125, 21, 87, 52, 81, 17, 32, 1, 118, 240, 91, 187, 247, 137, 62, 204, 160, 34, - 69, 36, 164, 226, 89, 159, 103, 4, 158, 242, 71, 90, 93, 103, 60, 105, 87, 92, 91, 123, 30, 32, - 4, 43, 52, 172, 230, 228, 231, 96, 63, 36, 144, 247, 138, 100, 46, 246, 150, 60, 68, 158, 219, - 36, 214, 103, 6, 239, 247, 7, 52, 24, 218, 9, 32, 3, 163, 22, 47, 83, 153, 215, 94, 53, 57, - 143, 169, 171, 180, 153, 157, 66, 55, 131, 123, 2, 192, 150, 17, 111, 186, 221, 143, 48, 35, - 244, 242, 32, 0, 144, 33, 48, 13, 46, 114, 107, 137, 14, 254, 113, 127, 77, 11, 39, 212, 239, - 245, 171, 41, 79, 51, 137, 181, 189, 229, 11, 168, 157, 251, 107, 32, 6, 51, 197, 14, 31, 202, - 121, 32, 167, 6, 206, 121, 188, 245, 252, 158, 176, 142, 238, 31, 182, 47, 60, 167, 68, 233, - 37, 205, 114, 180, 86, 55, 32, 7, 33, 56, 227, 151, 110, 73, 248, 224, 59, 109, 17, 6, 26, 181, - 171, 21, 142, 226, 63, 66, 255, 227, 80, 55, 35, 72, 114, 99, 107, 177, 93, 32, 7, 104, 89, - 220, 165, 175, 33, 10, 220, 251, 253, 78, 192, 35, 189, 228, 111, 67, 115, 157, 116, 98, 159, - 61, 118, 155, 0, 253, 90, 12, 45, 241, 32, 3, 18, 47, 200, 117, 54, 56, 97, 85, 139, 95, 67, - 221, 137, 8, 202, 15, 118, 202, 38, 64, 246, 80, 193, 62, 112, 39, 124, 124, 144, 104, 250, 32, - 5, 104, 8, 179, 151, 138, 149, 187, 225, 64, 223, 159, 128, 69, 133, 147, 44, 104, 233, 136, - 112, 175, 192, 241, 13, 172, 175, 159, 138, 2, 132, 234, 32, 7, 203, 195, 14, 157, 122, 116, - 235, 253, 61, 252, 129, 53, 255, 76, 116, 207, 26, 125, 222, 236, 191, 197, 166, 104, 107, 2, - 149, 255, 18, 239, 74, 32, 7, 147, 21, 198, 192, 30, 44, 11, 201, 224, 230, 96, 25, 167, 80, - 82, 17, 49, 75, 119, 139, 149, 156, 245, 163, 197, 93, 38, 177, 80, 13, 157, 32, 6, 184, 189, - 253, 114, 33, 247, 141, 107, 8, 7, 78, 107, 66, 116, 202, 145, 242, 219, 174, 196, 30, 81, 64, - 103, 188, 133, 217, 34, 249, 234, 180, 32, 2, 106, 209, 168, 116, 191, 232, 243, 186, 106, 91, - 167, 149, 20, 92, 144, 40, 49, 126, 228, 66, 66, 249, 215, 231, 57, 210, 11, 42, 28, 169, 147, - 32, 0, 78, 167, 203, 82, 40, 9, 47, 24, 125, 93, 240, 31, 187, 240, 164, 203, 206, 62, 96, 46, - 174, 246, 33, 101, 81, 206, 200, 72, 183, 49, 201, 32, 6, 162, 179, 8, 122, 123, 106, 129, 182, - 53, 161, 29, 133, 189, 198, 254, 110, 62, 113, 58, 127, 153, 124, 9, 52, 69, 101, 246, 139, - 164, 12, 247, 32, 2, 19, 82, 4, 150, 144, 210, 229, 125, 20, 176, 92, 95, 24, 12, 245, 76, 4, - 19, 188, 135, 175, 208, 26, 8, 248, 203, 34, 107, 33, 110, 118, 32, 0, 138, 96, 0, 39, 184, - 153, 134, 24, 215, 216, 120, 86, 21, 69, 207, 179, 145, 149, 205, 201, 122, 38, 171, 254, 99, - 79, 22, 171, 63, 7, 139, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +static PROOF: [u8; 25527] = [ + 189, 90, 0, 0, 64, 34, 150, 252, 123, 41, 36, 58, 219, 76, 25, 24, 145, 52, 128, 169, 121, 212, + 127, 31, 128, 230, 237, 60, 212, 165, 171, 81, 190, 191, 157, 241, 200, 1, 77, 31, 232, 212, + 215, 170, 34, 124, 137, 195, 171, 208, 228, 24, 74, 34, 237, 107, 135, 106, 205, 168, 33, 72, + 193, 202, 8, 15, 250, 12, 38, 185, 118, 32, 4, 171, 46, 46, 72, 149, 150, 106, 253, 54, 138, + 21, 229, 36, 56, 120, 99, 238, 141, 157, 240, 156, 48, 237, 33, 19, 160, 95, 129, 156, 230, + 103, 32, 5, 92, 56, 221, 221, 174, 163, 72, 227, 62, 104, 105, 67, 88, 2, 1, 167, 128, 234, + 197, 5, 19, 84, 237, 187, 255, 26, 202, 199, 198, 212, 123, 32, 6, 179, 252, 250, 97, 234, 175, + 28, 235, 179, 10, 3, 105, 223, 144, 217, 123, 180, 69, 201, 228, 37, 168, 76, 160, 177, 251, + 61, 4, 188, 206, 150, 32, 4, 35, 76, 126, 184, 197, 8, 44, 216, 227, 61, 246, 83, 23, 174, 189, + 38, 10, 238, 119, 48, 144, 139, 151, 205, 240, 162, 170, 38, 69, 32, 218, 32, 0, 78, 185, 188, + 8, 218, 190, 3, 120, 130, 120, 175, 57, 130, 249, 75, 60, 8, 193, 5, 209, 215, 185, 84, 20, + 152, 161, 136, 29, 105, 202, 146, 32, 1, 32, 251, 95, 208, 37, 117, 134, 77, 38, 169, 174, 238, + 162, 116, 104, 140, 208, 75, 38, 112, 222, 177, 176, 95, 20, 91, 133, 96, 134, 241, 165, 32, 5, + 58, 73, 111, 62, 133, 211, 177, 161, 47, 249, 216, 236, 145, 223, 222, 0, 208, 194, 218, 24, + 115, 204, 219, 46, 122, 185, 109, 97, 142, 6, 157, 32, 6, 157, 36, 183, 159, 66, 233, 225, 80, + 151, 252, 236, 118, 72, 239, 239, 0, 104, 97, 109, 12, 57, 230, 109, 151, 61, 92, 182, 176, + 199, 3, 79, 32, 1, 112, 61, 72, 192, 28, 241, 170, 208, 254, 139, 231, 7, 209, 54, 27, 192, + 145, 224, 147, 87, 46, 129, 150, 250, 105, 17, 146, 120, 238, 160, 242, 32, 6, 171, 57, 209, + 156, 186, 18, 81, 248, 81, 70, 231, 108, 187, 34, 194, 209, 111, 66, 41, 165, 196, 44, 203, + 185, 199, 88, 119, 146, 115, 47, 134, 32, 3, 253, 112, 171, 215, 178, 72, 192, 87, 190, 133, + 240, 187, 7, 161, 188, 230, 12, 255, 184, 120, 26, 25, 36, 123, 72, 169, 168, 139, 153, 157, + 59, 32, 4, 66, 223, 179, 251, 173, 74, 78, 2, 84, 31, 166, 43, 236, 208, 150, 145, 194, 243, + 10, 118, 191, 206, 20, 156, 165, 1, 69, 115, 4, 199, 206, 32, 7, 139, 31, 61, 86, 188, 154, + 191, 165, 215, 226, 86, 162, 70, 214, 134, 187, 31, 125, 208, 216, 59, 183, 54, 16, 82, 52, + 237, 240, 126, 50, 223, 32, 7, 191, 175, 19, 56, 74, 241, 8, 88, 216, 27, 92, 136, 239, 219, + 106, 181, 155, 238, 129, 10, 129, 221, 197, 69, 118, 172, 159, 87, 101, 181, 40, 32, 6, 1, 130, + 118, 140, 160, 245, 70, 213, 30, 155, 31, 17, 36, 171, 217, 155, 43, 167, 29, 86, 82, 125, 66, + 209, 133, 185, 134, 204, 61, 249, 223, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 5, 5, 77, 210, 22, 240, 224, 140, 52, 197, + 169, 11, 160, 21, 13, 55, 239, 229, 66, 219, 125, 182, 6, 36, 172, 145, 58, 153, 209, 202, 52, + 114, 32, 0, 148, 26, 6, 89, 195, 227, 119, 6, 167, 12, 169, 231, 161, 188, 192, 231, 34, 181, + 72, 158, 226, 167, 181, 10, 160, 173, 188, 242, 236, 219, 152, 32, 0, 228, 51, 150, 138, 23, + 240, 58, 239, 11, 11, 49, 205, 232, 207, 77, 226, 183, 230, 217, 57, 64, 109, 196, 12, 39, 85, + 255, 237, 18, 85, 193, 32, 6, 85, 4, 87, 121, 137, 197, 38, 112, 136, 27, 230, 225, 169, 48, + 102, 5, 84, 136, 250, 71, 223, 219, 186, 104, 60, 135, 3, 128, 62, 106, 45, 32, 7, 56, 179, + 166, 236, 231, 225, 176, 54, 191, 5, 110, 209, 16, 229, 236, 73, 110, 41, 50, 90, 219, 209, + 125, 93, 79, 197, 186, 92, 170, 49, 219, 32, 3, 10, 93, 160, 62, 131, 106, 88, 66, 230, 41, + 206, 250, 29, 161, 133, 102, 106, 249, 221, 8, 97, 108, 255, 63, 67, 47, 168, 171, 220, 106, + 41, 32, 0, 139, 66, 37, 71, 31, 54, 72, 41, 206, 95, 73, 251, 212, 195, 255, 11, 100, 97, 24, + 153, 43, 0, 48, 38, 167, 83, 93, 94, 34, 210, 250, 32, 5, 109, 143, 37, 8, 2, 69, 20, 0, 152, + 119, 96, 22, 198, 72, 166, 96, 100, 110, 176, 22, 243, 142, 119, 221, 147, 218, 103, 40, 35, + 46, 125, 32, 4, 188, 184, 9, 251, 140, 5, 113, 122, 209, 130, 111, 194, 127, 208, 115, 60, 123, + 105, 88, 5, 7, 234, 212, 115, 83, 139, 114, 75, 218, 7, 70, 32, 1, 227, 195, 188, 203, 231, + 139, 5, 12, 254, 39, 124, 95, 116, 106, 12, 10, 179, 28, 165, 161, 182, 30, 11, 214, 138, 148, + 228, 179, 58, 162, 36, 32, 5, 62, 149, 75, 155, 0, 72, 204, 169, 3, 6, 65, 123, 194, 43, 245, + 19, 54, 172, 21, 95, 53, 55, 148, 127, 95, 27, 64, 184, 193, 216, 120, 32, 4, 27, 7, 54, 57, + 150, 199, 162, 106, 136, 171, 108, 35, 200, 0, 240, 225, 124, 114, 127, 192, 131, 131, 14, 216, + 142, 47, 58, 30, 65, 127, 55, 32, 1, 148, 91, 227, 154, 152, 80, 25, 52, 55, 158, 182, 187, 14, + 180, 170, 20, 138, 197, 24, 175, 230, 9, 219, 122, 144, 144, 61, 108, 215, 189, 250, 32, 7, + 114, 120, 91, 223, 13, 219, 241, 233, 217, 192, 111, 190, 224, 172, 246, 176, 182, 66, 142, + 136, 226, 43, 54, 172, 53, 15, 59, 142, 97, 211, 188, 32, 2, 132, 57, 150, 70, 49, 1, 8, 234, + 246, 45, 249, 78, 230, 149, 128, 150, 96, 213, 213, 11, 99, 63, 254, 93, 43, 187, 22, 231, 60, + 154, 36, 32, 3, 63, 223, 107, 43, 58, 72, 137, 1, 117, 86, 227, 189, 56, 81, 54, 68, 42, 58, + 135, 219, 191, 103, 9, 82, 207, 206, 34, 151, 38, 200, 153, 32, 1, 238, 242, 213, 113, 252, 6, + 128, 90, 154, 103, 235, 16, 227, 37, 91, 202, 212, 170, 172, 81, 1, 10, 69, 89, 136, 49, 193, + 130, 176, 239, 60, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, 255, 254, 239, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 255, - 225, 32, 3, 214, 98, 213, 77, 223, 166, 113, 182, 177, 112, 202, 142, 120, 123, 210, 135, 65, - 231, 135, 220, 154, 101, 199, 52, 61, 65, 115, 101, 35, 238, 99, 32, 2, 237, 114, 68, 33, 120, - 121, 132, 101, 51, 184, 194, 74, 174, 1, 174, 42, 188, 230, 208, 206, 200, 252, 84, 118, 66, - 184, 50, 103, 137, 41, 129, 32, 2, 237, 114, 68, 33, 120, 121, 132, 101, 51, 184, 194, 74, 174, - 1, 174, 42, 188, 230, 208, 206, 200, 252, 84, 118, 66, 184, 50, 103, 137, 41, 129, 32, 2, 237, - 114, 68, 33, 120, 121, 132, 101, 51, 184, 194, 74, 174, 1, 174, 42, 188, 230, 208, 206, 200, - 252, 84, 118, 66, 184, 50, 103, 137, 41, 129, 32, 1, 226, 215, 61, 226, 138, 117, 212, 67, 55, - 36, 119, 189, 63, 52, 219, 125, 67, 232, 6, 109, 108, 224, 26, 24, 146, 21, 209, 121, 15, 248, - 44, 32, 0, 216, 17, 240, 131, 204, 175, 224, 176, 51, 191, 145, 234, 169, 170, 87, 47, 43, 20, - 84, 220, 133, 146, 169, 41, 125, 222, 242, 152, 108, 151, 95, 32, 3, 57, 147, 234, 249, 175, - 79, 245, 245, 16, 169, 45, 136, 115, 233, 195, 211, 149, 34, 194, 187, 199, 46, 90, 227, 120, - 141, 130, 128, 116, 103, 53, 32, 7, 0, 79, 88, 214, 180, 186, 237, 42, 157, 107, 42, 45, 82, - 99, 141, 140, 126, 4, 109, 84, 185, 23, 69, 119, 115, 28, 24, 237, 136, 136, 241, 32, 3, 254, - 203, 18, 120, 206, 98, 46, 204, 233, 191, 1, 152, 109, 82, 172, 172, 211, 63, 121, 14, 229, 7, - 157, 161, 255, 165, 27, 216, 75, 205, 186, 32, 3, 214, 241, 66, 54, 25, 57, 108, 78, 82, 195, - 134, 49, 188, 92, 61, 13, 157, 93, 84, 158, 24, 199, 65, 172, 179, 216, 196, 16, 113, 29, 105, - 32, 6, 70, 236, 249, 54, 22, 79, 2, 167, 17, 252, 132, 172, 193, 128, 127, 194, 113, 30, 197, - 159, 252, 77, 167, 56, 5, 21, 110, 26, 36, 38, 111, 32, 3, 75, 51, 219, 254, 96, 14, 192, 79, - 152, 164, 58, 183, 95, 77, 254, 10, 118, 248, 198, 242, 87, 96, 52, 208, 93, 197, 182, 103, - 237, 160, 66, 32, 4, 52, 60, 115, 181, 80, 56, 95, 29, 219, 97, 146, 147, 177, 176, 139, 153, - 80, 118, 74, 70, 33, 97, 32, 120, 72, 28, 237, 43, 120, 155, 233, 32, 0, 249, 250, 14, 170, - 185, 222, 147, 79, 238, 104, 251, 77, 90, 166, 150, 185, 116, 18, 203, 11, 213, 68, 250, 195, - 84, 198, 9, 172, 218, 65, 182, 32, 7, 115, 163, 106, 160, 150, 22, 133, 129, 181, 46, 47, 126, - 169, 161, 49, 60, 71, 42, 148, 116, 227, 131, 35, 110, 111, 100, 59, 115, 33, 75, 42, 32, 0, - 58, 58, 221, 103, 10, 95, 45, 209, 245, 41, 188, 148, 227, 124, 160, 141, 25, 194, 197, 236, - 19, 233, 77, 139, 122, 76, 248, 170, 38, 210, 157, 32, 2, 252, 198, 106, 130, 53, 131, 32, 61, - 31, 98, 220, 97, 5, 32, 233, 76, 207, 227, 54, 80, 52, 45, 57, 238, 210, 189, 64, 95, 231, 189, - 248, 32, 2, 13, 52, 92, 65, 79, 152, 137, 74, 116, 221, 34, 151, 75, 117, 203, 252, 151, 128, - 74, 139, 171, 5, 216, 61, 204, 135, 82, 158, 237, 107, 62, 32, 2, 63, 108, 142, 195, 144, 229, - 145, 69, 69, 97, 143, 241, 148, 139, 215, 254, 37, 80, 89, 242, 130, 34, 54, 198, 74, 104, 254, - 62, 119, 44, 40, 32, 0, 238, 209, 167, 184, 109, 75, 19, 28, 176, 178, 28, 103, 113, 68, 83, - 199, 192, 156, 120, 76, 9, 150, 48, 101, 100, 71, 37, 68, 75, 213, 80, 32, 7, 191, 157, 104, - 18, 12, 93, 97, 172, 229, 160, 153, 151, 14, 208, 157, 193, 90, 70, 255, 50, 212, 163, 199, - 205, 32, 159, 208, 177, 157, 244, 108, 32, 2, 220, 213, 133, 63, 179, 204, 62, 190, 103, 62, - 69, 247, 105, 172, 105, 76, 195, 231, 181, 173, 151, 115, 217, 6, 140, 52, 92, 242, 233, 56, - 206, 32, 0, 104, 29, 152, 213, 126, 239, 0, 38, 175, 105, 151, 57, 171, 178, 158, 138, 196, 44, - 50, 199, 79, 145, 20, 91, 195, 201, 178, 168, 208, 74, 66, 32, 5, 41, 154, 12, 120, 155, 85, - 233, 71, 190, 148, 249, 239, 90, 207, 179, 156, 187, 244, 214, 206, 98, 73, 93, 79, 115, 247, - 144, 42, 241, 116, 110, 32, 2, 124, 110, 226, 43, 123, 27, 189, 253, 5, 75, 19, 30, 120, 11, - 20, 188, 190, 47, 112, 84, 176, 50, 214, 77, 237, 206, 26, 31, 14, 124, 57, 32, 4, 200, 126, - 113, 162, 228, 55, 163, 247, 228, 10, 179, 147, 112, 138, 74, 118, 57, 174, 170, 193, 115, 169, - 248, 144, 88, 140, 78, 32, 189, 103, 119, 32, 6, 0, 103, 143, 116, 149, 145, 221, 249, 53, 131, - 199, 38, 12, 172, 230, 147, 202, 66, 169, 168, 125, 138, 207, 63, 176, 238, 193, 182, 167, 76, - 138, 32, 7, 186, 82, 178, 133, 47, 204, 198, 203, 64, 139, 47, 18, 1, 50, 124, 224, 139, 146, - 55, 157, 186, 193, 138, 62, 245, 73, 17, 103, 166, 85, 233, 32, 3, 134, 226, 23, 212, 143, 109, - 160, 153, 182, 253, 222, 173, 177, 85, 115, 128, 241, 236, 228, 189, 157, 174, 207, 136, 217, - 42, 61, 129, 55, 226, 147, 32, 0, 69, 208, 210, 93, 92, 46, 214, 30, 252, 59, 64, 200, 26, 116, - 173, 219, 61, 84, 98, 203, 171, 105, 109, 106, 83, 142, 105, 204, 164, 238, 49, 32, 4, 34, 232, - 105, 46, 174, 23, 115, 143, 126, 29, 160, 100, 13, 58, 86, 237, 158, 170, 49, 101, 213, 180, - 182, 181, 41, 199, 52, 230, 82, 119, 25, 32, 1, 98, 187, 27, 229, 201, 158, 39, 193, 211, 70, - 93, 222, 26, 5, 208, 115, 210, 21, 161, 69, 138, 38, 126, 136, 225, 143, 243, 88, 62, 176, 112, - 32, 0, 136, 160, 207, 160, 36, 211, 218, 149, 134, 82, 207, 185, 98, 155, 93, 221, 150, 244, - 137, 116, 252, 185, 45, 91, 150, 252, 187, 170, 150, 153, 240, 32, 7, 63, 179, 154, 70, 15, 13, - 52, 159, 28, 115, 234, 234, 22, 169, 0, 212, 185, 230, 170, 250, 100, 55, 218, 163, 214, 84, - 170, 136, 8, 142, 97, 32, 5, 217, 71, 77, 19, 95, 20, 76, 44, 72, 163, 176, 19, 211, 22, 142, - 186, 67, 122, 192, 46, 216, 204, 50, 83, 63, 159, 254, 166, 25, 231, 229, 32, 4, 186, 73, 62, - 240, 185, 64, 8, 8, 150, 199, 126, 108, 204, 90, 194, 239, 215, 28, 85, 150, 213, 221, 96, 0, - 34, 1, 166, 81, 19, 68, 70, 32, 2, 210, 221, 158, 235, 53, 247, 87, 138, 233, 254, 149, 50, 49, - 168, 161, 96, 16, 247, 56, 52, 79, 94, 34, 150, 175, 91, 146, 23, 83, 120, 217, 32, 0, 186, - 181, 182, 196, 13, 142, 25, 191, 137, 54, 216, 69, 44, 60, 245, 173, 11, 60, 36, 172, 198, 251, - 52, 121, 164, 90, 33, 240, 191, 49, 80, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 2, 185, 147, 123, 240, 148, 248, 86, 34, 198, - 131, 175, 51, 114, 107, 43, 204, 245, 218, 82, 25, 242, 116, 139, 149, 210, 133, 242, 17, 38, - 154, 101, 32, 5, 218, 137, 59, 56, 73, 168, 249, 28, 43, 152, 111, 178, 167, 240, 89, 76, 56, - 133, 242, 59, 62, 182, 190, 134, 62, 32, 140, 80, 247, 123, 199, 32, 6, 223, 154, 95, 147, 217, - 149, 32, 138, 151, 234, 22, 143, 159, 75, 217, 151, 154, 104, 108, 248, 56, 234, 28, 33, 72, - 69, 29, 126, 0, 37, 149, 32, 4, 252, 175, 102, 234, 233, 155, 13, 27, 209, 72, 177, 22, 12, 92, - 3, 255, 71, 203, 167, 25, 82, 233, 129, 235, 164, 97, 138, 157, 15, 224, 227, 32, 3, 4, 73, 26, - 107, 250, 0, 254, 2, 39, 152, 224, 93, 86, 160, 54, 98, 205, 34, 55, 115, 248, 65, 5, 85, 167, - 168, 100, 19, 108, 30, 164, 32, 4, 151, 70, 150, 32, 58, 243, 110, 26, 52, 132, 144, 9, 93, - 202, 40, 220, 116, 111, 0, 233, 209, 144, 123, 244, 43, 48, 125, 56, 200, 235, 3, 32, 7, 127, - 159, 97, 231, 114, 184, 127, 220, 238, 3, 123, 44, 24, 215, 185, 40, 149, 223, 63, 86, 149, - 183, 68, 62, 189, 105, 75, 144, 182, 156, 146, 32, 1, 242, 152, 102, 62, 90, 51, 174, 48, 78, - 109, 8, 202, 131, 33, 217, 222, 27, 231, 139, 119, 143, 128, 121, 106, 58, 190, 45, 205, 223, - 41, 116, 32, 5, 21, 67, 101, 92, 112, 182, 22, 84, 9, 131, 189, 140, 148, 40, 249, 183, 170, - 232, 249, 193, 46, 33, 133, 62, 45, 2, 149, 238, 64, 77, 65, 32, 1, 65, 52, 206, 95, 122, 14, - 30, 85, 227, 58, 109, 222, 186, 108, 71, 216, 221, 98, 221, 152, 108, 13, 1, 198, 23, 41, 8, - 233, 179, 120, 47, 32, 5, 107, 85, 58, 191, 186, 3, 85, 152, 193, 223, 228, 85, 180, 89, 225, - 70, 245, 252, 171, 158, 61, 139, 21, 175, 12, 135, 94, 163, 213, 207, 251, 32, 6, 9, 93, 104, - 192, 1, 153, 140, 83, 107, 120, 46, 208, 95, 169, 26, 156, 9, 162, 164, 84, 58, 55, 227, 90, - 131, 153, 24, 187, 111, 161, 118, 32, 2, 237, 53, 11, 10, 229, 176, 24, 193, 157, 67, 178, 202, - 93, 172, 211, 237, 254, 241, 23, 16, 173, 146, 57, 212, 151, 174, 67, 93, 164, 126, 251, 32, 1, - 87, 224, 55, 128, 255, 224, 195, 110, 136, 225, 75, 201, 124, 141, 192, 88, 120, 188, 94, 7, - 157, 228, 197, 153, 139, 73, 12, 237, 6, 108, 164, 32, 3, 197, 13, 245, 114, 224, 240, 118, 54, - 233, 73, 69, 151, 129, 83, 66, 82, 186, 10, 37, 0, 141, 139, 235, 184, 132, 151, 36, 129, 135, - 39, 182, 32, 7, 142, 104, 154, 189, 91, 35, 23, 112, 29, 208, 112, 13, 193, 77, 50, 121, 90, - 210, 166, 219, 18, 52, 194, 6, 215, 110, 80, 233, 244, 215, 99, 32, 5, 207, 93, 57, 54, 120, - 65, 123, 75, 235, 62, 188, 189, 109, 243, 93, 211, 196, 197, 33, 48, 55, 35, 99, 162, 4, 2, - 159, 153, 47, 127, 162, 32, 6, 23, 123, 3, 202, 203, 176, 30, 138, 218, 100, 205, 139, 159, 0, - 136, 166, 43, 52, 40, 249, 161, 79, 73, 173, 139, 128, 250, 62, 195, 20, 91, 32, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, - 255, 255, 254, 239, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 239, 255, 225, 32, 5, 43, 135, 220, 4, 20, 37, 137, - 206, 211, 41, 137, 149, 46, 214, 18, 210, 240, 36, 221, 209, 207, 124, 172, 250, 245, 28, 8, - 16, 17, 187, 180, 32, 7, 130, 204, 117, 190, 204, 151, 230, 31, 62, 230, 126, 81, 28, 157, 207, - 95, 23, 237, 231, 38, 172, 12, 111, 181, 5, 127, 152, 18, 165, 115, 130, 32, 7, 130, 204, 117, - 190, 204, 151, 230, 31, 62, 230, 126, 81, 28, 157, 207, 95, 23, 237, 231, 38, 172, 12, 111, - 181, 5, 127, 152, 18, 165, 115, 130, 32, 7, 130, 204, 117, 190, 204, 151, 230, 31, 62, 230, - 126, 81, 28, 157, 207, 95, 23, 237, 231, 38, 172, 12, 111, 181, 5, 127, 152, 18, 165, 115, 130, - 32, 0, 195, 183, 176, 27, 81, 155, 238, 186, 44, 253, 150, 225, 28, 116, 121, 199, 189, 132, - 56, 156, 128, 223, 126, 144, 120, 103, 174, 137, 206, 189, 111, 32, 2, 145, 177, 146, 33, 101, - 216, 108, 43, 10, 18, 211, 241, 152, 22, 149, 126, 107, 68, 164, 7, 134, 3, 2, 215, 228, 235, - 174, 85, 206, 109, 77, 32, 6, 42, 222, 248, 190, 214, 90, 144, 190, 236, 180, 216, 193, 202, - 174, 71, 122, 123, 252, 57, 144, 230, 252, 155, 241, 80, 13, 96, 156, 255, 10, 149, 32, 4, 68, - 218, 0, 92, 96, 138, 255, 100, 92, 49, 199, 172, 79, 217, 242, 58, 70, 1, 52, 225, 60, 57, 54, - 248, 63, 191, 93, 156, 135, 226, 39, 32, 4, 214, 168, 127, 209, 102, 94, 75, 225, 155, 110, 99, - 132, 166, 120, 148, 232, 208, 173, 212, 19, 37, 165, 201, 253, 171, 249, 216, 200, 161, 1, 118, - 32, 1, 249, 128, 72, 132, 159, 184, 205, 8, 57, 62, 104, 105, 189, 225, 133, 37, 48, 39, 89, - 120, 170, 242, 12, 186, 107, 243, 164, 185, 0, 77, 36, 32, 0, 13, 57, 220, 38, 101, 66, 119, - 46, 127, 223, 61, 100, 178, 87, 192, 77, 25, 10, 210, 231, 197, 254, 80, 117, 73, 176, 223, - 140, 183, 200, 92, 32, 1, 247, 151, 10, 233, 114, 55, 162, 88, 223, 209, 2, 61, 14, 167, 107, - 180, 113, 242, 18, 7, 25, 221, 113, 92, 190, 220, 102, 255, 242, 253, 245, 32, 5, 76, 150, 31, - 58, 213, 156, 176, 156, 225, 222, 111, 214, 3, 218, 248, 193, 118, 209, 180, 2, 68, 230, 241, - 140, 193, 151, 9, 163, 163, 43, 11, 32, 0, 77, 187, 34, 20, 17, 148, 12, 87, 131, 112, 102, 80, - 222, 102, 242, 240, 112, 89, 79, 156, 67, 118, 10, 172, 99, 123, 154, 244, 220, 81, 153, 32, 2, - 216, 81, 197, 140, 173, 120, 115, 163, 163, 34, 123, 36, 16, 140, 65, 231, 37, 111, 83, 253, - 186, 1, 139, 142, 116, 78, 174, 208, 245, 154, 234, 32, 0, 177, 26, 4, 194, 77, 16, 27, 211, - 125, 83, 64, 213, 247, 142, 22, 125, 246, 5, 182, 49, 2, 220, 228, 108, 80, 182, 115, 235, 16, - 39, 211, 32, 5, 57, 201, 227, 165, 230, 95, 132, 210, 221, 23, 250, 28, 71, 18, 136, 147, 136, - 122, 8, 223, 83, 100, 147, 35, 42, 136, 58, 38, 139, 194, 0, 32, 4, 115, 70, 70, 174, 178, 124, - 229, 36, 99, 50, 21, 100, 32, 183, 2, 255, 67, 133, 136, 66, 30, 152, 96, 99, 44, 4, 34, 65, - 147, 109, 184, 32, 4, 143, 181, 126, 212, 37, 1, 42, 209, 89, 50, 94, 11, 131, 138, 135, 221, - 74, 225, 13, 130, 77, 73, 114, 127, 197, 70, 196, 14, 198, 225, 232, 32, 0, 24, 103, 10, 43, - 27, 212, 36, 177, 105, 91, 134, 11, 46, 39, 39, 123, 210, 251, 145, 42, 115, 132, 138, 227, - 247, 39, 228, 116, 72, 27, 137, 32, 6, 104, 97, 213, 61, 114, 233, 56, 174, 178, 84, 22, 186, - 230, 25, 223, 196, 83, 104, 120, 126, 76, 29, 16, 82, 94, 41, 14, 157, 22, 212, 120, 32, 0, 89, - 100, 137, 84, 109, 95, 236, 37, 138, 245, 70, 30, 74, 169, 13, 59, 220, 138, 10, 234, 217, 15, - 149, 234, 11, 106, 31, 132, 215, 240, 40, 32, 6, 219, 186, 252, 174, 179, 10, 70, 25, 43, 128, - 126, 110, 32, 219, 105, 185, 110, 3, 117, 66, 98, 185, 125, 111, 89, 242, 0, 41, 101, 237, 250, - 59, 2, 142, 187, 62, 229, 132, 158, 36, 16, 12, 173, 185, 194, 45, 100, 229, 28, 116, 102, 80, - 139, 203, 122, 239, 107, 5, 71, 213, 187, 233, 15, 65, 88, 2, 32, 2, 123, 105, 208, 90, 184, - 94, 169, 135, 160, 75, 221, 76, 12, 83, 58, 150, 44, 222, 247, 43, 168, 111, 254, 9, 229, 218, - 136, 193, 38, 50, 125, 32, 5, 90, 248, 70, 158, 39, 21, 167, 219, 105, 15, 80, 73, 78, 54, 85, - 222, 25, 214, 217, 1, 139, 163, 182, 40, 119, 191, 90, 95, 181, 168, 20, 5, 131, 111, 215, 225, - 149, 191, 212, 165, 250, 230, 67, 228, 21, 196, 149, 169, 151, 70, 187, 6, 219, 108, 200, 212, - 22, 142, 43, 157, 233, 175, 113, 101, 60, 178, 7, 233, 231, 168, 175, 82, 151, 8, 75, 52, 126, - 226, 164, 124, 179, 40, 51, 25, 109, 186, 82, 217, 248, 241, 165, 152, 84, 38, 36, 56, 182, 46, - 199, 71, 214, 186, 180, 34, 107, 129, 218, 81, 189, 117, 202, 143, 38, 242, 1, 232, 103, 38, - 199, 181, 48, 25, 210, 62, 120, 250, 115, 109, 142, 248, 21, 108, 131, 209, 3, 237, 128, 8, 21, - 38, 229, 195, 183, 189, 201, 218, 168, 202, 146, 234, 61, 47, 232, 139, 2, 98, 207, 216, 127, - 65, 43, 167, 165, 74, 192, 62, 52, 12, 183, 22, 1, 198, 211, 171, 50, 119, 31, 106, 93, 37, 40, - 45, 109, 172, 52, 2, 219, 75, 97, 245, 132, 6, 32, 5, 19, 180, 59, 11, 93, 51, 113, 207, 95, - 59, 39, 22, 228, 23, 196, 80, 162, 247, 170, 137, 110, 239, 216, 103, 44, 162, 15, 54, 95, 242, - 81, 3, 5, 6, 42, 146, 143, 105, 189, 9, 36, 53, 22, 41, 199, 200, 239, 241, 49, 36, 135, 27, - 208, 91, 50, 76, 144, 231, 163, 95, 81, 141, 224, 94, 39, 129, 28, 167, 112, 37, 170, 195, 75, - 31, 3, 249, 44, 216, 93, 185, 212, 203, 185, 51, 96, 28, 107, 236, 26, 223, 67, 30, 161, 79, - 189, 112, 41, 2, 217, 55, 124, 187, 83, 130, 235, 229, 123, 191, 115, 112, 181, 1, 62, 43, 151, - 138, 52, 97, 44, 121, 130, 191, 82, 229, 145, 11, 253, 108, 37, 18, 164, 114, 39, 246, 25, 122, - 147, 124, 188, 15, 237, 244, 151, 73, 129, 251, 101, 39, 14, 172, 92, 177, 43, 79, 219, 230, - 83, 92, 76, 116, 193, 78, 54, 214, 107, 217, 53, 75, 236, 9, 29, 196, 3, 12, 221, 234, 66, 110, - 51, 235, 100, 249, 115, 196, 194, 90, 37, 87, 86, 210, 54, 43, 140, 49, 82, 110, 220, 98, 219, - 151, 74, 66, 63, 234, 88, 226, 184, 60, 116, 111, 157, 27, 198, 138, 102, 90, 221, 230, 21, 32, - 168, 220, 195, 132, 151, 8, 5, 154, 139, 158, 65, 145, 174, 202, 237, 94, 230, 150, 195, 181, - 63, 181, 67, 203, 138, 134, 253, 20, 115, 60, 121, 13, 105, 26, 163, 90, 87, 75, 23, 81, 47, - 160, 54, 183, 238, 223, 55, 102, 143, 95, 114, 242, 126, 232, 14, 52, 66, 88, 19, 234, 210, - 170, 76, 1, 241, 68, 12, 54, 95, 229, 8, 18, 220, 199, 100, 148, 155, 122, 223, 238, 14, 85, - 126, 160, 34, 92, 201, 111, 49, 1, 115, 33, 12, 59, 141, 24, 243, 35, 100, 238, 74, 71, 2, 134, - 178, 104, 173, 162, 236, 213, 249, 202, 164, 95, 107, 11, 56, 101, 109, 71, 98, 241, 182, 217, - 115, 170, 117, 39, 2, 37, 156, 103, 209, 110, 133, 30, 190, 131, 71, 14, 90, 255, 205, 61, 88, - 86, 55, 241, 231, 182, 211, 86, 246, 138, 91, 83, 4, 67, 162, 200, 215, 113, 231, 29, 89, 27, - 236, 4, 8, 97, 5, 233, 202, 248, 7, 241, 51, 89, 212, 117, 176, 184, 114, 90, 77, 25, 102, 24, - 189, 102, 222, 97, 171, 20, 187, 87, 250, 142, 75, 144, 147, 224, 60, 180, 110, 169, 108, 87, - 34, 58, 108, 150, 77, 56, 78, 69, 231, 41, 51, 214, 9, 85, 150, 104, 230, 145, 158, 115, 88, - 215, 168, 68, 172, 202, 195, 100, 183, 8, 248, 87, 224, 249, 36, 214, 116, 154, 201, 95, 128, - 208, 102, 44, 242, 180, 9, 138, 192, 150, 217, 18, 178, 102, 231, 213, 101, 49, 65, 103, 113, - 118, 42, 4, 74, 97, 34, 90, 19, 29, 120, 84, 150, 20, 219, 53, 125, 150, 188, 91, 33, 119, 96, - 191, 101, 209, 206, 167, 3, 12, 15, 199, 83, 63, 68, 148, 118, 143, 47, 180, 140, 68, 117, 103, - 124, 121, 228, 109, 185, 34, 133, 45, 48, 190, 51, 180, 94, 205, 33, 85, 223, 94, 97, 153, 163, - 118, 94, 200, 119, 214, 56, 226, 237, 184, 185, 3, 47, 174, 82, 226, 85, 207, 34, 218, 217, 79, - 247, 221, 132, 131, 238, 20, 142, 122, 7, 17, 56, 158, 86, 253, 71, 38, 234, 208, 24, 28, 219, - 192, 53, 2, 197, 117, 197, 222, 165, 125, 29, 80, 121, 46, 211, 128, 190, 245, 232, 2, 174, - 190, 237, 95, 171, 45, 71, 102, 4, 84, 162, 133, 201, 189, 45, 190, 155, 93, 153, 110, 55, 38, - 24, 17, 253, 255, 248, 218, 237, 159, 167, 246, 208, 150, 210, 243, 55, 236, 104, 72, 242, 85, - 241, 13, 150, 60, 152, 137, 135, 75, 168, 117, 8, 13, 98, 167, 249, 84, 182, 133, 143, 46, 153, - 242, 5, 32, 1, 74, 157, 151, 183, 156, 92, 133, 215, 203, 150, 24, 12, 64, 48, 163, 227, 184, - 206, 142, 139, 154, 66, 209, 87, 132, 11, 195, 40, 40, 190, 85, 32, 1, 217, 35, 239, 188, 188, - 192, 64, 184, 30, 245, 108, 186, 134, 120, 64, 17, 32, 181, 87, 206, 22, 158, 107, 0, 102, 101, - 213, 249, 61, 9, 232, 32, 1, 207, 238, 195, 141, 104, 219, 245, 189, 54, 228, 146, 14, 81, 70, - 45, 149, 48, 248, 144, 13, 3, 66, 227, 193, 193, 196, 32, 109, 79, 123, 127, 32, 0, 106, 40, - 50, 180, 62, 204, 17, 70, 227, 5, 189, 78, 38, 144, 100, 220, 143, 94, 39, 80, 8, 165, 207, - 115, 214, 112, 95, 249, 243, 89, 12, 32, 4, 20, 120, 140, 128, 32, 172, 217, 237, 97, 184, 6, - 77, 133, 94, 181, 191, 237, 48, 207, 60, 84, 223, 138, 203, 209, 88, 9, 95, 129, 248, 87, 5, 6, - 64, 75, 93, 238, 4, 192, 189, 0, 81, 57, 75, 239, 96, 171, 48, 220, 113, 119, 191, 183, 166, - 97, 79, 71, 187, 254, 136, 50, 43, 80, 111, 230, 2, 165, 29, 205, 69, 114, 231, 236, 231, 212, - 144, 47, 5, 228, 237, 157, 140, 166, 29, 37, 207, 36, 72, 224, 220, 137, 31, 196, 121, 6, 119, - 243, 50, 254, 104, 215, 44, 179, 199, 109, 91, 13, 72, 132, 212, 20, 92, 166, 50, 165, 21, 184, - 235, 49, 70, 59, 211, 114, 103, 141, 210, 158, 199, 36, 61, 50, 9, 64, 16, 157, 76, 170, 55, - 31, 126, 17, 30, 246, 105, 96, 151, 80, 219, 5, 50, 116, 121, 231, 29, 21, 122, 70, 54, 57, - 156, 236, 24, 240, 15, 118, 7, 150, 73, 202, 145, 220, 151, 32, 228, 40, 233, 188, 179, 172, - 202, 179, 168, 178, 79, 30, 249, 170, 119, 219, 21, 181, 208, 109, 82, 110, 220, 98, 219, 151, - 74, 66, 63, 234, 88, 226, 184, 60, 116, 111, 157, 27, 198, 138, 102, 90, 221, 230, 21, 32, 168, - 220, 195, 132, 151, 8, 5, 215, 248, 10, 120, 70, 46, 168, 36, 239, 94, 244, 162, 10, 232, 135, - 250, 235, 159, 24, 125, 250, 35, 91, 149, 51, 182, 98, 154, 217, 255, 96, 141, 122, 10, 164, - 59, 190, 4, 139, 80, 244, 120, 217, 166, 11, 127, 37, 199, 187, 152, 225, 197, 146, 30, 8, 167, - 135, 85, 53, 8, 91, 33, 49, 209, 177, 154, 9, 229, 51, 39, 128, 101, 111, 99, 196, 98, 31, 237, - 247, 243, 255, 240, 103, 195, 58, 103, 105, 181, 129, 127, 70, 56, 32, 139, 16, 63, 244, 187, - 244, 53, 159, 58, 150, 244, 180, 228, 32, 201, 165, 29, 165, 29, 46, 243, 27, 174, 71, 176, 71, - 137, 157, 185, 105, 162, 24, 41, 96, 163, 30, 190, 131, 71, 14, 90, 255, 205, 61, 88, 86, 55, - 241, 231, 182, 211, 86, 246, 138, 91, 83, 4, 67, 162, 200, 215, 113, 231, 29, 89, 27, 236, 4, - 44, 55, 157, 49, 94, 50, 241, 208, 158, 123, 172, 41, 145, 155, 118, 113, 169, 84, 191, 38, 7, - 191, 205, 246, 154, 110, 54, 29, 211, 142, 37, 69, 153, 27, 114, 237, 146, 206, 35, 153, 20, - 15, 225, 102, 160, 141, 228, 207, 168, 56, 158, 108, 123, 252, 39, 178, 247, 29, 105, 121, 164, - 1, 3, 59, 195, 44, 247, 30, 168, 201, 22, 128, 112, 164, 224, 249, 3, 150, 71, 41, 228, 182, - 63, 133, 124, 228, 35, 75, 14, 52, 255, 13, 35, 119, 204, 237, 101, 49, 65, 103, 113, 118, 42, - 4, 74, 97, 34, 90, 19, 29, 120, 84, 150, 20, 219, 53, 125, 150, 188, 91, 33, 119, 96, 191, 101, - 209, 206, 167, 3, 243, 187, 32, 47, 177, 239, 237, 95, 205, 58, 118, 26, 105, 43, 121, 6, 192, - 82, 22, 91, 43, 21, 2, 221, 4, 123, 238, 162, 216, 182, 14, 242, 55, 121, 241, 97, 136, 255, - 32, 59, 228, 246, 249, 127, 14, 186, 207, 237, 62, 133, 165, 197, 238, 174, 63, 193, 236, 41, - 244, 230, 135, 22, 233, 89, 122, 7, 17, 56, 158, 86, 253, 71, 38, 234, 208, 24, 28, 219, 192, - 53, 2, 197, 117, 197, 222, 165, 125, 29, 80, 121, 46, 211, 128, 190, 245, 232, 2, 66, 31, 173, - 142, 242, 226, 218, 51, 218, 239, 244, 186, 185, 7, 192, 174, 6, 64, 185, 189, 102, 230, 154, - 121, 222, 83, 93, 113, 161, 209, 208, 242, 208, 150, 210, 243, 55, 236, 104, 72, 242, 85, 241, - 13, 150, 60, 152, 137, 135, 75, 168, 117, 8, 13, 98, 167, 249, 84, 182, 133, 143, 46, 153, 242, - 5, 32, 5, 178, 3, 153, 123, 161, 110, 22, 246, 161, 16, 202, 79, 230, 29, 245, 66, 122, 78, - 201, 207, 76, 89, 189, 69, 208, 74, 14, 213, 208, 70, 119, 32, 4, 77, 21, 142, 151, 108, 12, - 73, 116, 205, 156, 82, 150, 200, 181, 246, 80, 56, 177, 6, 206, 80, 137, 239, 58, 223, 242, 80, - 176, 121, 97, 58, 32, 7, 30, 130, 186, 224, 209, 201, 161, 4, 201, 162, 132, 50, 244, 49, 53, - 58, 25, 231, 77, 159, 3, 219, 150, 214, 18, 48, 89, 39, 6, 253, 136, 32, 0, 54, 193, 58, 129, - 117, 159, 55, 195, 87, 173, 113, 30, 115, 217, 35, 33, 207, 15, 191, 135, 228, 83, 167, 39, 73, - 62, 112, 68, 9, 1, 150, 32, 4, 174, 89, 55, 121, 87, 136, 31, 33, 180, 244, 180, 251, 105, 77, - 59, 212, 61, 60, 110, 210, 110, 1, 71, 49, 208, 61, 220, 57, 227, 63, 209, 5, 6, 247, 13, 180, - 180, 158, 173, 253, 235, 243, 175, 214, 56, 254, 143, 193, 143, 39, 105, 86, 111, 165, 0, 95, - 33, 67, 58, 127, 165, 181, 92, 245, 245, 21, 180, 109, 23, 52, 21, 241, 87, 95, 12, 121, 0, - 171, 134, 79, 147, 78, 235, 143, 203, 127, 89, 113, 244, 16, 158, 84, 14, 19, 56, 111, 197, 59, - 112, 96, 156, 179, 119, 54, 45, 151, 105, 203, 11, 147, 83, 29, 143, 109, 14, 253, 202, 219, 0, - 36, 251, 111, 84, 56, 31, 204, 107, 221, 82, 122, 86, 151, 156, 205, 35, 12, 166, 246, 65, 45, - 17, 166, 246, 236, 196, 127, 148, 53, 93, 56, 227, 82, 118, 221, 90, 67, 8, 83, 161, 193, 206, - 226, 86, 133, 213, 224, 17, 2, 218, 146, 61, 58, 229, 173, 121, 253, 228, 83, 102, 129, 12, - 212, 84, 198, 181, 157, 44, 135, 95, 117, 204, 93, 31, 27, 108, 170, 108, 132, 143, 62, 226, - 84, 214, 99, 60, 118, 135, 230, 92, 139, 129, 62, 211, 204, 120, 144, 245, 165, 61, 171, 142, - 4, 134, 208, 66, 5, 246, 87, 134, 147, 55, 213, 225, 95, 208, 14, 125, 138, 160, 223, 40, 15, - 59, 150, 247, 215, 7, 118, 163, 215, 169, 17, 115, 125, 38, 134, 44, 10, 198, 98, 172, 189, 12, - 131, 140, 246, 245, 63, 217, 238, 52, 100, 80, 33, 104, 238, 105, 120, 58, 180, 248, 33, 119, - 75, 110, 169, 220, 127, 133, 4, 114, 142, 186, 36, 124, 122, 76, 229, 41, 174, 187, 167, 231, - 156, 4, 52, 114, 137, 156, 196, 134, 72, 34, 84, 79, 207, 128, 123, 188, 128, 103, 109, 58, - 227, 29, 235, 2, 179, 217, 189, 123, 155, 115, 18, 172, 21, 198, 228, 229, 39, 209, 136, 4, - 220, 30, 72, 152, 38, 64, 46, 99, 163, 18, 58, 134, 224, 244, 234, 207, 106, 89, 247, 123, 173, - 94, 233, 167, 80, 74, 197, 128, 149, 52, 100, 163, 208, 106, 208, 44, 71, 17, 161, 152, 1, 189, - 251, 4, 206, 196, 179, 98, 253, 74, 122, 97, 96, 155, 253, 208, 186, 219, 50, 188, 150, 18, - 196, 176, 59, 54, 208, 205, 0, 7, 222, 250, 247, 59, 2, 241, 111, 236, 132, 147, 136, 244, 23, - 243, 255, 104, 201, 209, 23, 26, 0, 63, 215, 105, 123, 202, 203, 52, 19, 40, 55, 192, 145, 144, - 155, 62, 28, 204, 243, 173, 86, 68, 46, 77, 210, 199, 157, 54, 4, 53, 203, 177, 125, 87, 13, - 190, 91, 171, 188, 211, 51, 67, 24, 66, 172, 240, 10, 112, 57, 234, 45, 208, 198, 35, 225, 94, - 4, 67, 168, 163, 9, 115, 213, 202, 241, 15, 82, 19, 28, 60, 37, 115, 163, 22, 157, 181, 91, - 179, 247, 24, 39, 224, 3, 249, 58, 62, 205, 43, 235, 252, 241, 205, 72, 94, 190, 238, 32, 242, - 169, 169, 62, 226, 207, 214, 41, 93, 168, 37, 137, 39, 12, 220, 62, 135, 34, 233, 239, 83, 191, - 72, 141, 52, 205, 215, 25, 177, 114, 235, 86, 108, 250, 216, 80, 19, 206, 41, 201, 178, 58, - 169, 124, 182, 9, 20, 222, 252, 16, 147, 40, 185, 95, 251, 15, 52, 220, 80, 64, 48, 110, 74, - 142, 151, 33, 95, 115, 137, 131, 180, 112, 225, 9, 153, 195, 124, 4, 104, 40, 97, 205, 2, 131, - 236, 59, 162, 100, 50, 120, 9, 165, 229, 53, 225, 35, 47, 250, 6, 153, 222, 165, 238, 183, 136, - 243, 54, 158, 248, 39, 243, 206, 154, 114, 150, 197, 4, 245, 21, 199, 46, 47, 153, 231, 22, 80, - 211, 28, 217, 26, 134, 122, 66, 79, 126, 134, 35, 186, 61, 21, 181, 213, 151, 94, 38, 137, 251, - 5, 32, 7, 60, 33, 40, 197, 19, 95, 219, 48, 49, 12, 197, 46, 185, 87, 27, 15, 58, 60, 126, 69, - 212, 159, 144, 120, 206, 151, 40, 28, 82, 215, 160, 32, 1, 91, 143, 225, 210, 13, 133, 79, 238, - 88, 182, 100, 108, 148, 153, 227, 240, 14, 35, 115, 120, 228, 147, 12, 67, 56, 137, 17, 65, 22, - 42, 244, 32, 5, 112, 78, 243, 219, 23, 105, 160, 148, 236, 114, 184, 173, 89, 241, 35, 117, 84, - 226, 73, 237, 176, 225, 162, 181, 188, 56, 187, 1, 110, 211, 34, 32, 4, 105, 252, 11, 132, 243, - 84, 26, 115, 115, 30, 185, 156, 33, 35, 142, 134, 211, 217, 34, 152, 233, 141, 222, 117, 214, - 75, 54, 88, 116, 170, 228, 32, 7, 59, 222, 227, 71, 114, 227, 250, 69, 27, 218, 4, 17, 238, 21, - 48, 63, 14, 163, 69, 9, 130, 202, 234, 103, 149, 126, 69, 180, 14, 214, 79, 3, 7, 143, 227, - 159, 164, 85, 246, 234, 50, 196, 91, 38, 46, 24, 101, 98, 89, 172, 100, 14, 251, 66, 137, 81, - 246, 211, 156, 208, 4, 122, 213, 140, 130, 53, 82, 202, 111, 213, 169, 243, 94, 112, 245, 75, - 185, 11, 236, 112, 194, 41, 200, 247, 216, 56, 153, 254, 12, 227, 169, 77, 8, 103, 81, 25, 187, - 88, 142, 120, 152, 104, 74, 247, 5, 9, 0, 23, 158, 66, 243, 222, 4, 163, 88, 57, 211, 75, 7, - 149, 187, 187, 81, 34, 2, 179, 160, 148, 228, 227, 113, 247, 198, 39, 137, 45, 114, 186, 121, - 135, 78, 55, 65, 177, 99, 102, 65, 28, 76, 48, 138, 171, 217, 51, 31, 154, 190, 174, 97, 168, - 117, 172, 237, 224, 114, 8, 103, 84, 1, 246, 211, 98, 97, 14, 58, 110, 46, 169, 251, 164, 131, - 193, 178, 14, 208, 74, 62, 157, 44, 103, 207, 112, 173, 216, 171, 182, 141, 100, 237, 193, 155, - 100, 252, 234, 227, 240, 128, 133, 102, 185, 99, 234, 200, 182, 96, 74, 192, 13, 209, 71, 115, - 104, 124, 179, 164, 223, 89, 216, 115, 42, 45, 247, 206, 176, 156, 12, 153, 45, 13, 251, 215, - 172, 248, 210, 214, 153, 245, 13, 166, 237, 50, 171, 170, 143, 159, 139, 197, 2, 32, 5, 13, - 111, 60, 39, 59, 146, 219, 116, 228, 231, 248, 193, 28, 12, 49, 93, 76, 134, 49, 58, 243, 125, - 164, 34, 169, 149, 164, 222, 107, 88, 131, 32, 1, 61, 210, 239, 49, 241, 173, 101, 137, 53, 46, - 255, 2, 205, 110, 136, 200, 30, 203, 225, 204, 208, 69, 74, 28, 171, 97, 96, 101, 206, 8, 61, - 2, 8, 246, 125, 95, 99, 25, 154, 3, 111, 151, 225, 16, 65, 107, 46, 18, 178, 23, 245, 118, 82, - 79, 97, 220, 72, 4, 165, 61, 87, 162, 27, 17, 109, 151, 203, 47, 152, 214, 200, 164, 98, 254, - 213, 134, 8, 241, 226, 104, 150, 244, 74, 115, 219, 255, 83, 218, 101, 111, 191, 131, 194, 62, - 167, 55, 72, 152, 27, 86, 66, 6, 244, 122, 121, 144, 46, 166, 66, 124, 203, 204, 235, 75, 61, - 217, 136, 85, 11, 213, 20, 21, 113, 177, 117, 29, 68, 188, 151, 243, 240, 99, 46, 3, 241, 18, - 179, 194, 224, 200, 241, 109, 149, 189, 60, 148, 99, 5, 35, 145, 128, 128, 21, 203, 239, 181, - 228, 240, 47, 135, 220, 34, 178, 167, 247, 106, 173, 158, 22, 63, 212, 201, 160, 117, 48, 148, - 208, 146, 236, 225, 34, 210, 178, 74, 27, 219, 146, 18, 246, 147, 54, 127, 158, 168, 87, 74, - 115, 111, 31, 193, 215, 116, 252, 153, 159, 179, 50, 108, 250, 181, 183, 86, 85, 81, 49, 50, - 182, 5, 156, 167, 125, 1, 203, 193, 96, 229, 23, 56, 185, 8, 183, 136, 50, 181, 158, 63, 15, - 141, 232, 151, 145, 44, 104, 52, 24, 145, 194, 228, 252, 233, 233, 188, 144, 1, 19, 65, 2, 229, - 146, 117, 67, 238, 190, 16, 96, 92, 169, 231, 87, 127, 81, 158, 229, 8, 37, 233, 213, 154, 139, - 101, 103, 128, 137, 232, 243, 179, 19, 171, 248, 8, 139, 146, 93, 32, 16, 112, 171, 228, 8, - 103, 32, 53, 33, 214, 236, 138, 223, 220, 8, 214, 104, 208, 254, 31, 143, 123, 12, 124, 21, 0, - 189, 144, 89, 215, 92, 202, 34, 161, 165, 176, 85, 85, 31, 163, 26, 106, 45, 165, 211, 109, 81, - 191, 141, 172, 192, 132, 37, 20, 17, 237, 17, 202, 4, 88, 20, 249, 223, 116, 21, 255, 35, 4, - 158, 8, 223, 44, 158, 133, 39, 80, 104, 164, 35, 21, 203, 9, 235, 52, 205, 135, 20, 82, 240, - 165, 60, 28, 223, 34, 9, 183, 92, 14, 152, 76, 254, 209, 126, 53, 54, 232, 220, 227, 8, 161, - 218, 16, 26, 241, 254, 53, 194, 219, 149, 106, 67, 173, 42, 36, 13, 233, 203, 206, 205, 110, - 99, 70, 226, 94, 86, 244, 125, 108, 41, 118, 254, 55, 55, 222, 22, 92, 112, 154, 65, 212, 142, - 59, 60, 3, 107, 208, 156, 230, 253, 184, 103, 128, 240, 14, 203, 86, 220, 97, 117, 207, 209, - 61, 122, 165, 43, 161, 50, 96, 156, 89, 156, 209, 198, 59, 100, 165, 195, 131, 9, 142, 113, - 217, 122, 61, 153, 59, 9, 163, 241, 6, 113, 196, 218, 94, 216, 173, 188, 168, 35, 191, 202, - 239, 105, 5, 128, 163, 172, 35, 193, 113, 25, 50, 185, 158, 146, 208, 222, 191, 50, 54, 152, - 19, 171, 153, 57, 88, 69, 45, 152, 185, 233, 13, 250, 36, 169, 142, 104, 60, 213, 67, 240, 88, - 59, 32, 5, 92, 52, 85, 123, 76, 75, 71, 12, 135, 142, 95, 97, 171, 243, 222, 13, 213, 213, 194, - 234, 62, 130, 171, 154, 52, 96, 191, 146, 113, 201, 190, 32, 6, 232, 75, 105, 216, 232, 185, - 94, 150, 42, 145, 240, 213, 165, 192, 92, 184, 241, 37, 164, 35, 140, 48, 101, 236, 184, 251, - 80, 232, 145, 232, 249, 32, 3, 235, 154, 88, 151, 144, 214, 105, 191, 102, 136, 162, 179, 198, - 105, 240, 157, 63, 163, 179, 114, 126, 210, 237, 254, 242, 159, 21, 244, 1, 147, 53, 32, 0, - 150, 104, 12, 176, 104, 99, 55, 78, 147, 229, 56, 248, 240, 151, 216, 170, 156, 13, 92, 146, - 202, 223, 224, 63, 168, 139, 76, 5, 57, 90, 230, 32, 0, 170, 80, 220, 20, 120, 44, 45, 93, 55, - 195, 241, 25, 149, 240, 181, 81, 166, 106, 76, 179, 9, 212, 201, 118, 245, 76, 241, 165, 67, - 56, 189, 32, 1, 85, 112, 183, 233, 88, 36, 146, 137, 205, 111, 188, 80, 159, 240, 177, 80, 126, - 150, 5, 22, 85, 15, 34, 36, 42, 99, 108, 36, 194, 127, 217, 32, 5, 139, 126, 223, 223, 52, 245, - 110, 171, 254, 45, 249, 136, 43, 63, 186, 150, 174, 200, 71, 151, 33, 156, 127, 123, 81, 146, - 182, 222, 117, 238, 38, 32, 2, 197, 191, 111, 239, 154, 122, 183, 85, 255, 22, 252, 196, 21, - 159, 221, 75, 87, 100, 35, 203, 144, 206, 63, 189, 168, 201, 91, 111, 58, 247, 19, 32, 6, 194, - 93, 215, 144, 192, 203, 65, 94, 79, 191, 179, 39, 253, 250, 72, 249, 230, 182, 24, 34, 4, 236, - 172, 146, 14, 251, 246, 95, 231, 32, 103, 32, 3, 215, 1, 13, 172, 227, 164, 234, 16, 137, 143, - 45, 85, 29, 235, 130, 1, 253, 33, 38, 96, 197, 145, 190, 238, 224, 229, 181, 31, 197, 91, 93, - 32, 5, 219, 228, 219, 47, 81, 157, 136, 93, 145, 1, 29, 241, 254, 232, 52, 150, 198, 117, 63, - 96, 191, 176, 228, 93, 80, 200, 0, 81, 235, 148, 9, 32, 0, 234, 250, 115, 49, 120, 81, 154, - 224, 95, 238, 150, 6, 89, 165, 154, 77, 149, 181, 180, 65, 183, 131, 205, 6, 188, 185, 91, 110, - 113, 217, 21, 32, 1, 134, 213, 164, 115, 155, 137, 177, 248, 14, 7, 56, 88, 252, 79, 118, 186, - 155, 96, 180, 62, 33, 1, 235, 252, 135, 49, 213, 91, 77, 212, 230, 32, 4, 75, 246, 46, 142, - 177, 76, 55, 7, 181, 195, 241, 227, 138, 157, 249, 28, 134, 159, 120, 190, 87, 198, 58, 245, - 173, 119, 125, 45, 238, 75, 203, 32, 7, 133, 121, 54, 224, 76, 52, 1, 55, 43, 22, 45, 183, 184, - 121, 86, 226, 126, 83, 194, 155, 104, 104, 170, 46, 17, 83, 7, 63, 64, 202, 195, 32, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 4, - 31, 62, 119, 18, 164, 241, 141, 66, 145, 243, 171, 136, 61, 188, 88, 40, 210, 183, 88, 83, 208, - 116, 129, 226, 244, 177, 198, 19, 25, 45, 118, 32, 1, 114, 239, 57, 20, 92, 73, 237, 66, 30, - 83, 248, 67, 183, 249, 234, 180, 93, 182, 215, 20, 250, 193, 217, 193, 199, 35, 19, 44, 1, 91, - 159, 32, 4, 49, 166, 118, 44, 12, 6, 54, 161, 109, 72, 41, 136, 16, 88, 210, 152, 120, 181, 40, - 29, 13, 38, 112, 242, 37, 86, 68, 209, 135, 245, 55, 32, 7, 61, 155, 178, 150, 133, 53, 24, - 235, 254, 24, 71, 16, 31, 104, 40, 224, 201, 119, 35, 183, 123, 34, 101, 143, 97, 157, 227, 77, - 193, 0, 193, 32, 0, 187, 189, 126, 156, 164, 113, 130, 134, 78, 5, 158, 86, 41, 46, 155, 83, - 203, 137, 93, 50, 224, 115, 165, 133, 112, 244, 141, 185, 44, 15, 47, 32, 5, 36, 146, 245, 209, - 92, 113, 76, 84, 114, 97, 47, 11, 7, 67, 140, 141, 232, 35, 219, 22, 116, 112, 95, 62, 225, 3, - 128, 84, 248, 91, 159, 32, 1, 212, 83, 42, 236, 11, 67, 181, 186, 109, 82, 251, 101, 180, 212, - 26, 181, 161, 247, 8, 54, 113, 207, 228, 122, 64, 188, 52, 63, 125, 133, 138, 32, 4, 27, 234, - 167, 243, 234, 183, 150, 123, 113, 125, 158, 70, 205, 139, 217, 169, 251, 221, 245, 114, 182, - 0, 179, 28, 42, 92, 63, 185, 197, 47, 167, 32, 1, 24, 80, 227, 238, 250, 36, 61, 84, 245, 17, - 67, 221, 98, 82, 154, 205, 170, 128, 248, 246, 254, 116, 15, 222, 25, 217, 198, 116, 99, 208, - 211, 32, 7, 192, 92, 180, 117, 92, 216, 160, 116, 35, 248, 203, 238, 6, 99, 92, 17, 244, 201, - 231, 14, 109, 216, 110, 50, 162, 171, 5, 132, 110, 98, 55, 32, 0, 238, 155, 244, 186, 68, 76, - 110, 255, 84, 88, 29, 221, 9, 56, 245, 214, 127, 62, 193, 108, 230, 164, 147, 199, 218, 209, - 172, 100, 241, 13, 137, 32, 1, 76, 198, 2, 52, 208, 121, 145, 148, 183, 174, 0, 143, 238, 56, - 180, 89, 130, 176, 203, 192, 211, 160, 152, 28, 209, 27, 180, 15, 238, 79, 220, 32, 0, 94, 97, - 8, 176, 199, 131, 5, 185, 53, 220, 104, 126, 4, 17, 6, 87, 163, 169, 235, 231, 83, 169, 25, - 162, 180, 224, 31, 3, 157, 100, 2, 32, 1, 123, 221, 86, 254, 138, 203, 73, 10, 146, 70, 168, - 22, 115, 67, 195, 208, 22, 17, 67, 35, 225, 171, 161, 168, 9, 42, 244, 85, 124, 232, 117, 32, - 7, 237, 86, 214, 195, 3, 73, 238, 178, 102, 94, 206, 89, 8, 143, 249, 253, 139, 80, 210, 35, - 239, 18, 29, 13, 36, 128, 18, 196, 207, 70, 125, 32, 0, 171, 2, 8, 46, 127, 219, 0, 200, 218, - 195, 4, 85, 97, 149, 240, 23, 194, 86, 204, 82, 4, 10, 227, 168, 28, 247, 84, 221, 199, 166, - 96, 32, 2, 220, 195, 38, 166, 138, 57, 16, 158, 59, 177, 179, 224, 251, 151, 91, 173, 87, 129, - 167, 13, 167, 136, 26, 133, 56, 139, 58, 206, 196, 139, 85, 32, 4, 184, 14, 164, 191, 190, 79, - 1, 253, 83, 90, 74, 196, 40, 2, 255, 119, 198, 49, 4, 204, 196, 127, 255, 171, 19, 83, 229, 43, - 244, 89, 242, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, 255, 254, 239, 253, 240, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 255, 225, 32, 7, 95, - 254, 110, 172, 185, 145, 29, 145, 41, 126, 136, 71, 179, 60, 206, 13, 172, 80, 87, 213, 98, - 140, 51, 123, 192, 128, 77, 225, 165, 29, 11, 32, 5, 79, 197, 111, 119, 238, 239, 226, 181, - 176, 201, 176, 69, 93, 192, 48, 239, 4, 245, 181, 0, 51, 111, 240, 229, 65, 40, 182, 91, 185, - 62, 155, 32, 5, 79, 197, 111, 119, 238, 239, 226, 181, 176, 201, 176, 69, 93, 192, 48, 239, 4, - 245, 181, 0, 51, 111, 240, 229, 65, 40, 182, 91, 185, 62, 155, 32, 5, 79, 197, 111, 119, 238, - 239, 226, 181, 176, 201, 176, 69, 93, 192, 48, 239, 4, 245, 181, 0, 51, 111, 240, 229, 65, 40, - 182, 91, 185, 62, 155, 32, 4, 252, 253, 219, 98, 235, 1, 204, 130, 73, 104, 130, 241, 14, 181, - 164, 188, 201, 84, 224, 141, 218, 53, 238, 51, 92, 56, 215, 112, 24, 63, 67, 32, 1, 221, 104, - 207, 214, 234, 23, 212, 72, 72, 223, 138, 254, 34, 254, 30, 157, 250, 247, 104, 201, 184, 9, - 214, 16, 211, 224, 179, 62, 98, 135, 71, 32, 1, 45, 45, 244, 41, 7, 107, 237, 73, 54, 146, 111, - 201, 76, 179, 1, 48, 39, 115, 194, 83, 98, 211, 128, 170, 255, 146, 68, 247, 75, 213, 123, 32, - 2, 39, 18, 130, 68, 27, 72, 204, 89, 15, 233, 51, 152, 239, 65, 126, 59, 35, 100, 95, 163, 17, - 146, 124, 174, 132, 194, 223, 243, 66, 87, 95, 32, 6, 242, 48, 144, 135, 136, 220, 80, 197, - 171, 151, 173, 56, 72, 145, 211, 42, 222, 63, 165, 46, 230, 181, 178, 147, 51, 13, 29, 117, 94, - 177, 134, 32, 0, 177, 205, 201, 82, 130, 135, 23, 113, 37, 195, 172, 83, 156, 70, 192, 252, - 139, 210, 192, 129, 54, 123, 138, 246, 227, 219, 195, 203, 135, 159, 10, 32, 4, 206, 172, 5, - 76, 142, 251, 69, 175, 249, 229, 48, 238, 251, 132, 18, 238, 83, 86, 73, 2, 75, 107, 243, 4, - 219, 195, 155, 244, 160, 34, 146, 32, 6, 77, 188, 199, 14, 153, 137, 235, 178, 108, 41, 52, - 190, 202, 212, 61, 117, 147, 67, 154, 154, 7, 11, 128, 7, 138, 26, 71, 208, 18, 238, 162, 32, - 4, 158, 18, 211, 94, 21, 126, 27, 164, 224, 57, 54, 219, 85, 164, 194, 4, 18, 186, 148, 140, - 225, 123, 20, 253, 226, 231, 242, 232, 59, 75, 114, 32, 7, 82, 225, 106, 213, 181, 251, 229, - 245, 141, 28, 102, 56, 227, 134, 147, 222, 88, 44, 147, 253, 87, 237, 98, 228, 245, 102, 115, - 129, 121, 115, 53, 32, 4, 21, 124, 52, 105, 105, 201, 201, 3, 166, 177, 113, 24, 188, 222, 104, - 194, 243, 143, 185, 47, 43, 212, 198, 106, 140, 225, 97, 59, 249, 182, 93, 32, 0, 139, 253, - 113, 84, 136, 231, 29, 244, 142, 97, 229, 189, 202, 247, 216, 105, 188, 90, 49, 54, 165, 107, - 228, 37, 127, 72, 153, 1, 159, 9, 235, 32, 0, 102, 63, 150, 77, 20, 175, 46, 241, 26, 185, 41, - 86, 238, 31, 2, 48, 126, 178, 249, 40, 187, 53, 117, 92, 100, 202, 232, 186, 129, 240, 170, 32, - 5, 51, 43, 225, 105, 117, 193, 186, 141, 109, 200, 150, 161, 146, 254, 97, 68, 22, 216, 227, - 179, 35, 99, 222, 229, 154, 148, 61, 20, 63, 19, 109, 32, 1, 28, 141, 93, 203, 14, 143, 10, 56, - 172, 211, 225, 75, 212, 98, 190, 69, 191, 38, 78, 106, 235, 248, 139, 142, 226, 121, 22, 153, - 87, 26, 22, 32, 3, 223, 197, 111, 201, 214, 99, 240, 7, 102, 65, 235, 52, 148, 98, 126, 137, - 140, 124, 86, 219, 38, 81, 2, 134, 140, 223, 87, 151, 91, 181, 254, 32, 4, 166, 152, 37, 97, - 106, 1, 182, 52, 191, 198, 51, 55, 199, 216, 50, 53, 76, 231, 180, 220, 136, 11, 247, 9, 143, - 192, 120, 126, 188, 52, 75, 32, 6, 246, 219, 170, 14, 210, 78, 50, 111, 150, 20, 187, 234, 96, - 189, 38, 248, 14, 39, 95, 179, 87, 36, 108, 25, 127, 125, 167, 25, 129, 57, 74, 32, 4, 125, - 171, 78, 47, 135, 249, 13, 169, 13, 166, 140, 79, 38, 18, 223, 11, 175, 204, 186, 223, 223, - 212, 20, 183, 6, 94, 227, 11, 224, 219, 165, 7, 30, 56, 95, 93, 65, 107, 32, 178, 223, 114, - 163, 90, 50, 171, 52, 10, 213, 111, 89, 246, 18, 76, 178, 252, 77, 219, 102, 185, 114, 197, 12, - 114, 192, 138, 45, 225, 107, 22, 106, 224, 41, 137, 103, 248, 223, 22, 39, 61, 244, 177, 198, - 228, 253, 97, 173, 154, 127, 57, 66, 129, 207, 106, 251, 250, 124, 108, 169, 249, 27, 120, 207, - 2, 180, 162, 185, 140, 51, 198, 145, 199, 171, 237, 142, 132, 163, 120, 55, 143, 186, 52, 166, - 177, 88, 152, 150, 179, 120, 123, 7, 138, 22, 8, 51, 13, 121, 209, 194, 84, 146, 81, 13, 118, - 191, 45, 15, 209, 108, 251, 217, 50, 208, 151, 247, 91, 64, 237, 174, 212, 192, 63, 52, 37, - 215, 6, 133, 135, 109, 38, 76, 158, 194, 112, 134, 65, 161, 23, 130, 7, 29, 86, 115, 26, 184, - 186, 17, 7, 4, 35, 160, 91, 122, 92, 47, 92, 145, 117, 116, 211, 43, 187, 31, 53, 154, 224, - 251, 167, 31, 167, 102, 25, 129, 146, 233, 128, 114, 177, 12, 67, 71, 44, 105, 254, 223, 89, - 216, 115, 42, 45, 247, 206, 176, 156, 12, 153, 45, 13, 251, 215, 172, 248, 210, 214, 153, 245, - 13, 166, 237, 50, 171, 170, 143, 159, 139, 197, 2, 32, 5, 122, 15, 22, 10, 222, 7, 141, 106, - 185, 213, 182, 78, 156, 247, 30, 131, 31, 96, 185, 53, 44, 175, 52, 103, 249, 122, 2, 119, 203, - 48, 123, 32, 0, 95, 225, 163, 231, 83, 239, 204, 46, 84, 198, 238, 164, 16, 232, 56, 235, 142, - 213, 20, 111, 225, 130, 98, 219, 68, 235, 253, 122, 155, 164, 100, 2, 8, 146, 244, 224, 197, - 117, 213, 192, 188, 114, 247, 51, 255, 118, 31, 230, 101, 225, 189, 171, 161, 46, 4, 222, 233, - 208, 72, 240, 45, 198, 29, 108, 255, 219, 22, 52, 188, 101, 175, 238, 251, 217, 42, 252, 130, - 173, 185, 82, 55, 1, 61, 248, 122, 61, 130, 157, 213, 235, 44, 103, 164, 94, 114, 88, 245, 99, - 20, 43, 249, 57, 98, 86, 177, 77, 57, 34, 62, 194, 40, 56, 105, 63, 142, 57, 215, 243, 49, 109, - 59, 109, 185, 142, 251, 56, 193, 39, 252, 195, 136, 193, 240, 72, 65, 241, 249, 71, 116, 224, - 227, 15, 162, 221, 45, 63, 46, 167, 114, 124, 200, 166, 251, 114, 77, 178, 34, 84, 169, 165, - 246, 66, 73, 78, 113, 115, 1, 120, 194, 171, 65, 135, 89, 54, 133, 205, 114, 2, 37, 232, 112, - 176, 15, 197, 164, 17, 248, 213, 189, 94, 22, 162, 91, 206, 237, 171, 111, 228, 106, 7, 67, - 203, 137, 134, 203, 148, 74, 76, 195, 150, 243, 35, 225, 237, 255, 101, 214, 133, 134, 186, - 220, 164, 167, 111, 79, 98, 26, 96, 213, 132, 26, 235, 97, 233, 166, 103, 42, 66, 145, 182, - 169, 127, 195, 28, 89, 69, 244, 193, 213, 237, 209, 167, 164, 31, 25, 35, 187, 229, 146, 117, - 67, 238, 190, 16, 96, 92, 169, 231, 87, 127, 81, 158, 229, 8, 37, 233, 213, 154, 139, 101, 103, - 128, 137, 232, 243, 179, 19, 171, 248, 8, 190, 219, 231, 208, 130, 25, 160, 241, 83, 152, 82, - 255, 6, 105, 60, 98, 180, 167, 18, 207, 242, 219, 206, 231, 234, 133, 23, 46, 31, 210, 98, 55, - 3, 185, 241, 81, 183, 10, 160, 199, 70, 246, 114, 29, 188, 19, 192, 161, 232, 150, 188, 199, - 186, 119, 192, 189, 101, 36, 57, 192, 251, 86, 202, 34, 218, 223, 60, 208, 196, 229, 46, 246, - 84, 27, 164, 74, 125, 185, 72, 240, 169, 55, 218, 7, 246, 177, 217, 159, 216, 67, 57, 112, 240, - 196, 8, 169, 236, 252, 187, 134, 180, 238, 106, 87, 248, 45, 26, 51, 89, 121, 208, 51, 64, 232, - 125, 22, 84, 176, 171, 67, 74, 22, 183, 244, 158, 204, 152, 84, 181, 211, 172, 142, 234, 220, - 9, 99, 223, 22, 246, 69, 69, 234, 146, 80, 90, 45, 203, 22, 48, 132, 255, 43, 182, 41, 114, - 225, 11, 234, 215, 180, 37, 148, 50, 36, 99, 91, 230, 108, 72, 148, 11, 43, 179, 194, 199, 77, - 158, 6, 76, 215, 176, 65, 68, 202, 156, 111, 130, 150, 8, 177, 76, 131, 101, 145, 253, 3, 135, - 186, 26, 10, 98, 199, 227, 177, 37, 212, 10, 191, 95, 30, 178, 47, 58, 193, 111, 161, 13, 124, - 159, 148, 167, 229, 75, 215, 25, 50, 185, 158, 146, 208, 222, 191, 50, 54, 152, 19, 171, 153, - 57, 88, 69, 45, 152, 185, 233, 13, 250, 36, 169, 142, 104, 60, 213, 67, 240, 88, 59, 32, 5, 25, - 218, 170, 21, 19, 197, 25, 96, 62, 135, 47, 191, 131, 75, 242, 52, 121, 158, 219, 78, 198, 71, - 224, 95, 226, 34, 50, 195, 97, 89, 175, 32, 5, 104, 136, 248, 25, 231, 79, 87, 221, 122, 76, - 120, 203, 74, 63, 155, 73, 158, 1, 72, 120, 34, 139, 5, 163, 50, 15, 139, 250, 128, 32, 25, 32, - 1, 238, 94, 9, 224, 129, 19, 9, 4, 253, 148, 151, 58, 16, 214, 120, 153, 100, 172, 59, 143, - 249, 45, 255, 70, 94, 115, 225, 168, 145, 49, 25, 32, 0, 93, 48, 192, 190, 49, 227, 239, 122, - 111, 235, 47, 28, 94, 105, 63, 198, 208, 250, 34, 195, 198, 89, 19, 38, 18, 247, 105, 195, 41, - 0, 101, 32, 6, 149, 110, 208, 77, 35, 168, 29, 225, 72, 230, 174, 157, 195, 117, 212, 55, 6, - 122, 99, 217, 208, 158, 171, 108, 77, 85, 215, 0, 68, 19, 162, 32, 5, 125, 223, 60, 106, 149, - 196, 158, 212, 162, 97, 92, 191, 247, 123, 178, 77, 198, 155, 218, 121, 49, 27, 32, 89, 255, - 19, 80, 114, 146, 14, 154, 32, 1, 55, 176, 227, 181, 169, 97, 105, 167, 180, 225, 145, 124, 24, - 76, 11, 173, 19, 97, 50, 84, 31, 175, 125, 135, 189, 252, 97, 157, 245, 39, 232, 32, 0, 155, - 216, 113, 218, 212, 176, 180, 211, 218, 112, 200, 190, 12, 38, 5, 214, 137, 176, 153, 42, 15, - 215, 190, 195, 222, 254, 48, 206, 250, 147, 244, 32, 2, 124, 161, 58, 67, 16, 110, 14, 171, - 135, 79, 98, 202, 148, 3, 151, 4, 109, 88, 109, 184, 59, 154, 206, 149, 3, 188, 179, 216, 242, - 166, 76, 32, 2, 108, 234, 247, 211, 61, 236, 193, 178, 197, 162, 71, 76, 221, 226, 176, 114, - 195, 155, 31, 18, 206, 116, 191, 29, 191, 163, 2, 209, 175, 127, 115, 32, 4, 189, 134, 36, 68, - 156, 24, 101, 33, 49, 47, 4, 216, 201, 207, 243, 175, 165, 4, 6, 66, 160, 21, 67, 204, 9, 82, - 199, 98, 221, 232, 238, 32, 3, 27, 229, 34, 251, 197, 33, 70, 65, 72, 194, 146, 254, 5, 26, - 220, 146, 165, 98, 198, 139, 10, 88, 15, 161, 4, 184, 48, 78, 59, 148, 233, 32, 2, 197, 111, - 192, 47, 2, 137, 23, 222, 237, 84, 236, 179, 134, 194, 191, 90, 229, 136, 198, 1, 189, 10, 4, - 17, 22, 116, 128, 252, 191, 98, 87, 32, 2, 40, 158, 82, 67, 243, 218, 62, 217, 54, 60, 27, 133, - 87, 170, 180, 184, 221, 24, 203, 172, 246, 156, 133, 147, 197, 206, 36, 211, 14, 144, 47, 32, - 7, 67, 4, 42, 119, 160, 2, 220, 46, 53, 53, 12, 46, 57, 197, 238, 117, 151, 12, 134, 249, 174, - 253, 49, 252, 247, 36, 173, 218, 252, 164, 106, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 4, 159, 60, 37, 240, 222, 46, 37, 0, - 169, 24, 233, 62, 155, 62, 244, 88, 187, 66, 3, 181, 29, 127, 88, 28, 21, 161, 199, 76, 37, 71, - 157, 32, 3, 95, 21, 84, 123, 217, 153, 109, 228, 161, 68, 236, 128, 30, 174, 129, 221, 62, 180, - 187, 25, 35, 4, 22, 242, 49, 37, 115, 64, 93, 150, 165, 32, 7, 80, 121, 33, 32, 74, 2, 21, 219, - 224, 212, 236, 217, 21, 38, 205, 52, 156, 47, 109, 72, 191, 184, 71, 91, 73, 1, 248, 243, 172, - 103, 236, 32, 4, 36, 216, 247, 210, 220, 235, 207, 152, 236, 150, 124, 26, 45, 204, 255, 118, - 231, 165, 72, 126, 205, 167, 85, 239, 63, 135, 93, 122, 241, 86, 78, 32, 5, 47, 184, 33, 147, - 250, 150, 84, 40, 36, 187, 154, 75, 129, 41, 19, 43, 80, 240, 57, 61, 224, 17, 77, 26, 98, 168, - 71, 138, 168, 1, 75, 32, 0, 193, 80, 45, 189, 34, 108, 62, 106, 2, 106, 17, 244, 104, 124, 116, - 9, 203, 253, 86, 106, 95, 34, 96, 253, 213, 240, 7, 16, 194, 172, 55, 32, 6, 138, 187, 194, - 179, 90, 208, 242, 51, 44, 182, 103, 218, 122, 81, 1, 96, 61, 235, 231, 79, 247, 96, 30, 172, - 2, 188, 15, 254, 248, 30, 135, 32, 5, 106, 52, 104, 220, 144, 92, 7, 26, 44, 63, 120, 183, 123, - 219, 28, 21, 142, 128, 85, 149, 178, 194, 9, 17, 30, 44, 140, 109, 121, 180, 5, 32, 5, 181, 34, - 125, 201, 6, 145, 222, 163, 225, 195, 3, 134, 163, 192, 44, 189, 201, 16, 213, 211, 48, 19, - 192, 41, 113, 220, 93, 156, 79, 194, 62, 32, 3, 191, 105, 12, 117, 23, 129, 8, 135, 99, 6, 208, - 223, 176, 106, 1, 119, 52, 228, 18, 128, 13, 222, 60, 63, 110, 159, 178, 83, 242, 117, 252, 32, - 7, 115, 74, 75, 196, 58, 24, 131, 163, 43, 72, 36, 124, 199, 180, 240, 199, 83, 234, 202, 175, - 197, 99, 139, 75, 180, 104, 233, 228, 84, 234, 207, 32, 6, 126, 229, 98, 91, 11, 153, 57, 82, - 92, 154, 240, 224, 86, 94, 73, 209, 99, 130, 134, 90, 170, 222, 111, 195, 49, 39, 180, 118, 82, - 111, 201, 32, 7, 121, 120, 78, 137, 189, 174, 214, 6, 61, 190, 228, 2, 202, 93, 125, 17, 166, - 214, 133, 123, 117, 247, 149, 154, 41, 187, 146, 243, 22, 80, 77, 32, 0, 31, 17, 80, 225, 142, - 59, 251, 55, 0, 117, 16, 188, 249, 109, 67, 218, 205, 28, 101, 37, 197, 34, 119, 33, 63, 134, - 14, 56, 171, 180, 253, 32, 5, 143, 33, 88, 127, 111, 0, 209, 74, 135, 73, 170, 1, 188, 215, 11, - 180, 217, 221, 244, 15, 181, 10, 166, 54, 245, 3, 194, 104, 16, 148, 122, 32, 4, 243, 1, 105, - 131, 38, 58, 93, 194, 236, 104, 155, 231, 207, 43, 238, 235, 191, 47, 253, 188, 235, 35, 96, - 171, 140, 187, 45, 1, 21, 204, 108, 32, 4, 187, 218, 122, 32, 147, 200, 121, 17, 168, 14, 22, - 111, 188, 77, 154, 12, 170, 196, 158, 191, 145, 28, 97, 181, 34, 245, 98, 250, 234, 180, 141, - 32, 2, 49, 191, 235, 85, 40, 229, 161, 53, 85, 145, 252, 187, 51, 173, 22, 192, 237, 144, 172, - 100, 225, 63, 108, 181, 252, 106, 114, 6, 113, 36, 106, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, 255, 254, 239, - 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 239, 255, 225, 32, 1, 196, 53, 181, 95, 75, 31, 202, 227, 211, 39, 177, 45, - 54, 198, 194, 16, 11, 44, 51, 60, 182, 42, 96, 103, 8, 74, 94, 146, 232, 102, 186, 32, 1, 66, - 251, 23, 165, 84, 44, 67, 206, 195, 81, 235, 119, 88, 133, 27, 74, 22, 85, 155, 187, 25, 123, - 162, 22, 99, 167, 78, 127, 57, 137, 102, 32, 1, 66, 251, 23, 165, 84, 44, 67, 206, 195, 81, - 235, 119, 88, 133, 27, 74, 22, 85, 155, 187, 25, 123, 162, 22, 99, 167, 78, 127, 57, 137, 102, - 32, 1, 66, 251, 23, 165, 84, 44, 67, 206, 195, 81, 235, 119, 88, 133, 27, 74, 22, 85, 155, 187, - 25, 123, 162, 22, 99, 167, 78, 127, 57, 137, 102, 32, 0, 223, 36, 6, 78, 160, 147, 139, 200, - 120, 217, 153, 132, 74, 8, 77, 233, 17, 244, 16, 96, 179, 1, 17, 115, 248, 16, 81, 132, 77, - 176, 186, 32, 0, 10, 88, 194, 113, 195, 231, 180, 138, 180, 27, 131, 125, 27, 70, 124, 223, 90, - 184, 99, 45, 91, 31, 223, 22, 27, 88, 186, 223, 95, 251, 190, 32, 2, 123, 65, 235, 137, 171, - 17, 184, 216, 95, 54, 207, 221, 236, 39, 114, 248, 151, 226, 192, 78, 75, 128, 148, 109, 176, - 146, 155, 61, 198, 9, 220, 32, 2, 18, 177, 85, 222, 153, 101, 197, 17, 101, 54, 245, 83, 144, - 124, 30, 212, 80, 111, 63, 177, 165, 108, 246, 214, 37, 191, 58, 78, 164, 22, 153, 32, 7, 195, - 179, 96, 23, 235, 170, 212, 249, 138, 153, 225, 176, 77, 198, 23, 98, 228, 81, 255, 185, 39, - 90, 103, 193, 240, 39, 47, 53, 207, 150, 239, 32, 2, 127, 141, 47, 10, 46, 91, 17, 177, 195, - 243, 85, 39, 73, 59, 77, 227, 117, 206, 24, 215, 215, 206, 125, 70, 86, 35, 26, 171, 156, 84, - 251, 32, 2, 101, 9, 124, 118, 138, 84, 243, 87, 218, 183, 37, 172, 76, 115, 249, 223, 43, 156, - 122, 192, 205, 155, 167, 26, 27, 184, 214, 198, 9, 73, 10, 32, 0, 9, 58, 185, 83, 31, 206, 79, - 153, 184, 113, 120, 179, 206, 79, 221, 198, 211, 215, 146, 120, 89, 212, 231, 53, 80, 132, 56, - 21, 180, 163, 248, 32, 0, 203, 141, 26, 73, 187, 111, 47, 11, 202, 176, 28, 117, 32, 53, 153, - 52, 133, 19, 116, 103, 122, 197, 170, 211, 147, 167, 249, 107, 243, 118, 123, 32, 3, 183, 49, - 253, 65, 119, 210, 134, 202, 192, 95, 233, 165, 92, 233, 70, 206, 64, 118, 252, 124, 118, 75, - 141, 251, 19, 181, 194, 116, 81, 142, 44, 32, 2, 80, 254, 240, 107, 219, 6, 237, 117, 77, 205, - 85, 182, 127, 219, 76, 62, 206, 140, 212, 157, 214, 221, 216, 211, 252, 133, 5, 40, 23, 226, - 61, 32, 6, 9, 221, 75, 210, 80, 32, 175, 115, 26, 202, 147, 37, 131, 94, 68, 197, 11, 177, 237, - 160, 31, 5, 168, 178, 84, 20, 29, 200, 64, 111, 195, 32, 0, 178, 82, 189, 124, 84, 65, 183, - 242, 70, 28, 182, 15, 28, 202, 161, 68, 217, 195, 155, 28, 147, 114, 114, 35, 247, 114, 45, - 119, 36, 109, 210, 32, 5, 109, 48, 52, 214, 147, 98, 67, 106, 157, 52, 151, 163, 148, 126, 166, - 152, 60, 240, 27, 36, 160, 167, 10, 49, 22, 89, 13, 65, 58, 101, 1, 32, 0, 195, 243, 205, 230, - 246, 33, 54, 63, 127, 194, 33, 20, 231, 176, 85, 64, 130, 70, 106, 17, 243, 213, 145, 70, 37, - 49, 126, 34, 220, 40, 144, 32, 5, 13, 151, 140, 38, 44, 28, 91, 7, 148, 83, 179, 140, 47, 181, - 16, 31, 28, 30, 1, 145, 181, 252, 46, 19, 95, 251, 152, 45, 148, 19, 232, 32, 4, 144, 27, 220, - 221, 194, 121, 212, 41, 235, 129, 119, 182, 30, 212, 196, 0, 216, 26, 191, 217, 126, 255, 192, - 88, 148, 134, 172, 180, 196, 66, 100, 32, 3, 137, 15, 197, 80, 93, 126, 178, 234, 144, 114, - 244, 252, 46, 11, 234, 25, 249, 202, 109, 95, 231, 188, 240, 104, 176, 199, 35, 136, 51, 30, - 234, 32, 6, 51, 196, 113, 60, 194, 36, 158, 221, 158, 174, 63, 154, 42, 42, 62, 107, 157, 46, - 114, 68, 47, 16, 101, 112, 77, 231, 213, 202, 65, 39, 192, 7, 185, 129, 243, 126, 12, 23, 81, - 193, 216, 100, 105, 241, 96, 224, 222, 187, 41, 87, 235, 59, 254, 254, 106, 215, 151, 198, 216, - 137, 87, 154, 17, 37, 116, 103, 217, 130, 125, 42, 71, 113, 160, 130, 226, 178, 176, 106, 211, - 188, 187, 126, 255, 56, 142, 114, 209, 252, 111, 34, 196, 50, 201, 217, 176, 242, 186, 227, 61, - 88, 41, 243, 62, 108, 65, 247, 58, 160, 126, 117, 196, 165, 187, 174, 40, 172, 249, 159, 23, - 38, 110, 145, 238, 99, 102, 145, 192, 151, 109, 212, 7, 144, 241, 219, 159, 240, 4, 186, 81, - 176, 23, 62, 250, 13, 221, 38, 179, 166, 120, 75, 229, 196, 180, 179, 248, 57, 206, 40, 99, - 254, 51, 230, 152, 210, 217, 212, 43, 71, 107, 171, 142, 71, 14, 62, 110, 41, 182, 36, 32, 96, - 88, 122, 254, 193, 218, 68, 147, 165, 70, 14, 28, 171, 15, 190, 222, 28, 255, 161, 229, 154, - 51, 48, 225, 56, 151, 205, 40, 203, 139, 117, 226, 253, 28, 32, 75, 6, 59, 72, 255, 67, 219, - 118, 21, 125, 51, 45, 171, 60, 71, 178, 34, 1, 103, 238, 163, 124, 189, 119, 30, 6, 204, 249, - 51, 163, 124, 49, 48, 66, 231, 52, 43, 104, 108, 3, 211, 196, 2, 32, 4, 76, 102, 247, 56, 162, - 75, 188, 245, 174, 117, 185, 145, 137, 149, 242, 0, 186, 86, 29, 125, 189, 83, 94, 148, 201, - 213, 210, 183, 26, 142, 14, 32, 7, 180, 142, 178, 208, 178, 94, 202, 162, 156, 127, 198, 22, - 62, 204, 155, 159, 70, 215, 19, 191, 180, 25, 148, 2, 251, 209, 157, 254, 186, 209, 147, 2, 8, - 185, 237, 7, 2, 9, 16, 91, 169, 86, 161, 120, 228, 11, 206, 14, 99, 215, 152, 177, 87, 126, - 214, 96, 52, 84, 130, 105, 108, 62, 181, 215, 220, 139, 170, 141, 93, 121, 239, 194, 14, 102, - 7, 189, 196, 115, 187, 60, 215, 18, 222, 131, 7, 75, 212, 188, 192, 76, 203, 238, 85, 79, 12, - 54, 231, 86, 151, 55, 0, 220, 136, 78, 126, 115, 209, 44, 92, 28, 137, 98, 83, 22, 139, 55, - 156, 16, 89, 230, 102, 74, 159, 250, 44, 229, 98, 135, 143, 187, 208, 228, 16, 121, 134, 209, - 217, 57, 22, 60, 30, 69, 253, 111, 210, 50, 102, 144, 215, 31, 249, 250, 32, 200, 168, 28, 236, - 35, 123, 186, 205, 119, 105, 144, 137, 225, 179, 93, 226, 177, 47, 220, 80, 135, 139, 16, 59, - 142, 114, 145, 122, 199, 250, 90, 16, 218, 146, 188, 78, 179, 117, 0, 127, 65, 73, 175, 31, - 243, 113, 84, 117, 9, 243, 236, 149, 222, 78, 175, 15, 254, 3, 30, 184, 97, 18, 206, 47, 248, - 251, 115, 153, 87, 228, 133, 185, 202, 162, 8, 67, 150, 128, 8, 9, 249, 181, 204, 76, 95, 153, - 145, 183, 153, 231, 164, 80, 156, 141, 63, 16, 150, 55, 32, 29, 23, 94, 121, 91, 230, 102, 86, - 203, 113, 159, 74, 128, 251, 129, 47, 111, 246, 84, 15, 173, 114, 238, 116, 16, 155, 12, 31, - 135, 15, 42, 107, 57, 231, 57, 31, 248, 8, 141, 26, 47, 190, 213, 221, 52, 152, 105, 214, 197, - 12, 40, 5, 33, 57, 248, 188, 82, 116, 140, 18, 217, 43, 37, 83, 136, 56, 44, 58, 17, 121, 63, - 224, 18, 67, 171, 13, 122, 132, 252, 153, 36, 224, 133, 210, 189, 180, 69, 146, 227, 165, 98, - 191, 84, 224, 137, 164, 97, 2, 32, 195, 224, 144, 88, 12, 7, 243, 172, 61, 5, 72, 70, 231, 153, - 25, 126, 141, 100, 242, 218, 86, 20, 90, 112, 209, 214, 32, 139, 78, 52, 92, 234, 90, 86, 132, - 197, 86, 73, 49, 109, 163, 185, 196, 195, 155, 141, 71, 195, 163, 248, 147, 134, 215, 134, 31, - 228, 223, 171, 121, 215, 38, 128, 8, 219, 153, 215, 227, 201, 119, 48, 125, 31, 23, 13, 80, - 120, 151, 246, 202, 241, 51, 242, 209, 218, 235, 129, 8, 75, 194, 189, 27, 29, 123, 71, 22, 84, - 43, 35, 129, 61, 245, 107, 25, 19, 158, 188, 56, 201, 24, 23, 148, 115, 220, 185, 3, 231, 184, - 65, 25, 159, 105, 232, 92, 122, 22, 105, 104, 136, 15, 213, 20, 47, 241, 143, 10, 103, 38, 150, - 60, 79, 110, 163, 8, 253, 180, 70, 46, 57, 62, 228, 197, 202, 96, 179, 17, 52, 135, 35, 192, - 169, 105, 94, 140, 38, 226, 17, 200, 114, 66, 199, 188, 124, 141, 168, 29, 23, 194, 195, 44, - 138, 2, 56, 50, 173, 213, 76, 21, 151, 35, 181, 172, 207, 17, 240, 231, 59, 32, 6, 214, 173, - 218, 190, 111, 132, 52, 230, 226, 50, 45, 215, 184, 146, 202, 241, 20, 124, 158, 92, 46, 106, - 82, 81, 0, 171, 137, 38, 7, 25, 82, 32, 1, 84, 72, 210, 222, 186, 171, 17, 16, 224, 213, 207, - 124, 167, 167, 152, 202, 229, 9, 183, 97, 128, 241, 1, 94, 227, 205, 248, 54, 27, 117, 141, 32, - 3, 2, 72, 120, 93, 243, 130, 98, 113, 116, 60, 177, 18, 101, 19, 84, 156, 97, 133, 92, 1, 63, - 124, 183, 253, 137, 8, 27, 209, 24, 88, 185, 32, 0, 122, 78, 118, 229, 190, 149, 251, 119, 23, - 52, 180, 146, 20, 11, 187, 142, 133, 243, 179, 136, 126, 20, 146, 181, 219, 81, 243, 231, 236, - 182, 8, 32, 2, 225, 5, 53, 227, 210, 233, 89, 170, 120, 59, 16, 52, 203, 49, 120, 112, 2, 241, - 9, 58, 77, 86, 152, 1, 105, 99, 124, 3, 22, 88, 52, 32, 3, 211, 122, 101, 202, 49, 2, 159, 39, - 242, 102, 118, 37, 194, 235, 16, 78, 236, 80, 79, 159, 58, 8, 198, 163, 34, 41, 85, 243, 10, - 165, 79, 32, 1, 193, 123, 253, 133, 169, 51, 44, 62, 245, 92, 247, 54, 152, 71, 177, 146, 167, - 145, 243, 165, 11, 67, 157, 196, 52, 190, 76, 190, 137, 197, 223, 32, 4, 224, 189, 254, 194, - 212, 153, 158, 159, 122, 174, 123, 155, 76, 35, 216, 201, 83, 200, 249, 210, 133, 161, 206, - 226, 26, 95, 38, 95, 68, 226, 240, 32, 4, 138, 36, 155, 128, 30, 157, 177, 233, 96, 182, 159, - 21, 167, 19, 232, 28, 162, 65, 90, 189, 143, 147, 60, 172, 83, 132, 114, 91, 118, 21, 47, 32, - 3, 66, 127, 133, 166, 217, 164, 27, 180, 207, 185, 245, 18, 250, 176, 35, 103, 212, 163, 194, - 250, 21, 240, 188, 173, 222, 78, 163, 110, 33, 246, 205, 32, 3, 188, 208, 75, 81, 194, 250, - 231, 255, 53, 210, 168, 71, 22, 123, 141, 8, 74, 173, 8, 200, 31, 215, 72, 130, 45, 171, 188, - 226, 98, 179, 105, 32, 0, 60, 114, 159, 181, 249, 107, 178, 53, 118, 32, 142, 210, 132, 16, - 203, 57, 180, 230, 81, 140, 74, 218, 231, 107, 41, 199, 61, 198, 178, 92, 200, 32, 0, 207, 160, - 97, 233, 192, 245, 68, 14, 217, 125, 135, 109, 239, 108, 122, 105, 90, 228, 200, 103, 32, 188, - 32, 241, 119, 167, 98, 37, 49, 4, 122, 32, 2, 15, 172, 34, 6, 74, 78, 224, 158, 104, 236, 250, - 98, 230, 118, 180, 253, 190, 113, 227, 227, 17, 89, 217, 42, 164, 178, 145, 92, 141, 228, 91, - 32, 7, 33, 155, 173, 33, 217, 120, 91, 104, 215, 213, 222, 121, 116, 61, 86, 54, 215, 149, 207, - 197, 213, 111, 65, 208, 152, 174, 39, 218, 26, 149, 229, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 211, 127, 37, 118, 43, - 160, 128, 160, 83, 21, 91, 160, 69, 181, 0, 96, 181, 193, 248, 179, 67, 72, 195, 77, 241, 179, - 145, 69, 252, 0, 134, 32, 0, 145, 105, 21, 120, 81, 70, 186, 227, 114, 75, 117, 223, 91, 194, - 45, 38, 144, 202, 255, 207, 188, 241, 156, 204, 33, 26, 251, 30, 196, 98, 48, 32, 6, 97, 62, - 102, 41, 112, 254, 80, 220, 35, 165, 152, 101, 38, 123, 227, 25, 122, 129, 184, 195, 165, 163, - 84, 183, 100, 3, 228, 3, 187, 58, 191, 32, 1, 46, 111, 190, 173, 92, 197, 120, 233, 63, 189, - 208, 114, 177, 243, 201, 50, 69, 43, 221, 88, 136, 86, 155, 157, 191, 116, 139, 226, 233, 169, - 173, 32, 2, 97, 248, 193, 95, 248, 204, 192, 87, 209, 246, 102, 78, 186, 30, 249, 68, 95, 106, - 25, 96, 46, 179, 253, 39, 67, 46, 60, 189, 114, 204, 163, 32, 0, 16, 85, 152, 122, 104, 121, - 181, 172, 76, 29, 250, 104, 184, 225, 47, 0, 161, 167, 174, 26, 41, 195, 109, 37, 51, 82, 49, - 207, 95, 172, 233, 32, 4, 75, 9, 119, 219, 200, 226, 151, 83, 154, 172, 222, 200, 202, 28, 233, - 193, 248, 193, 230, 80, 101, 57, 162, 75, 249, 198, 146, 65, 174, 243, 29, 32, 6, 129, 175, - 250, 17, 227, 135, 49, 99, 0, 79, 252, 42, 155, 166, 0, 1, 91, 183, 211, 2, 139, 146, 98, 68, - 230, 117, 70, 124, 198, 132, 177, 32, 6, 136, 163, 62, 213, 198, 47, 62, 133, 199, 115, 215, - 183, 213, 70, 229, 251, 127, 252, 111, 21, 60, 110, 91, 77, 75, 183, 108, 62, 66, 235, 233, 32, - 2, 53, 67, 254, 134, 85, 244, 58, 71, 154, 158, 141, 37, 103, 42, 156, 114, 26, 214, 251, 130, - 216, 238, 104, 26, 255, 57, 105, 217, 30, 197, 30, 32, 5, 215, 123, 64, 210, 236, 25, 222, 165, - 104, 154, 207, 14, 159, 73, 15, 13, 117, 154, 56, 72, 176, 129, 67, 20, 241, 108, 132, 241, 54, - 89, 57, 32, 7, 239, 71, 83, 245, 85, 31, 250, 176, 73, 94, 140, 37, 46, 72, 203, 150, 79, 68, - 26, 51, 244, 107, 103, 71, 239, 118, 35, 237, 67, 183, 48, 32, 2, 67, 176, 132, 110, 91, 110, - 247, 41, 133, 86, 54, 106, 62, 183, 160, 119, 156, 81, 131, 132, 197, 55, 77, 231, 138, 170, - 110, 245, 71, 206, 150, 32, 4, 29, 216, 224, 193, 231, 155, 162, 171, 175, 192, 103, 201, 181, - 150, 28, 209, 46, 137, 105, 175, 54, 157, 242, 235, 192, 25, 105, 57, 54, 228, 177, 32, 3, 158, - 80, 240, 174, 229, 103, 178, 208, 149, 230, 251, 198, 109, 70, 240, 72, 101, 61, 193, 214, 241, - 247, 165, 242, 213, 58, 139, 58, 100, 111, 47, 32, 3, 254, 132, 240, 234, 142, 122, 94, 116, - 130, 158, 52, 131, 164, 229, 212, 67, 230, 212, 164, 23, 157, 2, 126, 228, 120, 232, 111, 2, - 163, 232, 155, 32, 5, 61, 128, 55, 63, 2, 128, 82, 118, 69, 27, 130, 57, 14, 87, 48, 150, 151, - 144, 171, 54, 232, 137, 3, 253, 1, 242, 26, 220, 26, 81, 149, 32, 5, 57, 118, 112, 212, 52, 76, - 34, 244, 232, 159, 201, 159, 188, 228, 220, 7, 59, 44, 80, 65, 186, 13, 187, 207, 105, 138, - 233, 74, 193, 65, 59, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, 255, 254, 239, 253, 240, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 255, 225, - 32, 7, 55, 243, 202, 184, 150, 108, 89, 85, 63, 214, 136, 228, 140, 218, 99, 196, 126, 255, 21, - 117, 23, 218, 226, 98, 27, 129, 30, 30, 207, 49, 247, 32, 0, 51, 246, 197, 92, 62, 51, 150, 94, - 20, 164, 247, 9, 39, 103, 247, 172, 252, 98, 73, 30, 240, 224, 229, 46, 242, 98, 101, 114, 234, - 202, 152, 32, 0, 51, 246, 197, 92, 62, 51, 150, 94, 20, 164, 247, 9, 39, 103, 247, 172, 252, - 98, 73, 30, 240, 224, 229, 46, 242, 98, 101, 114, 234, 202, 152, 32, 0, 51, 246, 197, 92, 62, - 51, 150, 94, 20, 164, 247, 9, 39, 103, 247, 172, 252, 98, 73, 30, 240, 224, 229, 46, 242, 98, - 101, 114, 234, 202, 152, 32, 7, 104, 198, 51, 126, 159, 201, 170, 123, 1, 138, 56, 236, 198, - 220, 61, 254, 169, 158, 133, 193, 25, 109, 142, 20, 92, 108, 145, 8, 249, 115, 206, 32, 5, 238, - 89, 103, 75, 246, 225, 191, 219, 236, 156, 122, 56, 230, 195, 77, 29, 15, 105, 132, 109, 152, - 176, 97, 168, 142, 141, 11, 224, 200, 104, 77, 32, 5, 132, 32, 228, 241, 252, 109, 24, 2, 136, - 44, 122, 212, 175, 254, 125, 53, 97, 49, 232, 42, 106, 71, 178, 77, 106, 85, 8, 139, 164, 31, - 151, 32, 6, 30, 208, 162, 18, 150, 1, 177, 87, 238, 4, 87, 23, 21, 135, 5, 184, 243, 195, 137, - 165, 32, 186, 99, 171, 54, 205, 88, 36, 123, 218, 13, 32, 2, 204, 180, 163, 100, 118, 68, 166, - 73, 87, 243, 198, 250, 176, 166, 146, 250, 166, 249, 230, 65, 93, 45, 255, 143, 224, 210, 31, - 242, 172, 83, 238, 32, 6, 86, 151, 113, 239, 3, 96, 201, 52, 75, 230, 86, 92, 111, 33, 13, 92, - 24, 148, 128, 166, 14, 136, 115, 26, 29, 124, 107, 3, 235, 231, 245, 32, 4, 3, 136, 206, 22, - 215, 97, 224, 52, 45, 25, 46, 220, 208, 28, 81, 141, 76, 209, 128, 5, 189, 109, 58, 197, 21, - 68, 132, 55, 55, 83, 19, 32, 3, 117, 252, 111, 205, 82, 100, 222, 249, 234, 136, 127, 170, 162, - 216, 203, 164, 33, 5, 250, 103, 201, 77, 113, 112, 51, 166, 140, 56, 206, 86, 185, 32, 5, 27, - 50, 140, 154, 213, 43, 50, 25, 103, 142, 79, 230, 158, 241, 116, 89, 136, 5, 93, 53, 52, 180, - 83, 59, 210, 122, 90, 127, 91, 81, 161, 32, 3, 168, 169, 95, 100, 158, 95, 90, 235, 132, 148, - 20, 1, 101, 118, 74, 61, 234, 179, 210, 85, 80, 57, 95, 204, 220, 24, 153, 253, 89, 158, 222, - 32, 0, 188, 213, 18, 86, 250, 143, 101, 216, 78, 84, 172, 151, 157, 175, 84, 14, 214, 167, 212, - 158, 112, 167, 139, 141, 185, 248, 46, 53, 201, 6, 36, 32, 1, 158, 159, 142, 231, 0, 218, 208, - 6, 81, 150, 155, 224, 72, 228, 187, 157, 242, 222, 163, 186, 71, 40, 227, 232, 157, 26, 250, - 21, 221, 232, 31, 32, 6, 129, 140, 70, 18, 13, 97, 66, 231, 165, 252, 195, 139, 133, 45, 199, - 157, 128, 63, 23, 105, 145, 184, 197, 239, 149, 172, 67, 0, 213, 1, 212, 32, 1, 52, 14, 31, - 180, 76, 194, 207, 196, 103, 142, 14, 225, 243, 194, 54, 168, 213, 196, 180, 93, 230, 134, 58, - 193, 171, 136, 210, 219, 212, 246, 203, 32, 4, 173, 124, 64, 215, 91, 108, 105, 98, 157, 149, - 26, 9, 251, 88, 134, 31, 122, 36, 57, 213, 38, 47, 92, 16, 180, 49, 149, 96, 235, 86, 91, 32, - 6, 108, 250, 129, 12, 150, 254, 190, 213, 75, 227, 208, 187, 237, 183, 32, 183, 100, 58, 246, - 34, 236, 58, 108, 129, 31, 16, 25, 139, 239, 61, 37, 32, 4, 168, 34, 125, 125, 129, 149, 246, - 153, 243, 190, 171, 181, 23, 233, 118, 244, 60, 197, 76, 199, 140, 114, 13, 35, 5, 92, 22, 193, - 28, 39, 214, 32, 4, 142, 7, 201, 252, 99, 247, 167, 6, 179, 191, 225, 211, 11, 134, 24, 77, - 216, 237, 42, 228, 128, 160, 8, 206, 240, 32, 44, 43, 125, 128, 116, 32, 6, 34, 241, 202, 126, - 63, 60, 89, 80, 61, 5, 78, 197, 98, 88, 110, 192, 135, 211, 87, 132, 154, 11, 13, 231, 161, 67, - 100, 44, 21, 106, 184, 3, 7, 143, 227, 159, 164, 85, 246, 234, 50, 196, 91, 38, 46, 24, 101, - 98, 89, 172, 100, 14, 251, 66, 137, 81, 246, 211, 156, 208, 4, 122, 213, 140, 130, 53, 82, 202, - 111, 213, 169, 243, 94, 112, 245, 75, 185, 11, 236, 112, 194, 41, 200, 247, 216, 56, 153, 254, - 12, 227, 169, 77, 8, 103, 81, 25, 187, 88, 142, 120, 152, 104, 74, 247, 5, 9, 0, 23, 158, 66, - 243, 222, 4, 163, 88, 57, 211, 75, 7, 149, 187, 187, 81, 34, 2, 179, 160, 148, 228, 227, 113, - 247, 198, 39, 137, 45, 114, 186, 121, 135, 78, 55, 65, 177, 99, 102, 65, 28, 76, 48, 138, 171, - 217, 51, 31, 154, 190, 174, 97, 168, 117, 172, 237, 224, 114, 8, 103, 84, 1, 246, 211, 98, 97, - 14, 58, 110, 46, 169, 251, 164, 131, 193, 178, 14, 208, 74, 62, 157, 44, 103, 207, 112, 173, - 216, 171, 182, 141, 100, 237, 193, 155, 100, 252, 234, 227, 240, 128, 133, 102, 185, 99, 234, - 200, 182, 96, 74, 192, 13, 209, 71, 115, 104, 124, 179, 164, 223, 89, 216, 115, 42, 45, 247, - 206, 176, 156, 12, 153, 45, 13, 251, 215, 172, 248, 210, 214, 153, 245, 13, 166, 237, 50, 171, - 170, 143, 159, 139, 197, 2, 32, 4, 92, 87, 228, 248, 120, 156, 15, 188, 36, 219, 70, 149, 33, - 151, 210, 173, 43, 232, 232, 23, 173, 77, 61, 170, 89, 216, 91, 175, 177, 82, 106, 32, 5, 122, - 53, 232, 84, 165, 55, 115, 83, 22, 185, 109, 160, 95, 135, 139, 236, 58, 215, 84, 174, 132, - 161, 172, 156, 207, 194, 206, 81, 243, 106, 27, 2, 8, 156, 90, 196, 61, 251, 24, 214, 160, 159, - 203, 46, 123, 140, 238, 152, 95, 54, 36, 97, 235, 82, 194, 247, 9, 181, 230, 136, 73, 201, 135, - 205, 39, 151, 203, 47, 152, 214, 200, 164, 98, 254, 213, 134, 8, 241, 226, 104, 150, 244, 74, - 115, 219, 255, 83, 218, 101, 111, 191, 131, 194, 62, 167, 55, 72, 152, 27, 86, 66, 6, 244, 122, - 121, 144, 46, 166, 66, 124, 203, 204, 235, 75, 61, 217, 136, 85, 11, 213, 20, 21, 113, 177, - 117, 29, 68, 188, 151, 243, 240, 99, 46, 3, 241, 18, 179, 194, 224, 200, 241, 109, 149, 189, - 60, 148, 99, 5, 35, 145, 128, 128, 21, 203, 239, 181, 228, 240, 47, 135, 220, 34, 178, 167, - 247, 106, 173, 158, 22, 63, 212, 201, 160, 117, 48, 148, 208, 146, 236, 225, 34, 210, 178, 74, - 27, 219, 146, 18, 246, 147, 54, 127, 158, 168, 87, 74, 115, 111, 31, 193, 215, 116, 252, 153, - 159, 179, 50, 108, 250, 181, 183, 86, 85, 81, 49, 50, 182, 5, 156, 167, 125, 1, 203, 193, 96, - 229, 23, 56, 185, 8, 183, 136, 50, 181, 158, 63, 15, 141, 232, 151, 145, 44, 104, 52, 24, 145, - 194, 228, 252, 233, 233, 188, 144, 1, 19, 65, 2, 229, 146, 117, 67, 238, 190, 16, 96, 92, 169, - 231, 87, 127, 81, 158, 229, 8, 37, 233, 213, 154, 139, 101, 103, 128, 137, 232, 243, 179, 19, - 171, 248, 8, 248, 184, 187, 201, 55, 198, 254, 234, 226, 145, 11, 51, 172, 137, 200, 181, 186, - 172, 239, 57, 223, 226, 234, 218, 91, 71, 118, 140, 120, 165, 36, 223, 89, 215, 92, 202, 34, - 161, 165, 176, 85, 85, 31, 163, 26, 106, 45, 165, 211, 109, 81, 191, 141, 172, 192, 132, 37, - 20, 17, 237, 17, 202, 4, 88, 20, 249, 223, 116, 21, 255, 35, 4, 158, 8, 223, 44, 158, 133, 39, - 80, 104, 164, 35, 21, 203, 9, 235, 52, 205, 135, 20, 82, 240, 165, 60, 28, 223, 34, 9, 183, 92, - 14, 152, 76, 254, 209, 126, 53, 54, 232, 220, 227, 8, 161, 218, 16, 26, 241, 254, 53, 194, 219, - 149, 106, 67, 173, 42, 36, 13, 233, 203, 206, 205, 110, 99, 70, 226, 94, 86, 244, 125, 108, 41, - 118, 254, 55, 55, 222, 22, 92, 112, 154, 65, 212, 142, 59, 60, 3, 107, 208, 156, 230, 253, 184, - 103, 128, 240, 14, 203, 86, 220, 97, 117, 207, 209, 61, 122, 165, 43, 161, 50, 96, 156, 89, - 156, 209, 198, 59, 100, 165, 195, 131, 9, 142, 113, 217, 122, 61, 153, 59, 9, 163, 241, 6, 113, - 196, 218, 94, 216, 173, 188, 168, 35, 191, 202, 239, 105, 5, 128, 163, 172, 35, 193, 113, 25, - 50, 185, 158, 146, 208, 222, 191, 50, 54, 152, 19, 171, 153, 57, 88, 69, 45, 152, 185, 233, 13, - 250, 36, 169, 142, 104, 60, 213, 67, 240, 88, 59, 32, 4, 156, 64, 9, 145, 37, 151, 170, 222, - 245, 212, 179, 179, 95, 15, 92, 134, 140, 235, 51, 76, 184, 77, 219, 134, 120, 83, 135, 187, - 113, 230, 108, 32, 7, 180, 85, 30, 235, 49, 150, 123, 139, 21, 97, 53, 180, 76, 65, 53, 29, 81, - 242, 180, 182, 109, 27, 44, 27, 209, 137, 104, 132, 203, 216, 223, 32, 1, 167, 163, 6, 212, - 176, 209, 163, 168, 79, 34, 201, 245, 245, 33, 176, 192, 133, 68, 76, 227, 32, 128, 19, 130, - 94, 41, 155, 15, 85, 69, 3, 32, 6, 93, 22, 62, 108, 26, 116, 170, 1, 220, 48, 246, 63, 14, 245, - 73, 142, 211, 185, 136, 227, 247, 199, 193, 50, 103, 134, 100, 224, 207, 38, 138, 32, 0, 138, - 122, 222, 252, 143, 99, 6, 6, 186, 215, 1, 145, 107, 193, 163, 218, 135, 118, 143, 237, 222, - 70, 147, 53, 154, 212, 171, 33, 177, 105, 117, 32, 5, 96, 8, 244, 182, 3, 126, 26, 207, 220, - 13, 105, 18, 189, 53, 97, 171, 149, 10, 26, 8, 165, 56, 223, 141, 45, 231, 69, 134, 106, 90, - 147, 32, 5, 190, 189, 68, 182, 166, 142, 113, 170, 103, 164, 124, 232, 53, 136, 88, 187, 165, - 178, 126, 26, 94, 174, 1, 109, 116, 175, 4, 218, 178, 115, 157, 32, 6, 223, 94, 162, 91, 83, - 71, 65, 85, 51, 210, 62, 116, 26, 196, 44, 93, 210, 217, 63, 13, 47, 87, 0, 182, 186, 87, 130, - 109, 89, 57, 207, 32, 3, 110, 13, 140, 241, 158, 226, 150, 158, 33, 144, 20, 173, 32, 89, 214, - 81, 43, 239, 131, 96, 7, 149, 210, 204, 242, 237, 242, 10, 76, 151, 155, 32, 3, 54, 190, 117, - 76, 64, 66, 50, 94, 106, 31, 14, 0, 72, 86, 144, 4, 87, 175, 113, 95, 132, 20, 150, 244, 193, - 183, 55, 31, 143, 102, 121, 32, 1, 196, 158, 65, 79, 172, 210, 38, 96, 195, 245, 247, 251, 104, - 34, 204, 51, 17, 168, 172, 143, 54, 7, 25, 194, 187, 91, 201, 165, 246, 30, 130, 32, 6, 211, - 182, 210, 42, 106, 109, 121, 52, 200, 27, 62, 164, 114, 176, 237, 146, 205, 250, 247, 103, 93, - 212, 127, 125, 69, 38, 5, 168, 231, 27, 194, 32, 0, 103, 93, 21, 202, 145, 48, 94, 190, 26, - 109, 238, 97, 191, 230, 177, 28, 40, 27, 153, 235, 76, 73, 184, 67, 147, 150, 249, 28, 123, 46, - 222, 32, 6, 102, 54, 19, 134, 48, 146, 225, 252, 72, 196, 200, 21, 16, 242, 66, 92, 55, 194, - 218, 109, 188, 50, 94, 173, 84, 102, 149, 193, 78, 62, 236, 32, 7, 49, 121, 69, 135, 13, 136, - 111, 113, 172, 9, 89, 125, 155, 112, 225, 80, 94, 100, 81, 16, 78, 3, 129, 200, 63, 245, 123, - 180, 71, 26, 42, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 32, 5, 182, 61, 27, 251, 143, 56, 240, 230, 192, 49, 200, 143, 255, 153, - 215, 200, 138, 223, 117, 110, 114, 188, 121, 78, 3, 227, 6, 137, 144, 131, 56, 32, 3, 23, 106, - 46, 33, 132, 89, 30, 45, 227, 80, 227, 168, 28, 145, 192, 171, 38, 0, 64, 169, 235, 255, 185, - 85, 94, 68, 43, 212, 212, 245, 96, 32, 5, 205, 118, 197, 63, 107, 148, 117, 161, 22, 167, 227, - 58, 18, 176, 249, 94, 70, 209, 8, 251, 141, 92, 74, 236, 181, 239, 172, 249, 79, 132, 199, 32, - 1, 251, 236, 84, 22, 64, 178, 150, 60, 215, 234, 45, 93, 26, 247, 167, 246, 225, 150, 121, 200, - 72, 133, 50, 170, 157, 249, 14, 244, 222, 202, 100, 32, 3, 104, 197, 122, 79, 86, 33, 171, 80, - 105, 176, 97, 127, 183, 155, 202, 210, 41, 50, 193, 230, 105, 41, 80, 94, 32, 25, 109, 74, 84, - 152, 62, 32, 0, 140, 105, 53, 12, 204, 23, 109, 32, 99, 158, 224, 62, 223, 171, 129, 110, 25, - 34, 112, 58, 88, 146, 253, 148, 62, 94, 186, 28, 21, 9, 216, 32, 1, 200, 72, 87, 10, 70, 185, - 170, 2, 244, 47, 69, 150, 134, 74, 15, 125, 152, 29, 21, 142, 193, 86, 47, 87, 130, 206, 26, - 149, 113, 90, 251, 32, 0, 171, 27, 118, 237, 98, 130, 23, 13, 77, 156, 195, 190, 14, 235, 26, - 124, 83, 128, 52, 145, 130, 117, 81, 229, 98, 35, 104, 9, 201, 162, 255, 32, 2, 102, 37, 165, - 125, 41, 20, 174, 20, 27, 202, 245, 7, 134, 253, 254, 182, 106, 22, 52, 179, 183, 65, 253, 24, - 37, 175, 40, 54, 192, 157, 134, 32, 0, 229, 143, 12, 220, 204, 241, 43, 26, 130, 177, 242, 7, - 140, 67, 1, 255, 14, 189, 19, 251, 63, 41, 192, 147, 180, 237, 237, 169, 11, 134, 101, 32, 3, - 169, 47, 96, 251, 89, 185, 44, 150, 94, 67, 58, 247, 56, 231, 232, 157, 51, 67, 59, 11, 45, 67, - 249, 192, 188, 147, 65, 230, 200, 140, 128, 32, 2, 65, 166, 39, 128, 133, 91, 168, 43, 103, 11, - 110, 235, 198, 179, 72, 198, 82, 163, 63, 209, 72, 237, 231, 227, 158, 70, 251, 9, 185, 17, - 170, 32, 5, 25, 72, 27, 123, 213, 104, 215, 121, 121, 85, 229, 23, 243, 127, 148, 33, 145, 63, - 168, 119, 237, 75, 234, 188, 198, 98, 220, 15, 87, 137, 129, 32, 4, 65, 237, 54, 118, 172, 230, - 149, 79, 201, 82, 241, 124, 164, 242, 212, 103, 128, 201, 136, 40, 124, 222, 143, 32, 199, 15, - 49, 184, 124, 96, 158, 32, 1, 235, 160, 73, 109, 4, 233, 12, 41, 163, 205, 106, 30, 239, 247, - 132, 171, 183, 95, 84, 124, 132, 32, 154, 217, 64, 69, 79, 231, 170, 71, 229, 32, 2, 83, 235, - 88, 245, 124, 155, 148, 78, 176, 119, 210, 81, 96, 24, 181, 208, 66, 161, 121, 229, 84, 100, - 148, 161, 40, 8, 52, 64, 75, 91, 23, 32, 4, 32, 122, 123, 210, 97, 99, 50, 125, 70, 140, 155, - 24, 207, 244, 72, 94, 243, 41, 78, 145, 135, 67, 104, 118, 23, 12, 111, 75, 26, 122, 203, 32, - 7, 136, 227, 2, 96, 38, 242, 105, 239, 68, 47, 219, 157, 213, 93, 48, 161, 64, 164, 250, 78, - 209, 101, 62, 61, 240, 232, 12, 48, 212, 248, 241, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, 255, 254, 239, 253, - 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 239, 255, 225, 32, 0, 58, 129, 30, 76, 176, 24, 111, 103, 5, 239, 139, 54, 59, - 134, 167, 114, 197, 80, 165, 136, 113, 166, 191, 174, 75, 172, 117, 193, 188, 79, 191, 32, 4, - 180, 232, 114, 72, 98, 11, 53, 63, 127, 2, 27, 49, 133, 85, 139, 214, 50, 80, 71, 71, 176, 113, - 202, 120, 85, 245, 157, 109, 190, 3, 128, 32, 4, 180, 232, 114, 72, 98, 11, 53, 63, 127, 2, 27, - 49, 133, 85, 139, 214, 50, 80, 71, 71, 176, 113, 202, 120, 85, 245, 157, 109, 190, 3, 128, 32, - 4, 180, 232, 114, 72, 98, 11, 53, 63, 127, 2, 27, 49, 133, 85, 139, 214, 50, 80, 71, 71, 176, - 113, 202, 120, 85, 245, 157, 109, 190, 3, 128, 32, 0, 5, 163, 104, 105, 99, 47, 186, 197, 98, - 16, 243, 163, 205, 84, 210, 9, 176, 77, 125, 133, 73, 89, 50, 125, 165, 26, 56, 92, 68, 119, - 113, 32, 2, 16, 215, 198, 204, 185, 235, 248, 101, 74, 180, 181, 111, 58, 212, 105, 78, 51, - 159, 120, 168, 158, 247, 105, 25, 64, 124, 116, 50, 202, 182, 235, 32, 6, 36, 24, 103, 125, - 154, 4, 209, 15, 152, 251, 76, 72, 101, 181, 245, 213, 121, 145, 103, 14, 188, 113, 196, 171, - 46, 79, 172, 66, 110, 62, 49, 32, 3, 135, 47, 178, 59, 85, 29, 104, 227, 133, 249, 128, 162, - 126, 176, 31, 195, 239, 25, 215, 232, 82, 214, 82, 181, 184, 142, 67, 42, 52, 101, 222, 32, 0, - 222, 174, 252, 159, 156, 229, 69, 58, 15, 31, 122, 75, 47, 207, 57, 4, 61, 88, 133, 13, 217, - 17, 174, 163, 79, 141, 154, 44, 185, 210, 26, 32, 0, 173, 22, 169, 9, 107, 226, 42, 151, 33, - 162, 184, 244, 107, 72, 179, 107, 3, 130, 145, 63, 43, 236, 108, 33, 23, 251, 97, 250, 94, 190, - 245, 32, 6, 200, 177, 47, 103, 138, 94, 218, 112, 31, 97, 43, 101, 114, 117, 99, 75, 166, 211, - 165, 29, 157, 118, 210, 190, 224, 204, 205, 95, 98, 202, 92, 32, 3, 255, 119, 15, 101, 204, 80, - 154, 190, 214, 187, 102, 143, 89, 155, 136, 196, 117, 53, 198, 51, 103, 68, 213, 47, 77, 45, - 69, 208, 223, 31, 64, 32, 0, 213, 66, 133, 206, 18, 94, 44, 226, 208, 103, 136, 235, 165, 114, - 239, 206, 161, 234, 88, 210, 162, 209, 211, 104, 30, 137, 176, 204, 231, 174, 66, 32, 5, 39, 5, - 0, 143, 234, 125, 187, 180, 150, 73, 117, 255, 81, 157, 102, 97, 20, 9, 33, 187, 78, 187, 235, - 52, 13, 154, 123, 68, 137, 93, 70, 32, 3, 229, 54, 198, 218, 23, 140, 26, 184, 216, 68, 167, 6, - 201, 87, 13, 240, 92, 130, 228, 74, 47, 34, 52, 191, 137, 153, 233, 203, 47, 25, 74, 32, 5, 86, - 78, 15, 90, 225, 101, 68, 180, 53, 221, 241, 131, 195, 50, 211, 120, 120, 247, 190, 224, 174, - 89, 78, 246, 205, 38, 85, 69, 230, 178, 117, 32, 1, 106, 168, 254, 170, 174, 193, 171, 204, - 114, 98, 148, 160, 133, 188, 114, 19, 38, 147, 119, 88, 196, 176, 169, 7, 228, 118, 122, 179, - 99, 106, 124, 32, 0, 238, 129, 61, 62, 223, 159, 58, 194, 55, 247, 156, 56, 182, 13, 247, 238, - 181, 44, 104, 204, 221, 231, 133, 141, 55, 84, 150, 116, 173, 6, 126, 32, 5, 198, 136, 201, - 134, 173, 212, 161, 248, 38, 98, 26, 53, 53, 174, 90, 63, 69, 124, 203, 100, 48, 222, 25, 31, - 98, 137, 53, 116, 103, 100, 19, 32, 6, 252, 1, 171, 25, 163, 75, 127, 142, 17, 220, 134, 250, - 116, 98, 111, 61, 73, 115, 3, 234, 22, 151, 77, 50, 92, 61, 67, 144, 237, 2, 116, 32, 2, 211, - 141, 169, 190, 197, 245, 223, 215, 35, 247, 90, 245, 119, 5, 4, 226, 182, 163, 98, 109, 40, 29, - 255, 140, 129, 207, 232, 121, 108, 157, 134, 32, 4, 229, 156, 27, 166, 207, 171, 143, 43, 182, - 7, 64, 50, 141, 112, 253, 89, 192, 106, 175, 118, 205, 164, 99, 83, 182, 55, 151, 194, 243, - 187, 222, 32, 4, 84, 140, 219, 172, 171, 132, 66, 176, 14, 181, 240, 106, 210, 5, 93, 108, 229, - 240, 28, 190, 19, 166, 43, 81, 6, 202, 187, 163, 113, 60, 112, 7, 30, 56, 95, 93, 65, 107, 32, - 178, 223, 114, 163, 90, 50, 171, 52, 10, 213, 111, 89, 246, 18, 76, 178, 252, 77, 219, 102, - 185, 114, 197, 12, 114, 192, 138, 45, 225, 107, 22, 106, 224, 41, 137, 103, 248, 223, 22, 39, - 61, 244, 177, 198, 228, 253, 97, 173, 154, 127, 57, 66, 129, 207, 106, 251, 250, 124, 108, 169, - 249, 27, 120, 207, 2, 180, 162, 185, 140, 51, 198, 145, 199, 171, 237, 142, 132, 163, 120, 55, - 143, 186, 52, 166, 177, 88, 152, 150, 179, 120, 123, 7, 138, 22, 8, 51, 13, 121, 209, 194, 84, - 146, 81, 13, 118, 191, 45, 15, 209, 108, 251, 217, 50, 208, 151, 247, 91, 64, 237, 174, 212, - 192, 63, 52, 37, 215, 6, 133, 135, 109, 38, 76, 158, 194, 112, 134, 65, 161, 23, 130, 7, 29, - 86, 115, 26, 184, 186, 17, 7, 4, 35, 160, 91, 122, 92, 47, 92, 145, 117, 116, 211, 43, 187, 31, - 53, 154, 224, 251, 167, 31, 167, 102, 25, 129, 146, 233, 128, 114, 177, 12, 67, 71, 44, 105, - 254, 223, 89, 216, 115, 42, 45, 247, 206, 176, 156, 12, 153, 45, 13, 251, 215, 172, 248, 210, - 214, 153, 245, 13, 166, 237, 50, 171, 170, 143, 159, 139, 197, 2, 32, 7, 172, 215, 165, 196, 4, - 43, 142, 132, 176, 80, 252, 203, 135, 109, 163, 211, 28, 76, 93, 172, 241, 129, 98, 17, 228, - 146, 220, 113, 156, 1, 184, 32, 3, 95, 208, 58, 3, 34, 144, 82, 63, 225, 146, 62, 58, 97, 192, - 221, 34, 203, 197, 77, 11, 10, 213, 215, 59, 46, 68, 206, 177, 216, 96, 45, 2, 8, 37, 73, 254, - 131, 157, 74, 167, 45, 231, 116, 153, 4, 129, 194, 210, 57, 116, 186, 154, 142, 35, 0, 191, - 198, 123, 192, 101, 198, 36, 11, 109, 251, 219, 22, 52, 188, 101, 175, 238, 251, 217, 42, 252, - 130, 173, 185, 82, 55, 1, 61, 248, 122, 61, 130, 157, 213, 235, 44, 103, 164, 94, 114, 88, 245, - 99, 20, 43, 249, 57, 98, 86, 177, 77, 57, 34, 62, 194, 40, 56, 105, 63, 142, 57, 215, 243, 49, - 109, 59, 109, 185, 142, 251, 56, 193, 39, 252, 195, 136, 193, 240, 72, 65, 241, 249, 71, 116, - 224, 227, 15, 162, 221, 45, 63, 46, 167, 114, 124, 200, 166, 251, 114, 77, 178, 34, 84, 169, - 165, 246, 66, 73, 78, 113, 115, 1, 120, 194, 171, 65, 135, 89, 54, 133, 205, 114, 2, 37, 232, - 112, 176, 15, 197, 164, 17, 248, 213, 189, 94, 22, 162, 91, 206, 237, 171, 111, 228, 106, 7, - 67, 203, 137, 134, 203, 148, 74, 76, 195, 150, 243, 35, 225, 237, 255, 101, 214, 133, 134, 186, - 220, 164, 167, 111, 79, 98, 26, 96, 213, 132, 26, 235, 97, 233, 166, 103, 42, 66, 145, 182, - 169, 127, 195, 28, 89, 69, 244, 193, 213, 237, 209, 167, 164, 31, 25, 35, 187, 229, 146, 117, - 67, 238, 190, 16, 96, 92, 169, 231, 87, 127, 81, 158, 229, 8, 37, 233, 213, 154, 139, 101, 103, - 128, 137, 232, 243, 179, 19, 171, 248, 8, 62, 240, 129, 84, 231, 116, 81, 87, 171, 7, 185, 132, - 62, 177, 218, 218, 189, 29, 153, 247, 103, 151, 127, 204, 16, 88, 152, 104, 233, 58, 161, 255, - 3, 185, 241, 81, 183, 10, 160, 199, 70, 246, 114, 29, 188, 19, 192, 161, 232, 150, 188, 199, - 186, 119, 192, 189, 101, 36, 57, 192, 251, 86, 202, 34, 218, 223, 60, 208, 196, 229, 46, 246, - 84, 27, 164, 74, 125, 185, 72, 240, 169, 55, 218, 7, 246, 177, 217, 159, 216, 67, 57, 112, 240, - 196, 8, 169, 236, 252, 187, 134, 180, 238, 106, 87, 248, 45, 26, 51, 89, 121, 208, 51, 64, 232, - 125, 22, 84, 176, 171, 67, 74, 22, 183, 244, 158, 204, 152, 84, 181, 211, 172, 142, 234, 220, - 9, 99, 223, 22, 246, 69, 69, 234, 146, 80, 90, 45, 203, 22, 48, 132, 255, 43, 182, 41, 114, - 225, 11, 234, 215, 180, 37, 148, 50, 36, 99, 91, 230, 108, 72, 148, 11, 43, 179, 194, 199, 77, - 158, 6, 76, 215, 176, 65, 68, 202, 156, 111, 130, 150, 8, 177, 76, 131, 101, 145, 253, 3, 135, - 186, 26, 10, 98, 199, 227, 177, 37, 212, 10, 191, 95, 30, 178, 47, 58, 193, 111, 161, 13, 124, - 159, 148, 167, 229, 75, 215, 25, 50, 185, 158, 146, 208, 222, 191, 50, 54, 152, 19, 171, 153, - 57, 88, 69, 45, 152, 185, 233, 13, 250, 36, 169, 142, 104, 60, 213, 67, 240, 88, 59, 32, 0, - 166, 143, 42, 142, 87, 169, 31, 11, 27, 210, 12, 68, 31, 182, 65, 117, 250, 144, 16, 91, 173, - 97, 19, 254, 149, 210, 84, 21, 253, 96, 120, 32, 4, 41, 156, 3, 173, 114, 108, 175, 146, 233, - 175, 17, 238, 202, 118, 142, 94, 121, 182, 45, 179, 102, 148, 213, 156, 196, 66, 160, 19, 5, - 29, 39, 32, 6, 108, 64, 255, 166, 174, 19, 182, 248, 170, 76, 25, 101, 120, 24, 239, 2, 23, 63, - 32, 9, 94, 175, 138, 77, 170, 63, 183, 143, 59, 115, 142, 32, 3, 44, 122, 23, 255, 24, 38, 243, - 213, 251, 142, 134, 163, 16, 187, 220, 20, 13, 29, 222, 245, 71, 0, 161, 5, 181, 218, 247, 13, - 75, 58, 32, 32, 7, 229, 194, 112, 210, 213, 228, 202, 0, 76, 189, 118, 147, 194, 36, 29, 153, - 60, 12, 190, 196, 241, 195, 93, 59, 190, 241, 148, 101, 110, 25, 12, 32, 5, 173, 2, 59, 106, - 96, 5, 29, 145, 49, 0, 14, 23, 82, 156, 122, 170, 103, 10, 65, 39, 146, 245, 198, 6, 26, 185, - 150, 14, 65, 16, 66, 32, 3, 8, 145, 103, 131, 196, 179, 140, 37, 249, 147, 115, 195, 116, 2, - 74, 246, 4, 56, 1, 171, 104, 77, 160, 175, 85, 36, 230, 200, 80, 38, 72, 32, 1, 132, 72, 179, - 193, 226, 89, 198, 18, 252, 201, 185, 225, 186, 1, 37, 123, 2, 28, 0, 213, 180, 38, 208, 87, - 170, 146, 115, 100, 40, 19, 36, 32, 3, 171, 35, 71, 90, 156, 125, 54, 148, 224, 20, 204, 191, - 234, 21, 246, 170, 77, 143, 54, 167, 147, 24, 19, 245, 232, 152, 177, 125, 251, 9, 0, 32, 6, - 32, 19, 73, 56, 101, 113, 222, 150, 126, 103, 38, 64, 23, 197, 61, 36, 93, 105, 0, 69, 204, - 175, 230, 198, 170, 11, 144, 166, 153, 47, 109, 32, 1, 29, 213, 165, 59, 53, 227, 227, 171, 79, - 44, 174, 51, 97, 175, 90, 20, 63, 111, 214, 99, 68, 145, 112, 78, 127, 108, 10, 25, 250, 152, - 67, 32, 6, 204, 127, 175, 144, 38, 255, 80, 252, 77, 226, 7, 181, 210, 249, 17, 156, 173, 228, - 22, 24, 108, 101, 222, 196, 125, 124, 34, 21, 26, 41, 60, 32, 1, 1, 245, 202, 107, 202, 77, - 104, 34, 22, 181, 60, 149, 47, 83, 238, 119, 147, 20, 28, 27, 150, 176, 240, 199, 204, 215, 99, - 238, 66, 11, 162, 32, 0, 41, 135, 231, 101, 240, 74, 109, 97, 213, 230, 13, 220, 132, 204, 79, - 104, 239, 38, 4, 222, 31, 243, 88, 228, 158, 77, 74, 113, 104, 32, 231, 32, 6, 253, 194, 225, - 44, 163, 117, 146, 188, 76, 162, 246, 189, 79, 123, 139, 161, 68, 20, 56, 171, 200, 254, 88, - 60, 98, 118, 29, 4, 155, 15, 226, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 2, 100, 32, 251, 198, 159, 1, 247, 12, 160, 94, 135, - 200, 45, 246, 119, 38, 108, 199, 147, 140, 94, 250, 44, 232, 201, 29, 38, 60, 232, 115, 42, 32, - 6, 248, 21, 91, 221, 143, 155, 216, 33, 110, 122, 203, 49, 112, 175, 80, 39, 136, 229, 70, 175, - 81, 119, 15, 175, 184, 32, 24, 109, 127, 251, 196, 32, 1, 213, 211, 90, 187, 239, 184, 54, 242, - 41, 23, 119, 156, 57, 168, 25, 107, 14, 142, 120, 30, 20, 131, 18, 120, 71, 99, 187, 95, 73, - 102, 87, 32, 7, 232, 182, 129, 69, 143, 174, 25, 223, 66, 49, 64, 32, 174, 19, 228, 47, 172, - 119, 217, 99, 92, 3, 197, 18, 155, 219, 138, 215, 65, 146, 101, 32, 3, 147, 118, 65, 245, 241, - 204, 90, 229, 135, 53, 20, 61, 6, 127, 54, 246, 193, 246, 52, 109, 53, 128, 192, 111, 131, 224, - 230, 183, 22, 133, 186, 32, 5, 43, 178, 109, 43, 78, 76, 194, 131, 70, 113, 67, 218, 111, 135, - 0, 239, 165, 249, 8, 114, 44, 141, 60, 219, 106, 250, 90, 66, 164, 36, 0, 32, 1, 46, 154, 148, - 230, 179, 190, 49, 33, 98, 117, 94, 58, 101, 183, 243, 209, 29, 84, 71, 18, 165, 153, 81, 93, - 110, 234, 123, 246, 45, 217, 169, 32, 7, 170, 50, 2, 39, 193, 159, 151, 186, 19, 168, 237, 7, - 166, 54, 228, 197, 89, 205, 54, 54, 68, 171, 158, 194, 208, 78, 68, 136, 32, 180, 132, 32, 5, - 159, 18, 60, 150, 103, 158, 110, 170, 224, 38, 17, 161, 240, 2, 15, 87, 98, 9, 64, 185, 165, - 62, 157, 8, 107, 112, 34, 114, 202, 18, 85, 32, 3, 237, 206, 118, 90, 116, 2, 108, 83, 0, 105, - 254, 108, 86, 132, 242, 9, 227, 96, 66, 2, 58, 35, 42, 82, 24, 237, 222, 47, 162, 213, 32, 32, - 1, 80, 55, 105, 41, 207, 136, 201, 137, 192, 241, 230, 139, 235, 242, 49, 19, 33, 1, 252, 96, - 138, 109, 232, 163, 112, 72, 220, 161, 12, 100, 124, 32, 3, 128, 167, 243, 198, 207, 107, 166, - 173, 142, 164, 251, 83, 11, 106, 58, 53, 24, 115, 181, 187, 128, 189, 135, 116, 68, 66, 31, - 246, 125, 220, 250, 32, 0, 122, 159, 127, 39, 250, 214, 143, 89, 84, 201, 167, 17, 235, 230, - 136, 118, 223, 21, 216, 95, 229, 186, 152, 7, 40, 220, 125, 47, 113, 119, 53, 32, 0, 2, 2, 113, - 107, 140, 184, 231, 247, 11, 70, 82, 140, 156, 124, 11, 62, 1, 250, 30, 25, 84, 251, 4, 237, - 215, 208, 241, 17, 150, 241, 133, 32, 1, 5, 5, 136, 49, 246, 212, 227, 60, 197, 251, 113, 194, - 188, 179, 50, 241, 137, 251, 51, 52, 160, 86, 90, 197, 75, 57, 250, 177, 148, 180, 155, 32, 5, - 177, 251, 114, 115, 201, 142, 215, 249, 28, 205, 105, 118, 251, 113, 123, 132, 205, 140, 72, - 243, 202, 2, 195, 248, 96, 28, 182, 12, 140, 165, 57, 32, 4, 186, 10, 44, 47, 66, 34, 143, 51, - 253, 221, 196, 150, 134, 156, 69, 139, 160, 116, 199, 220, 244, 81, 127, 249, 196, 1, 250, 141, - 110, 46, 53, 32, 5, 21, 242, 125, 66, 20, 198, 53, 163, 185, 171, 58, 47, 187, 233, 164, 129, - 145, 77, 156, 27, 12, 240, 165, 235, 10, 4, 248, 9, 2, 115, 198, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, 255, 254, - 239, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 239, 255, 225, 32, 6, 187, 191, 78, 79, 246, 26, 10, 85, 107, 225, - 214, 19, 178, 27, 151, 30, 106, 24, 73, 101, 81, 222, 70, 200, 68, 51, 23, 188, 125, 65, 207, - 32, 4, 61, 176, 228, 242, 140, 243, 114, 106, 144, 62, 135, 193, 36, 228, 246, 132, 223, 144, - 177, 202, 108, 133, 98, 123, 14, 0, 161, 88, 231, 193, 69, 32, 4, 61, 176, 228, 242, 140, 243, - 114, 106, 144, 62, 135, 193, 36, 228, 246, 132, 223, 144, 177, 202, 108, 133, 98, 123, 14, 0, - 161, 88, 231, 193, 69, 32, 4, 61, 176, 228, 242, 140, 243, 114, 106, 144, 62, 135, 193, 36, - 228, 246, 132, 223, 144, 177, 202, 108, 133, 98, 123, 14, 0, 161, 88, 231, 193, 69, 32, 0, 216, - 34, 32, 138, 213, 169, 14, 208, 160, 180, 195, 54, 132, 203, 118, 4, 235, 206, 36, 242, 72, 86, - 179, 121, 240, 75, 220, 250, 9, 126, 177, 32, 2, 8, 57, 154, 188, 175, 168, 248, 107, 139, 189, - 179, 77, 180, 196, 64, 36, 187, 227, 74, 77, 141, 231, 103, 41, 183, 33, 165, 104, 92, 201, 94, - 32, 2, 16, 133, 8, 171, 44, 120, 218, 134, 25, 109, 127, 192, 115, 201, 240, 93, 44, 117, 128, - 212, 17, 169, 80, 91, 207, 101, 208, 133, 194, 249, 5, 32, 2, 175, 67, 185, 76, 143, 174, 195, - 170, 35, 196, 37, 85, 43, 222, 248, 16, 217, 42, 98, 197, 232, 146, 53, 249, 165, 192, 115, 20, - 74, 246, 174, 32, 5, 111, 151, 157, 196, 194, 232, 28, 7, 244, 155, 10, 209, 225, 199, 64, 195, - 132, 255, 99, 81, 212, 207, 104, 136, 168, 39, 95, 118, 36, 217, 132, 32, 2, 144, 66, 96, 53, - 27, 231, 96, 173, 19, 24, 19, 244, 174, 34, 184, 181, 93, 180, 41, 230, 66, 183, 161, 123, 53, - 78, 154, 172, 122, 135, 186, 32, 1, 152, 248, 63, 117, 121, 11, 37, 226, 127, 3, 142, 7, 106, - 74, 25, 58, 10, 153, 46, 177, 141, 118, 221, 236, 40, 212, 251, 54, 70, 250, 12, 32, 6, 50, - 185, 46, 241, 55, 80, 15, 7, 78, 242, 10, 66, 37, 20, 235, 66, 33, 174, 157, 249, 70, 5, 26, - 183, 82, 133, 224, 154, 95, 123, 58, 32, 5, 253, 178, 186, 90, 250, 135, 19, 111, 87, 189, 193, - 166, 170, 98, 185, 215, 109, 154, 240, 33, 35, 208, 211, 87, 62, 69, 65, 165, 179, 103, 154, - 32, 5, 106, 244, 118, 69, 159, 249, 155, 236, 88, 59, 78, 67, 117, 22, 146, 183, 124, 52, 1, - 170, 253, 246, 105, 157, 232, 168, 43, 138, 73, 139, 158, 32, 2, 18, 204, 95, 191, 3, 208, 65, - 39, 131, 233, 114, 195, 117, 119, 8, 159, 131, 220, 220, 160, 241, 229, 11, 247, 96, 249, 37, - 138, 114, 208, 247, 32, 0, 163, 189, 96, 73, 84, 33, 86, 211, 55, 66, 111, 79, 109, 74, 66, - 138, 102, 11, 140, 24, 188, 32, 56, 46, 141, 193, 48, 161, 121, 183, 136, 32, 2, 84, 60, 50, - 213, 51, 208, 153, 180, 87, 255, 223, 115, 159, 157, 97, 11, 52, 174, 103, 36, 125, 99, 208, - 66, 158, 206, 162, 82, 113, 159, 44, 32, 7, 206, 132, 103, 44, 8, 251, 3, 4, 204, 128, 221, 14, - 221, 90, 118, 169, 220, 45, 150, 232, 167, 246, 14, 3, 176, 154, 49, 39, 48, 205, 1, 32, 2, - 126, 227, 160, 140, 85, 176, 180, 247, 35, 79, 98, 199, 146, 81, 226, 39, 120, 143, 37, 66, - 149, 140, 160, 224, 253, 196, 80, 73, 54, 166, 41, 32, 0, 136, 230, 108, 206, 7, 172, 148, 42, - 120, 220, 17, 50, 109, 250, 165, 53, 244, 136, 9, 190, 10, 43, 255, 11, 26, 231, 44, 215, 163, - 106, 115, 32, 6, 85, 207, 138, 54, 242, 201, 19, 219, 246, 206, 128, 82, 159, 129, 132, 162, - 195, 184, 235, 33, 32, 126, 11, 166, 93, 129, 175, 179, 205, 135, 0, 32, 7, 7, 172, 237, 220, - 64, 70, 119, 171, 250, 118, 28, 177, 99, 26, 172, 238, 226, 223, 20, 217, 202, 255, 54, 196, - 93, 179, 223, 106, 173, 49, 111, 32, 7, 206, 177, 11, 109, 6, 131, 77, 220, 64, 132, 119, 159, - 193, 180, 48, 239, 63, 216, 62, 112, 208, 6, 35, 73, 190, 219, 138, 220, 23, 86, 178, 7, 185, - 129, 243, 126, 12, 23, 81, 193, 216, 100, 105, 241, 96, 224, 222, 187, 41, 87, 235, 59, 254, - 254, 106, 215, 151, 198, 216, 137, 87, 154, 17, 37, 116, 103, 217, 130, 125, 42, 71, 113, 160, - 130, 226, 178, 176, 106, 211, 188, 187, 126, 255, 56, 142, 114, 209, 252, 111, 34, 196, 50, - 201, 217, 176, 242, 186, 227, 61, 88, 41, 243, 62, 108, 65, 247, 58, 160, 126, 117, 196, 165, - 187, 174, 40, 172, 249, 159, 23, 38, 110, 145, 238, 99, 102, 145, 192, 151, 109, 212, 7, 144, - 241, 219, 159, 240, 4, 186, 81, 176, 23, 62, 250, 13, 221, 38, 179, 166, 120, 75, 229, 196, - 180, 179, 248, 57, 206, 40, 99, 254, 51, 230, 152, 210, 217, 212, 43, 71, 107, 171, 142, 71, - 14, 62, 110, 41, 182, 36, 32, 96, 88, 122, 254, 193, 218, 68, 147, 165, 70, 14, 28, 171, 15, - 190, 222, 28, 255, 161, 229, 154, 51, 48, 225, 56, 151, 205, 40, 203, 139, 117, 226, 253, 28, - 32, 75, 6, 59, 72, 255, 67, 219, 118, 21, 125, 51, 45, 171, 60, 71, 178, 34, 1, 103, 238, 163, - 124, 189, 119, 30, 6, 204, 249, 51, 163, 124, 49, 48, 66, 231, 52, 43, 104, 108, 3, 211, 196, - 2, 32, 2, 247, 104, 232, 78, 138, 140, 199, 2, 140, 39, 120, 94, 185, 164, 123, 77, 125, 113, - 179, 173, 99, 143, 19, 123, 80, 101, 84, 6, 173, 103, 69, 32, 1, 15, 198, 103, 129, 99, 8, 22, - 107, 216, 82, 89, 124, 124, 99, 18, 26, 181, 85, 166, 91, 48, 19, 253, 134, 85, 43, 62, 164, - 107, 61, 150, 2, 8, 62, 44, 109, 175, 20, 72, 111, 8, 201, 64, 93, 178, 214, 219, 167, 10, 170, - 159, 48, 151, 59, 160, 66, 114, 152, 134, 248, 127, 120, 30, 179, 14, 139, 170, 141, 93, 121, - 239, 194, 14, 102, 7, 189, 196, 115, 187, 60, 215, 18, 222, 131, 7, 75, 212, 188, 192, 76, 203, - 238, 85, 79, 12, 54, 231, 86, 151, 55, 0, 220, 136, 78, 126, 115, 209, 44, 92, 28, 137, 98, 83, - 22, 139, 55, 156, 16, 89, 230, 102, 74, 159, 250, 44, 229, 98, 135, 143, 187, 208, 228, 16, - 121, 134, 209, 217, 57, 22, 60, 30, 69, 253, 111, 210, 50, 102, 144, 215, 31, 249, 250, 32, - 200, 168, 28, 236, 35, 123, 186, 205, 119, 105, 144, 137, 225, 179, 93, 226, 177, 47, 220, 80, - 135, 139, 16, 59, 142, 114, 145, 122, 199, 250, 90, 16, 218, 146, 188, 78, 179, 117, 0, 127, - 65, 73, 175, 31, 243, 113, 84, 117, 9, 243, 236, 149, 222, 78, 175, 15, 254, 3, 30, 184, 97, - 18, 206, 47, 248, 251, 115, 153, 87, 228, 133, 185, 202, 162, 8, 67, 150, 128, 8, 9, 249, 181, - 204, 76, 95, 153, 145, 183, 153, 231, 164, 80, 156, 141, 63, 16, 150, 55, 32, 29, 23, 94, 121, - 91, 230, 102, 86, 203, 113, 159, 74, 128, 251, 129, 47, 111, 246, 84, 15, 173, 114, 238, 116, - 16, 155, 12, 31, 135, 15, 42, 107, 57, 231, 57, 31, 248, 8, 92, 249, 101, 16, 8, 74, 255, 104, - 122, 65, 96, 127, 45, 224, 74, 185, 38, 177, 102, 195, 110, 102, 251, 117, 128, 155, 193, 70, - 209, 131, 134, 95, 63, 224, 18, 67, 171, 13, 122, 132, 252, 153, 36, 224, 133, 210, 189, 180, - 69, 146, 227, 165, 98, 191, 84, 224, 137, 164, 97, 2, 32, 195, 224, 144, 88, 12, 7, 243, 172, - 61, 5, 72, 70, 231, 153, 25, 126, 141, 100, 242, 218, 86, 20, 90, 112, 209, 214, 32, 139, 78, - 52, 92, 234, 90, 86, 132, 197, 86, 73, 49, 109, 163, 185, 196, 195, 155, 141, 71, 195, 163, - 248, 147, 134, 215, 134, 31, 228, 223, 171, 121, 215, 38, 128, 8, 219, 153, 215, 227, 201, 119, - 48, 125, 31, 23, 13, 80, 120, 151, 246, 202, 241, 51, 242, 209, 218, 235, 129, 8, 75, 194, 189, - 27, 29, 123, 71, 22, 84, 43, 35, 129, 61, 245, 107, 25, 19, 158, 188, 56, 201, 24, 23, 148, - 115, 220, 185, 3, 231, 184, 65, 25, 159, 105, 232, 92, 122, 22, 105, 104, 136, 15, 213, 20, 47, - 241, 143, 10, 103, 38, 150, 60, 79, 110, 163, 8, 253, 180, 70, 46, 57, 62, 228, 197, 202, 96, - 179, 17, 52, 135, 35, 192, 169, 105, 94, 140, 38, 226, 17, 200, 114, 66, 199, 188, 124, 141, - 168, 29, 23, 194, 195, 44, 138, 2, 56, 50, 173, 213, 76, 21, 151, 35, 181, 172, 207, 17, 240, - 231, 59, 32, 3, 231, 1, 133, 105, 97, 226, 179, 108, 126, 58, 84, 78, 86, 117, 74, 66, 233, - 104, 124, 175, 92, 68, 143, 109, 254, 160, 50, 243, 105, 59, 13, 32, 4, 143, 212, 130, 250, - 198, 88, 191, 18, 127, 88, 135, 118, 245, 3, 215, 13, 226, 150, 186, 41, 244, 175, 119, 11, 49, - 58, 174, 57, 141, 150, 200, 32, 3, 237, 24, 186, 10, 144, 100, 60, 177, 121, 160, 180, 151, - 106, 22, 212, 84, 230, 225, 223, 37, 107, 47, 110, 218, 115, 0, 205, 45, 112, 24, 179, 32, 0, - 144, 221, 82, 119, 83, 231, 101, 41, 132, 156, 177, 221, 161, 88, 5, 214, 182, 89, 132, 4, 158, - 12, 118, 122, 164, 52, 12, 140, 9, 192, 80, 32, 4, 11, 74, 65, 121, 99, 166, 33, 25, 12, 205, - 197, 109, 12, 183, 195, 198, 3, 231, 165, 69, 227, 248, 35, 5, 51, 19, 62, 215, 100, 26, 189, - 32, 7, 168, 120, 147, 12, 236, 108, 124, 55, 116, 27, 30, 166, 94, 3, 133, 92, 22, 80, 90, 237, - 116, 149, 106, 173, 77, 220, 192, 229, 1, 30, 228, 32, 0, 144, 124, 82, 154, 203, 93, 210, 178, - 4, 170, 176, 46, 204, 196, 201, 236, 2, 84, 119, 191, 35, 246, 193, 165, 158, 125, 40, 189, - 131, 28, 131, 32, 4, 72, 62, 41, 77, 101, 174, 241, 217, 2, 85, 88, 23, 102, 98, 100, 246, 1, - 42, 59, 223, 145, 251, 96, 210, 207, 62, 148, 94, 193, 142, 66, 32, 6, 63, 126, 116, 113, 69, - 86, 163, 24, 77, 37, 12, 74, 147, 135, 149, 59, 48, 113, 216, 141, 16, 166, 41, 76, 46, 129, - 178, 61, 225, 110, 134, 32, 0, 80, 144, 156, 103, 169, 192, 140, 123, 99, 173, 4, 166, 136, 11, - 46, 255, 53, 133, 15, 96, 216, 177, 44, 197, 70, 150, 37, 178, 246, 60, 69, 32, 0, 132, 238, - 214, 50, 92, 196, 9, 21, 1, 39, 28, 225, 116, 106, 130, 110, 70, 137, 21, 241, 106, 248, 49, - 159, 55, 255, 54, 186, 120, 195, 63, 32, 3, 144, 197, 104, 217, 245, 26, 125, 76, 46, 117, 1, - 150, 69, 102, 216, 69, 34, 161, 24, 193, 244, 139, 104, 144, 104, 154, 225, 136, 232, 60, 31, - 32, 2, 150, 9, 175, 183, 81, 237, 68, 65, 118, 54, 196, 91, 47, 114, 143, 142, 238, 25, 231, - 186, 112, 185, 99, 25, 85, 118, 65, 96, 205, 92, 127, 32, 3, 165, 214, 95, 78, 123, 191, 221, - 120, 129, 38, 241, 81, 168, 36, 94, 249, 129, 118, 113, 204, 199, 132, 254, 55, 208, 87, 170, - 159, 189, 97, 1, 32, 1, 238, 74, 143, 113, 208, 95, 16, 104, 12, 141, 216, 231, 180, 104, 146, - 60, 240, 151, 243, 131, 171, 106, 247, 254, 175, 14, 61, 94, 95, 87, 229, 32, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 4, 204, 236, - 235, 136, 183, 129, 162, 75, 235, 210, 25, 81, 247, 166, 210, 135, 172, 130, 161, 80, 90, 183, - 84, 156, 89, 216, 30, 196, 41, 208, 215, 32, 6, 131, 10, 67, 164, 68, 109, 189, 205, 93, 11, 3, - 81, 113, 26, 19, 107, 200, 207, 195, 3, 37, 117, 200, 102, 191, 117, 234, 127, 119, 156, 148, - 32, 6, 78, 34, 240, 240, 171, 220, 113, 184, 143, 203, 245, 31, 63, 102, 106, 255, 10, 23, 162, - 53, 166, 172, 112, 18, 226, 180, 36, 99, 239, 188, 209, 32, 1, 183, 108, 72, 116, 66, 213, 158, - 5, 46, 172, 165, 253, 231, 4, 189, 2, 243, 188, 150, 98, 62, 67, 35, 241, 229, 112, 121, 169, - 136, 15, 215, 32, 1, 182, 189, 134, 28, 103, 154, 101, 48, 180, 19, 255, 53, 35, 8, 35, 68, 46, - 161, 212, 238, 126, 247, 94, 204, 7, 103, 109, 240, 214, 247, 80, 32, 4, 169, 74, 44, 134, 187, - 169, 93, 154, 131, 164, 218, 96, 104, 247, 117, 192, 187, 227, 223, 39, 195, 76, 70, 78, 115, - 161, 59, 99, 211, 228, 230, 32, 2, 136, 171, 226, 58, 237, 9, 27, 178, 228, 34, 4, 135, 92, - 226, 106, 18, 248, 72, 116, 183, 254, 118, 65, 208, 44, 99, 122, 93, 174, 112, 130, 32, 7, 158, - 12, 64, 160, 138, 200, 229, 157, 96, 20, 234, 216, 250, 68, 27, 186, 162, 192, 113, 131, 68, - 246, 122, 73, 28, 26, 47, 2, 224, 187, 144, 32, 4, 5, 138, 33, 10, 4, 81, 61, 153, 108, 90, 11, - 76, 235, 68, 68, 68, 248, 214, 138, 90, 174, 9, 107, 184, 95, 26, 49, 45, 251, 114, 234, 32, 4, - 206, 163, 72, 223, 163, 17, 135, 31, 255, 156, 59, 21, 47, 26, 244, 100, 117, 78, 154, 135, - 166, 22, 173, 120, 87, 220, 14, 122, 45, 66, 130, 32, 0, 69, 121, 90, 55, 255, 55, 20, 161, - 234, 95, 71, 25, 228, 156, 246, 136, 36, 20, 208, 178, 171, 183, 223, 225, 139, 190, 20, 179, - 0, 183, 111, 32, 3, 88, 103, 218, 19, 224, 60, 253, 206, 139, 39, 133, 78, 71, 235, 251, 254, - 64, 109, 251, 172, 126, 151, 103, 191, 83, 10, 170, 137, 136, 0, 250, 32, 4, 103, 231, 120, - 239, 116, 211, 238, 132, 56, 69, 102, 77, 111, 66, 67, 93, 16, 196, 159, 204, 39, 184, 57, 11, - 27, 32, 61, 197, 245, 131, 48, 32, 6, 201, 76, 97, 138, 33, 147, 101, 37, 179, 85, 99, 20, 36, - 93, 12, 223, 242, 75, 178, 179, 51, 24, 79, 126, 90, 96, 142, 46, 126, 198, 112, 32, 1, 147, - 254, 58, 143, 38, 224, 92, 98, 33, 27, 206, 51, 214, 71, 51, 169, 97, 58, 20, 215, 159, 183, - 146, 151, 48, 110, 248, 42, 1, 157, 148, 32, 3, 224, 19, 161, 116, 19, 29, 60, 179, 169, 117, - 93, 124, 146, 171, 150, 171, 136, 63, 148, 246, 202, 224, 19, 215, 214, 158, 108, 156, 1, 97, - 110, 32, 7, 176, 192, 121, 94, 231, 205, 149, 139, 133, 27, 50, 43, 171, 6, 70, 142, 121, 161, - 157, 143, 145, 212, 218, 125, 73, 178, 124, 68, 7, 165, 177, 32, 3, 9, 28, 139, 79, 223, 84, - 123, 23, 227, 102, 28, 232, 73, 253, 106, 156, 39, 149, 169, 66, 186, 74, 175, 78, 114, 101, 1, - 108, 97, 21, 57, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 225, 32, 2, 91, 68, 30, 167, 149, 38, 118, 145, 95, 148, 62, 85, 145, 230, 53, 55, 130, 176, + 244, 48, 67, 173, 42, 214, 141, 102, 245, 187, 90, 230, 19, 32, 5, 34, 69, 70, 141, 13, 255, + 133, 225, 104, 55, 188, 22, 185, 42, 245, 181, 99, 180, 84, 29, 89, 106, 130, 235, 89, 220, 87, + 144, 176, 77, 60, 32, 5, 34, 69, 70, 141, 13, 255, 133, 225, 104, 55, 188, 22, 185, 42, 245, + 181, 99, 180, 84, 29, 89, 106, 130, 235, 89, 220, 87, 144, 176, 77, 60, 32, 5, 34, 69, 70, 141, + 13, 255, 133, 225, 104, 55, 188, 22, 185, 42, 245, 181, 99, 180, 84, 29, 89, 106, 130, 235, 89, + 220, 87, 144, 176, 77, 60, 32, 4, 172, 50, 234, 18, 80, 78, 10, 45, 122, 188, 84, 165, 217, + 106, 152, 186, 208, 180, 33, 157, 178, 199, 196, 247, 86, 102, 162, 254, 117, 30, 72, 32, 0, + 181, 233, 160, 243, 50, 209, 135, 128, 205, 250, 37, 164, 203, 220, 42, 145, 194, 139, 44, 213, + 121, 80, 254, 184, 153, 32, 79, 69, 57, 83, 216, 32, 6, 19, 23, 214, 231, 29, 95, 179, 150, + 132, 169, 32, 196, 217, 91, 169, 163, 124, 152, 184, 237, 143, 180, 231, 117, 152, 167, 123, + 83, 209, 158, 147, 32, 6, 53, 28, 103, 222, 115, 121, 45, 17, 1, 207, 189, 245, 123, 206, 196, + 83, 65, 169, 108, 164, 89, 160, 91, 82, 95, 127, 247, 39, 181, 34, 110, 32, 3, 31, 236, 182, + 13, 15, 107, 36, 19, 38, 250, 71, 179, 208, 136, 106, 154, 173, 227, 31, 17, 152, 79, 12, 144, + 63, 104, 61, 227, 164, 31, 228, 32, 1, 159, 112, 153, 196, 243, 158, 187, 254, 28, 60, 241, 46, + 105, 131, 157, 200, 26, 27, 122, 61, 228, 124, 189, 29, 242, 143, 79, 146, 139, 250, 153, 32, + 6, 193, 59, 12, 1, 213, 95, 240, 123, 204, 211, 89, 91, 2, 87, 245, 137, 143, 166, 33, 52, 104, + 219, 164, 137, 65, 209, 54, 190, 218, 216, 212, 32, 3, 229, 249, 197, 153, 170, 217, 112, 20, + 238, 233, 2, 174, 64, 10, 149, 156, 93, 238, 183, 26, 131, 97, 252, 235, 129, 71, 6, 23, 175, + 79, 173, 32, 6, 253, 237, 190, 189, 104, 8, 118, 156, 19, 217, 149, 238, 165, 120, 5, 142, 216, + 5, 93, 213, 251, 77, 5, 153, 131, 157, 90, 222, 105, 122, 120, 32, 2, 129, 96, 22, 155, 203, + 11, 182, 232, 185, 144, 137, 252, 51, 123, 36, 247, 87, 64, 32, 96, 251, 255, 208, 25, 98, 57, + 66, 175, 117, 176, 193, 32, 3, 193, 122, 195, 109, 117, 12, 99, 231, 45, 239, 98, 138, 155, 65, + 11, 130, 87, 253, 152, 152, 204, 121, 224, 34, 239, 131, 187, 169, 165, 80, 147, 32, 4, 246, + 117, 1, 253, 181, 210, 144, 241, 105, 62, 229, 159, 173, 99, 208, 146, 160, 204, 225, 177, 13, + 249, 172, 46, 189, 182, 61, 154, 161, 37, 214, 32, 7, 204, 121, 1, 160, 97, 61, 117, 31, 237, + 167, 240, 191, 56, 46, 150, 73, 122, 206, 192, 102, 9, 94, 103, 110, 233, 248, 26, 236, 255, + 132, 154, 32, 4, 137, 131, 56, 170, 8, 125, 216, 70, 207, 87, 130, 132, 70, 1, 125, 242, 43, + 121, 151, 176, 95, 112, 189, 214, 242, 170, 214, 129, 55, 154, 128, 32, 4, 201, 114, 110, 175, + 33, 250, 89, 22, 203, 194, 137, 227, 107, 31, 82, 20, 62, 160, 204, 121, 71, 65, 18, 220, 240, + 245, 105, 137, 65, 104, 186, 32, 5, 73, 163, 150, 210, 209, 44, 45, 88, 147, 177, 140, 73, 245, + 231, 52, 114, 8, 113, 216, 127, 88, 253, 134, 215, 221, 35, 183, 71, 99, 55, 163, 32, 4, 183, + 149, 133, 222, 16, 181, 158, 187, 26, 112, 160, 13, 165, 89, 191, 175, 117, 135, 204, 186, 117, + 164, 118, 218, 181, 24, 248, 8, 182, 165, 181, 32, 5, 180, 94, 217, 181, 61, 240, 200, 208, 79, + 68, 123, 238, 236, 167, 131, 241, 128, 31, 247, 36, 252, 212, 75, 196, 240, 89, 232, 91, 20, + 91, 209, 32, 4, 44, 50, 77, 67, 251, 45, 217, 70, 42, 101, 206, 195, 184, 161, 10, 85, 151, 59, + 51, 28, 208, 81, 78, 247, 241, 88, 37, 195, 149, 151, 228, 32, 5, 164, 73, 93, 67, 178, 117, + 132, 236, 197, 104, 163, 216, 184, 202, 92, 66, 199, 96, 214, 233, 218, 106, 44, 172, 235, 112, + 136, 66, 249, 90, 126, 32, 3, 216, 209, 48, 53, 210, 164, 231, 47, 146, 117, 140, 11, 65, 169, + 131, 250, 39, 109, 15, 216, 66, 25, 51, 242, 199, 136, 251, 208, 55, 7, 38, 32, 0, 28, 192, + 139, 158, 30, 114, 208, 27, 253, 203, 86, 73, 93, 185, 133, 138, 151, 159, 169, 44, 150, 149, + 137, 211, 84, 61, 186, 3, 10, 45, 127, 32, 7, 151, 176, 122, 197, 209, 248, 208, 207, 146, 225, + 176, 196, 201, 153, 23, 74, 153, 165, 107, 70, 98, 205, 161, 122, 185, 108, 196, 218, 16, 169, + 168, 32, 3, 137, 111, 149, 102, 153, 147, 0, 104, 36, 138, 100, 191, 144, 237, 41, 233, 209, + 213, 131, 135, 16, 234, 103, 148, 243, 231, 86, 220, 198, 217, 201, 32, 2, 125, 208, 61, 184, + 217, 116, 160, 114, 35, 47, 160, 98, 129, 153, 162, 43, 22, 18, 93, 47, 145, 110, 192, 97, 211, + 116, 207, 38, 25, 85, 24, 32, 4, 152, 150, 235, 18, 135, 172, 152, 201, 60, 89, 181, 164, 133, + 155, 248, 226, 65, 214, 142, 49, 219, 216, 225, 220, 73, 222, 23, 153, 75, 82, 156, 32, 2, 76, + 75, 117, 137, 67, 214, 76, 100, 158, 44, 218, 210, 66, 205, 252, 113, 32, 235, 71, 24, 237, + 236, 112, 238, 36, 239, 11, 204, 165, 169, 78, 32, 6, 175, 201, 40, 59, 171, 43, 36, 172, 136, + 108, 255, 100, 80, 227, 10, 174, 193, 212, 112, 189, 11, 125, 254, 168, 172, 28, 240, 125, 57, + 253, 45, 32, 1, 255, 183, 117, 104, 114, 177, 68, 60, 194, 230, 255, 45, 181, 89, 233, 102, 27, + 100, 223, 222, 254, 63, 101, 162, 246, 68, 126, 58, 217, 87, 79, 32, 0, 114, 182, 113, 16, 115, + 168, 121, 94, 190, 154, 99, 70, 138, 167, 215, 132, 181, 223, 136, 147, 14, 148, 170, 167, 159, + 29, 60, 250, 92, 24, 190, 32, 7, 107, 94, 193, 123, 29, 115, 158, 67, 69, 79, 230, 88, 1, 84, + 42, 183, 6, 188, 186, 23, 202, 40, 94, 191, 211, 15, 171, 160, 230, 206, 22, 32, 4, 236, 198, + 161, 176, 103, 140, 241, 199, 93, 76, 39, 115, 254, 213, 231, 156, 64, 243, 38, 213, 147, 204, + 53, 0, 133, 24, 170, 120, 158, 49, 121, 32, 0, 70, 11, 93, 84, 254, 167, 35, 223, 122, 21, 131, + 118, 66, 134, 48, 64, 156, 144, 114, 42, 84, 93, 42, 166, 82, 19, 25, 33, 96, 110, 224, 32, 5, + 172, 169, 28, 33, 136, 147, 144, 105, 246, 97, 83, 182, 80, 191, 36, 150, 127, 167, 6, 69, 190, + 182, 91, 132, 194, 174, 247, 39, 151, 95, 246, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 3, 76, 211, 160, 97, 229, 52, 131, 185, + 195, 236, 105, 209, 142, 248, 93, 71, 33, 48, 49, 104, 170, 92, 146, 98, 174, 83, 1, 102, 205, + 200, 192, 32, 7, 21, 81, 39, 117, 159, 244, 252, 181, 175, 145, 237, 1, 23, 50, 219, 46, 206, + 97, 51, 195, 250, 165, 147, 122, 124, 78, 133, 234, 165, 212, 70, 32, 2, 178, 201, 123, 52, + 117, 122, 50, 239, 44, 197, 75, 38, 69, 125, 242, 79, 142, 17, 155, 233, 154, 65, 23, 28, 123, + 55, 181, 160, 88, 161, 235, 32, 2, 97, 38, 117, 200, 21, 124, 210, 53, 130, 17, 191, 250, 34, + 14, 52, 151, 68, 138, 131, 98, 249, 199, 148, 80, 56, 252, 154, 65, 213, 27, 192, 32, 0, 205, + 164, 83, 227, 233, 193, 236, 210, 30, 88, 170, 213, 13, 56, 163, 215, 160, 26, 150, 23, 215, + 32, 252, 71, 100, 125, 180, 18, 148, 175, 39, 32, 3, 45, 95, 106, 195, 89, 219, 152, 25, 57, + 128, 134, 236, 220, 170, 22, 10, 97, 237, 14, 147, 217, 20, 174, 29, 230, 46, 245, 87, 63, 10, + 9, 32, 3, 134, 64, 23, 104, 88, 168, 156, 44, 38, 141, 172, 137, 24, 124, 147, 215, 125, 196, + 252, 194, 131, 105, 70, 62, 139, 23, 141, 214, 75, 34, 196, 32, 7, 93, 19, 92, 57, 51, 193, + 163, 234, 57, 245, 251, 103, 204, 240, 215, 231, 38, 72, 197, 247, 151, 71, 191, 130, 143, 88, + 241, 1, 100, 102, 217, 32, 5, 43, 151, 202, 64, 35, 38, 41, 169, 58, 219, 96, 206, 11, 117, + 119, 84, 93, 194, 67, 15, 2, 242, 147, 229, 149, 91, 222, 249, 92, 31, 92, 32, 2, 89, 7, 44, + 73, 97, 6, 224, 231, 0, 56, 130, 53, 184, 253, 76, 88, 187, 244, 246, 33, 102, 42, 19, 92, 201, + 250, 169, 85, 234, 251, 36, 32, 5, 37, 89, 172, 166, 123, 192, 20, 204, 113, 206, 178, 126, + 109, 201, 135, 209, 180, 221, 30, 30, 97, 167, 219, 101, 162, 44, 245, 229, 176, 105, 184, 32, + 2, 209, 51, 25, 126, 164, 89, 205, 178, 219, 33, 106, 172, 156, 85, 134, 204, 144, 26, 171, + 220, 244, 133, 64, 46, 109, 7, 155, 135, 3, 196, 98, 32, 7, 168, 92, 40, 141, 187, 183, 115, + 78, 146, 156, 15, 97, 150, 45, 64, 224, 150, 100, 243, 178, 163, 219, 179, 111, 162, 215, 107, + 86, 87, 182, 159, 32, 0, 132, 167, 145, 122, 234, 98, 222, 154, 156, 118, 163, 208, 243, 48, + 51, 45, 24, 208, 224, 75, 117, 206, 138, 206, 103, 15, 227, 34, 57, 175, 135, 32, 5, 239, 184, + 156, 216, 201, 229, 236, 146, 243, 183, 254, 196, 139, 157, 236, 157, 30, 48, 142, 221, 118, + 118, 234, 33, 60, 254, 67, 243, 214, 168, 130, 32, 0, 46, 216, 103, 55, 129, 154, 253, 255, + 113, 23, 89, 175, 255, 179, 132, 214, 206, 245, 7, 52, 120, 138, 66, 122, 184, 203, 113, 156, + 214, 90, 65, 32, 0, 73, 33, 6, 193, 9, 222, 195, 71, 225, 131, 170, 231, 144, 206, 14, 16, 158, + 154, 184, 162, 0, 249, 182, 49, 74, 153, 197, 210, 39, 218, 148, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, + 255, 254, 239, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 239, 255, 225, 32, 2, 97, 27, 155, 37, 146, 202, 43, 214, + 62, 62, 237, 12, 122, 139, 243, 242, 150, 242, 183, 49, 130, 122, 247, 48, 56, 106, 114, 143, + 235, 8, 79, 32, 2, 185, 124, 96, 183, 155, 14, 136, 208, 75, 114, 37, 245, 191, 178, 178, 63, + 111, 67, 60, 28, 27, 238, 221, 224, 240, 253, 47, 91, 239, 13, 179, 32, 2, 185, 124, 96, 183, + 155, 14, 136, 208, 75, 114, 37, 245, 191, 178, 178, 63, 111, 67, 60, 28, 27, 238, 221, 224, + 240, 253, 47, 91, 239, 13, 179, 32, 2, 185, 124, 96, 183, 155, 14, 136, 208, 75, 114, 37, 245, + 191, 178, 178, 63, 111, 67, 60, 28, 27, 238, 221, 224, 240, 253, 47, 91, 239, 13, 179, 32, 3, + 81, 15, 195, 103, 39, 66, 162, 136, 92, 92, 14, 153, 166, 21, 71, 131, 158, 181, 10, 33, 207, + 138, 191, 11, 61, 159, 8, 59, 196, 116, 206, 32, 6, 92, 174, 186, 3, 163, 232, 46, 142, 159, + 231, 236, 199, 52, 201, 253, 94, 196, 194, 27, 170, 65, 56, 3, 235, 54, 159, 71, 74, 224, 145, + 194, 32, 7, 37, 192, 191, 55, 232, 222, 163, 133, 119, 204, 154, 2, 252, 68, 204, 127, 35, 115, + 18, 255, 101, 92, 159, 113, 38, 148, 11, 88, 14, 188, 24, 32, 1, 185, 157, 208, 201, 232, 187, + 167, 69, 41, 191, 112, 20, 178, 90, 158, 106, 103, 187, 169, 152, 209, 138, 82, 222, 48, 151, + 43, 211, 122, 139, 62, 32, 0, 82, 151, 15, 254, 101, 72, 162, 180, 25, 122, 196, 91, 240, 198, + 230, 133, 210, 171, 61, 168, 31, 207, 166, 62, 126, 137, 58, 58, 215, 89, 196, 32, 7, 243, 45, + 235, 180, 10, 147, 108, 102, 148, 41, 190, 1, 4, 246, 173, 38, 149, 22, 241, 244, 63, 151, 200, + 36, 150, 99, 141, 148, 213, 170, 198, 32, 3, 127, 184, 40, 17, 194, 24, 186, 31, 167, 103, 162, + 171, 234, 52, 191, 197, 201, 175, 217, 230, 31, 216, 93, 227, 79, 185, 7, 14, 166, 193, 138, + 32, 5, 198, 163, 129, 217, 40, 49, 109, 212, 20, 113, 205, 18, 187, 6, 2, 41, 105, 178, 32, + 112, 127, 81, 84, 120, 159, 217, 95, 160, 16, 1, 38, 32, 0, 214, 196, 180, 99, 92, 89, 104, + 164, 35, 27, 6, 129, 63, 210, 196, 204, 72, 210, 247, 189, 173, 49, 236, 125, 2, 169, 73, 236, + 99, 59, 143, 32, 6, 118, 23, 240, 120, 81, 48, 50, 12, 164, 136, 237, 115, 2, 142, 1, 195, 49, + 183, 32, 18, 172, 59, 8, 81, 8, 54, 22, 129, 174, 149, 208, 32, 5, 235, 69, 63, 4, 2, 112, 82, + 71, 244, 130, 29, 243, 60, 199, 50, 242, 167, 231, 155, 160, 192, 217, 62, 149, 60, 73, 211, + 184, 175, 27, 199, 32, 4, 149, 42, 108, 215, 143, 62, 251, 86, 44, 43, 206, 24, 25, 153, 123, + 152, 228, 253, 246, 39, 29, 119, 101, 65, 112, 255, 188, 65, 104, 178, 152, 32, 1, 11, 198, + 183, 72, 20, 94, 168, 158, 4, 35, 255, 90, 48, 115, 225, 132, 79, 198, 112, 81, 250, 150, 205, + 240, 253, 74, 111, 40, 244, 118, 132, 32, 4, 34, 127, 238, 138, 239, 252, 226, 9, 107, 68, 128, + 80, 62, 242, 72, 177, 253, 112, 233, 34, 77, 129, 230, 247, 131, 60, 168, 117, 37, 255, 47, 32, + 1, 28, 189, 19, 230, 215, 229, 105, 97, 221, 162, 156, 153, 44, 128, 199, 191, 82, 91, 140, + 143, 108, 204, 195, 58, 240, 115, 225, 147, 155, 56, 110, 32, 0, 57, 219, 152, 51, 97, 31, 231, + 165, 105, 11, 92, 33, 44, 44, 210, 231, 255, 254, 124, 211, 208, 61, 253, 82, 136, 246, 114, + 193, 58, 71, 189, 32, 7, 89, 87, 211, 70, 169, 55, 121, 167, 147, 202, 221, 211, 140, 161, 36, + 161, 129, 74, 133, 100, 113, 62, 104, 54, 249, 65, 156, 152, 203, 181, 174, 32, 7, 10, 198, 8, + 71, 203, 123, 22, 215, 252, 95, 209, 228, 106, 1, 250, 148, 203, 149, 7, 35, 101, 250, 210, + 186, 174, 64, 246, 179, 223, 66, 30, 32, 5, 242, 65, 116, 15, 209, 144, 160, 137, 244, 39, 200, + 223, 20, 127, 84, 108, 194, 233, 203, 81, 191, 36, 35, 67, 165, 197, 81, 185, 130, 37, 193, 59, + 2, 109, 191, 15, 222, 88, 251, 175, 15, 246, 130, 107, 105, 199, 118, 215, 241, 134, 76, 221, + 63, 47, 49, 81, 45, 0, 176, 60, 72, 117, 236, 249, 84, 2, 32, 3, 48, 127, 250, 48, 148, 100, + 159, 213, 218, 177, 58, 105, 139, 239, 218, 169, 185, 129, 164, 21, 88, 237, 88, 82, 191, 111, + 215, 137, 76, 223, 150, 32, 6, 16, 251, 113, 251, 35, 236, 142, 170, 88, 82, 203, 6, 0, 233, + 114, 105, 131, 238, 92, 207, 172, 58, 109, 219, 24, 246, 118, 55, 208, 230, 78, 5, 82, 112, + 121, 238, 185, 141, 1, 80, 168, 186, 34, 154, 225, 245, 139, 2, 123, 218, 190, 111, 15, 255, + 121, 41, 116, 227, 152, 42, 33, 168, 14, 1, 91, 35, 62, 84, 79, 132, 71, 170, 171, 23, 112, + 214, 190, 237, 191, 69, 250, 105, 10, 169, 108, 130, 87, 63, 201, 38, 178, 114, 139, 43, 91, + 113, 197, 141, 75, 213, 227, 174, 88, 250, 176, 106, 37, 76, 216, 107, 19, 138, 88, 156, 239, + 72, 27, 21, 218, 19, 60, 112, 156, 230, 158, 109, 190, 37, 168, 189, 205, 25, 14, 92, 102, 145, + 190, 239, 54, 179, 10, 15, 6, 103, 175, 66, 228, 134, 136, 118, 9, 64, 139, 92, 75, 55, 96, + 218, 229, 124, 227, 68, 31, 40, 194, 252, 219, 96, 27, 0, 152, 179, 217, 227, 210, 255, 77, 23, + 81, 166, 105, 218, 253, 13, 104, 220, 10, 192, 184, 18, 0, 193, 32, 1, 104, 12, 185, 205, 138, + 208, 244, 253, 33, 222, 248, 187, 128, 233, 27, 56, 12, 110, 239, 76, 137, 124, 240, 234, 232, + 103, 31, 240, 100, 98, 53, 3, 5, 6, 115, 121, 6, 85, 176, 150, 169, 85, 8, 57, 5, 144, 154, 23, + 153, 192, 202, 71, 43, 105, 225, 171, 19, 208, 87, 5, 6, 31, 239, 175, 229, 64, 33, 47, 206, 7, + 162, 188, 218, 42, 204, 87, 160, 251, 213, 62, 180, 7, 121, 99, 0, 211, 63, 30, 254, 106, 31, + 176, 41, 25, 175, 80, 100, 190, 111, 88, 75, 217, 188, 235, 191, 225, 117, 197, 67, 219, 63, + 231, 94, 69, 23, 155, 115, 39, 126, 66, 230, 232, 97, 65, 196, 47, 174, 63, 3, 102, 12, 170, + 97, 193, 165, 130, 39, 228, 52, 78, 243, 4, 108, 238, 148, 139, 4, 138, 220, 22, 151, 90, 245, + 199, 228, 126, 112, 63, 238, 140, 3, 100, 141, 244, 146, 128, 230, 70, 232, 217, 250, 154, 27, + 231, 23, 163, 61, 230, 124, 50, 221, 102, 196, 17, 93, 22, 222, 37, 204, 3, 54, 252, 116, 208, + 45, 93, 164, 82, 210, 92, 91, 63, 69, 76, 186, 185, 17, 78, 26, 238, 81, 20, 223, 109, 188, + 216, 28, 7, 139, 13, 241, 111, 119, 115, 3, 223, 5, 138, 86, 3, 251, 12, 37, 26, 26, 37, 229, + 31, 77, 213, 196, 29, 94, 132, 241, 23, 42, 110, 8, 144, 8, 241, 120, 250, 91, 227, 24, 59, + 118, 27, 166, 110, 151, 252, 42, 38, 13, 148, 178, 163, 7, 86, 243, 206, 99, 51, 45, 174, 176, + 2, 176, 222, 79, 185, 46, 239, 213, 231, 110, 203, 243, 231, 144, 68, 128, 118, 48, 62, 253, + 17, 89, 151, 143, 22, 34, 87, 213, 31, 37, 220, 227, 16, 247, 190, 214, 150, 187, 105, 125, + 102, 15, 145, 3, 159, 192, 79, 117, 81, 131, 127, 69, 145, 20, 160, 108, 209, 65, 86, 163, 214, + 220, 48, 231, 113, 225, 26, 192, 222, 215, 102, 154, 77, 26, 184, 181, 230, 178, 31, 28, 242, + 118, 216, 199, 237, 31, 119, 2, 113, 240, 112, 172, 197, 210, 98, 201, 91, 235, 196, 125, 20, + 119, 2, 25, 24, 146, 211, 241, 4, 74, 100, 162, 210, 220, 234, 124, 46, 120, 142, 236, 102, + 152, 212, 37, 114, 126, 89, 239, 119, 17, 248, 231, 171, 61, 108, 232, 175, 196, 62, 194, 69, + 13, 46, 84, 234, 17, 3, 21, 46, 232, 96, 9, 70, 128, 36, 230, 16, 165, 191, 254, 148, 41, 202, + 229, 89, 212, 233, 141, 168, 174, 229, 140, 224, 81, 97, 29, 1, 23, 2, 22, 151, 247, 233, 81, + 79, 170, 158, 249, 229, 199, 81, 89, 200, 216, 145, 38, 153, 216, 65, 34, 219, 99, 193, 248, + 36, 23, 36, 129, 51, 155, 81, 170, 123, 1, 59, 160, 203, 139, 28, 197, 58, 211, 230, 37, 31, + 85, 193, 140, 119, 108, 130, 78, 25, 191, 43, 91, 161, 3, 0, 250, 166, 193, 194, 161, 166, 73, + 214, 204, 218, 13, 80, 76, 118, 250, 79, 222, 128, 150, 252, 245, 253, 158, 102, 27, 172, 50, + 192, 131, 192, 147, 28, 23, 189, 214, 38, 116, 4, 248, 192, 87, 143, 63, 134, 25, 6, 201, 48, + 121, 23, 233, 211, 162, 185, 219, 176, 27, 21, 61, 121, 58, 228, 60, 47, 126, 131, 56, 51, 84, + 132, 13, 81, 124, 22, 98, 17, 50, 110, 109, 59, 181, 109, 154, 84, 88, 60, 188, 0, 191, 64, 46, + 229, 182, 32, 20, 2, 74, 42, 18, 235, 9, 249, 64, 116, 65, 54, 67, 1, 118, 34, 236, 213, 63, + 88, 202, 131, 114, 138, 154, 105, 29, 58, 106, 66, 201, 200, 74, 150, 27, 149, 159, 90, 213, + 12, 112, 252, 64, 244, 104, 220, 50, 128, 113, 201, 104, 20, 107, 144, 30, 58, 28, 189, 44, 89, + 107, 176, 167, 113, 52, 250, 5, 32, 2, 78, 97, 121, 127, 238, 47, 119, 199, 15, 200, 125, 16, + 150, 254, 168, 168, 59, 109, 247, 150, 38, 227, 91, 124, 212, 64, 194, 25, 127, 188, 125, 32, + 0, 113, 249, 249, 255, 228, 13, 136, 153, 122, 192, 187, 56, 42, 197, 101, 118, 59, 124, 61, + 95, 37, 92, 208, 123, 153, 43, 38, 94, 210, 169, 139, 32, 6, 167, 37, 185, 208, 123, 36, 223, + 152, 196, 150, 147, 157, 29, 252, 246, 66, 97, 248, 67, 241, 28, 215, 27, 63, 0, 229, 114, 99, + 207, 95, 21, 32, 2, 112, 59, 63, 171, 54, 166, 178, 221, 202, 101, 39, 130, 200, 103, 184, 44, + 166, 220, 246, 108, 106, 76, 54, 33, 134, 47, 117, 48, 240, 255, 173, 32, 5, 196, 143, 35, 233, + 0, 32, 54, 82, 255, 118, 12, 30, 80, 201, 187, 151, 120, 176, 215, 146, 164, 12, 194, 190, 166, + 214, 43, 224, 224, 149, 217, 5, 6, 168, 38, 220, 25, 149, 71, 84, 129, 64, 73, 73, 59, 140, 69, + 111, 67, 195, 15, 178, 147, 177, 105, 84, 186, 79, 63, 194, 33, 85, 112, 1, 213, 33, 47, 206, + 7, 162, 188, 218, 42, 204, 87, 160, 251, 213, 62, 180, 7, 121, 99, 0, 211, 63, 30, 254, 106, + 31, 176, 41, 25, 175, 80, 100, 190, 111, 88, 75, 217, 188, 235, 191, 225, 117, 197, 67, 219, + 63, 231, 94, 69, 23, 155, 115, 39, 126, 66, 230, 232, 97, 65, 196, 47, 174, 63, 3, 102, 12, + 170, 97, 193, 165, 130, 39, 228, 52, 78, 243, 4, 108, 238, 148, 139, 4, 138, 220, 22, 151, 90, + 245, 199, 228, 126, 112, 63, 238, 140, 3, 100, 141, 244, 146, 128, 230, 70, 232, 217, 250, 154, + 27, 231, 23, 163, 61, 230, 124, 50, 221, 102, 196, 17, 93, 22, 222, 37, 204, 3, 54, 252, 116, + 208, 45, 93, 164, 82, 210, 92, 91, 63, 69, 76, 186, 185, 17, 78, 26, 238, 81, 20, 223, 109, + 188, 216, 28, 7, 139, 13, 241, 111, 119, 115, 3, 223, 5, 138, 86, 3, 251, 12, 37, 26, 26, 37, + 229, 31, 77, 213, 196, 29, 94, 132, 241, 23, 42, 110, 8, 144, 8, 241, 120, 250, 91, 227, 24, + 59, 118, 27, 166, 110, 151, 252, 42, 38, 13, 148, 178, 163, 7, 86, 243, 206, 99, 51, 45, 174, + 176, 2, 176, 222, 79, 185, 46, 239, 213, 231, 110, 203, 243, 231, 144, 68, 128, 118, 48, 62, + 253, 17, 89, 151, 143, 22, 34, 87, 213, 31, 37, 220, 227, 16, 247, 190, 214, 150, 187, 105, + 125, 102, 15, 145, 3, 159, 192, 79, 117, 81, 131, 127, 69, 145, 20, 160, 108, 209, 65, 86, 163, + 214, 220, 48, 231, 113, 225, 26, 192, 222, 215, 102, 154, 77, 26, 184, 181, 230, 178, 31, 28, + 242, 118, 216, 199, 237, 31, 119, 2, 113, 240, 112, 172, 197, 210, 98, 201, 91, 235, 196, 125, + 20, 119, 2, 25, 24, 146, 211, 241, 4, 74, 100, 162, 210, 220, 234, 124, 46, 120, 142, 236, 102, + 152, 212, 37, 114, 126, 89, 239, 119, 17, 248, 231, 171, 61, 108, 232, 175, 196, 62, 194, 69, + 13, 46, 84, 234, 17, 3, 21, 46, 232, 96, 9, 70, 128, 36, 230, 16, 165, 191, 254, 148, 41, 202, + 229, 89, 212, 233, 141, 168, 174, 229, 140, 224, 81, 97, 29, 1, 23, 2, 22, 151, 247, 233, 81, + 79, 170, 158, 249, 229, 199, 81, 89, 200, 216, 145, 38, 153, 216, 65, 34, 219, 99, 193, 248, + 36, 23, 36, 129, 51, 155, 81, 170, 123, 1, 59, 160, 203, 139, 28, 197, 58, 211, 230, 37, 31, + 85, 193, 140, 119, 108, 130, 78, 25, 191, 43, 91, 161, 3, 0, 250, 166, 193, 194, 161, 166, 73, + 214, 204, 218, 13, 80, 76, 118, 250, 79, 222, 128, 150, 252, 245, 253, 158, 102, 27, 172, 50, + 192, 131, 192, 147, 28, 23, 189, 214, 38, 116, 4, 248, 192, 87, 143, 63, 134, 25, 6, 201, 48, + 121, 23, 233, 211, 162, 185, 219, 176, 27, 21, 61, 121, 58, 228, 60, 47, 126, 131, 56, 51, 84, + 132, 13, 81, 124, 22, 98, 17, 50, 110, 109, 59, 181, 109, 154, 84, 88, 60, 188, 0, 191, 64, 46, + 229, 182, 32, 20, 2, 74, 42, 18, 235, 9, 249, 64, 116, 65, 54, 67, 1, 118, 34, 236, 213, 63, + 88, 202, 131, 114, 138, 154, 105, 29, 58, 106, 66, 201, 200, 74, 150, 27, 149, 159, 90, 213, + 12, 112, 252, 64, 244, 104, 220, 50, 128, 113, 201, 104, 20, 107, 144, 30, 58, 28, 189, 44, 89, + 107, 176, 167, 113, 52, 250, 5, 32, 4, 87, 105, 19, 100, 49, 206, 121, 89, 252, 243, 252, 225, + 44, 180, 118, 113, 8, 186, 165, 2, 200, 56, 154, 176, 142, 73, 83, 118, 20, 113, 72, 32, 6, + 136, 174, 25, 103, 129, 228, 236, 152, 40, 59, 146, 249, 150, 237, 20, 81, 215, 10, 200, 139, + 59, 35, 133, 200, 43, 47, 215, 22, 184, 229, 2, 32, 6, 167, 37, 185, 208, 123, 36, 223, 152, + 196, 150, 147, 157, 29, 252, 246, 66, 97, 248, 67, 241, 28, 215, 27, 63, 0, 229, 114, 99, 207, + 95, 21, 32, 2, 112, 59, 63, 171, 54, 166, 178, 221, 202, 101, 39, 130, 200, 103, 184, 44, 166, + 220, 246, 108, 106, 76, 54, 33, 134, 47, 117, 48, 240, 255, 173, 32, 5, 196, 143, 35, 233, 0, + 32, 54, 82, 255, 118, 12, 30, 80, 201, 187, 151, 120, 176, 215, 146, 164, 12, 194, 190, 166, + 214, 43, 224, 224, 149, 217, 5, 6, 168, 38, 220, 25, 149, 71, 84, 129, 64, 73, 73, 59, 140, 69, + 111, 67, 195, 15, 178, 147, 177, 105, 84, 186, 79, 63, 194, 33, 85, 112, 1, 213, 33, 47, 206, + 7, 162, 188, 218, 42, 204, 87, 160, 251, 213, 62, 180, 7, 121, 99, 0, 211, 63, 30, 254, 106, + 31, 176, 41, 25, 175, 80, 100, 190, 111, 88, 75, 217, 188, 235, 191, 225, 117, 197, 67, 219, + 63, 231, 94, 69, 23, 155, 115, 39, 126, 66, 230, 232, 97, 65, 196, 47, 174, 63, 3, 102, 12, + 170, 97, 193, 165, 130, 39, 228, 52, 78, 243, 4, 108, 238, 148, 139, 4, 138, 220, 22, 151, 90, + 245, 199, 228, 126, 112, 63, 238, 140, 3, 100, 141, 244, 146, 128, 230, 70, 232, 217, 250, 154, + 27, 231, 23, 163, 61, 230, 124, 50, 221, 102, 196, 17, 93, 22, 222, 37, 204, 3, 54, 252, 116, + 208, 45, 93, 164, 82, 210, 92, 91, 63, 69, 76, 186, 185, 17, 78, 26, 238, 81, 20, 223, 109, + 188, 216, 28, 7, 139, 13, 241, 111, 119, 115, 3, 223, 5, 138, 86, 3, 251, 12, 37, 26, 26, 37, + 229, 31, 77, 213, 196, 29, 94, 132, 241, 23, 42, 110, 8, 144, 8, 241, 120, 250, 91, 227, 24, + 59, 118, 27, 166, 110, 151, 252, 42, 38, 13, 148, 178, 163, 7, 86, 243, 206, 99, 51, 45, 174, + 176, 2, 176, 222, 79, 185, 46, 239, 213, 231, 110, 203, 243, 231, 144, 68, 128, 118, 48, 62, + 253, 17, 89, 151, 143, 22, 34, 87, 213, 31, 37, 220, 227, 16, 247, 190, 214, 150, 187, 105, + 125, 102, 15, 145, 3, 159, 192, 79, 117, 81, 131, 127, 69, 145, 20, 160, 108, 209, 65, 86, 163, + 214, 220, 48, 231, 113, 225, 26, 192, 222, 215, 102, 154, 77, 26, 184, 181, 230, 178, 31, 28, + 242, 118, 216, 199, 237, 31, 119, 2, 113, 240, 112, 172, 197, 210, 98, 201, 91, 235, 196, 125, + 20, 119, 2, 25, 24, 146, 211, 241, 4, 74, 100, 162, 210, 220, 234, 124, 46, 120, 142, 236, 102, + 152, 212, 37, 114, 126, 89, 239, 119, 17, 248, 231, 171, 61, 108, 232, 175, 196, 62, 194, 69, + 13, 46, 84, 234, 17, 3, 21, 46, 232, 96, 9, 70, 128, 36, 230, 16, 165, 191, 254, 148, 41, 202, + 229, 89, 212, 233, 141, 168, 174, 229, 140, 224, 81, 97, 29, 1, 23, 2, 22, 151, 247, 233, 81, + 79, 170, 158, 249, 229, 199, 81, 89, 200, 216, 145, 38, 153, 216, 65, 34, 219, 99, 193, 248, + 36, 23, 36, 129, 51, 155, 81, 170, 123, 1, 59, 160, 203, 139, 28, 197, 58, 211, 230, 37, 31, + 85, 193, 140, 119, 108, 130, 78, 25, 191, 43, 91, 161, 3, 0, 250, 166, 193, 194, 161, 166, 73, + 214, 204, 218, 13, 80, 76, 118, 250, 79, 222, 128, 150, 252, 245, 253, 158, 102, 27, 172, 50, + 192, 131, 192, 147, 28, 23, 189, 214, 38, 116, 4, 248, 192, 87, 143, 63, 134, 25, 6, 201, 48, + 121, 23, 233, 211, 162, 185, 219, 176, 27, 21, 61, 121, 58, 228, 60, 47, 126, 131, 56, 51, 84, + 132, 13, 81, 124, 22, 98, 17, 50, 110, 109, 59, 181, 109, 154, 84, 88, 60, 188, 0, 191, 64, 46, + 229, 182, 32, 20, 2, 74, 42, 18, 235, 9, 249, 64, 116, 65, 54, 67, 1, 118, 34, 236, 213, 63, + 88, 202, 131, 114, 138, 154, 105, 29, 58, 106, 66, 201, 200, 74, 150, 27, 149, 159, 90, 213, + 12, 112, 252, 64, 244, 104, 220, 50, 128, 113, 201, 104, 20, 107, 144, 30, 58, 28, 189, 44, 89, + 107, 176, 167, 113, 52, 250, 5, 32, 4, 87, 105, 19, 100, 49, 206, 121, 89, 252, 243, 252, 225, + 44, 180, 118, 113, 8, 186, 165, 2, 200, 56, 154, 176, 142, 73, 83, 118, 20, 113, 72, 32, 6, + 136, 174, 25, 103, 129, 228, 236, 152, 40, 59, 146, 249, 150, 237, 20, 81, 215, 10, 200, 139, + 59, 35, 133, 200, 43, 47, 215, 22, 184, 229, 2, 32, 6, 167, 37, 185, 208, 123, 36, 223, 152, + 196, 150, 147, 157, 29, 252, 246, 66, 97, 248, 67, 241, 28, 215, 27, 63, 0, 229, 114, 99, 207, + 95, 21, 32, 2, 112, 59, 63, 171, 54, 166, 178, 221, 202, 101, 39, 130, 200, 103, 184, 44, 166, + 220, 246, 108, 106, 76, 54, 33, 134, 47, 117, 48, 240, 255, 173, 32, 5, 196, 143, 35, 233, 0, + 32, 54, 82, 255, 118, 12, 30, 80, 201, 187, 151, 120, 176, 215, 146, 164, 12, 194, 190, 166, + 214, 43, 224, 224, 149, 217, 3, 7, 126, 40, 243, 172, 167, 175, 234, 240, 174, 123, 250, 6, 48, + 63, 188, 203, 225, 130, 112, 128, 54, 46, 62, 66, 37, 235, 178, 113, 16, 113, 73, 110, 112, + 159, 196, 68, 95, 225, 214, 36, 29, 252, 221, 60, 143, 203, 240, 62, 161, 247, 182, 212, 232, + 10, 238, 65, 42, 138, 195, 155, 1, 255, 6, 12, 202, 146, 92, 237, 231, 92, 225, 161, 236, 140, + 152, 24, 136, 150, 9, 10, 132, 241, 33, 38, 195, 190, 159, 14, 216, 41, 128, 143, 188, 48, 251, + 239, 231, 102, 253, 245, 174, 171, 190, 149, 90, 248, 5, 170, 199, 101, 38, 205, 66, 24, 120, + 101, 195, 107, 199, 25, 191, 20, 195, 54, 3, 190, 149, 34, 232, 6, 174, 198, 153, 194, 67, 57, + 25, 13, 66, 10, 104, 202, 169, 248, 127, 114, 195, 29, 6, 232, 245, 21, 133, 119, 106, 167, 44, + 27, 8, 27, 237, 138, 226, 59, 242, 120, 11, 188, 127, 143, 128, 243, 155, 247, 151, 78, 29, + 103, 194, 98, 234, 250, 167, 255, 241, 154, 113, 186, 85, 108, 240, 84, 111, 163, 238, 222, 68, + 50, 217, 122, 69, 188, 177, 88, 171, 3, 59, 238, 53, 27, 39, 235, 53, 93, 13, 190, 107, 174, + 144, 238, 168, 55, 70, 208, 7, 126, 40, 243, 172, 167, 175, 234, 240, 174, 123, 250, 6, 48, 63, + 188, 203, 225, 130, 112, 128, 54, 46, 62, 66, 37, 235, 178, 113, 16, 113, 73, 110, 112, 159, + 196, 68, 95, 225, 214, 36, 29, 252, 221, 60, 143, 203, 240, 62, 161, 247, 182, 212, 232, 10, + 238, 65, 42, 138, 195, 155, 1, 255, 6, 12, 202, 146, 92, 237, 231, 92, 225, 161, 236, 140, 152, + 24, 136, 150, 9, 10, 132, 241, 33, 38, 195, 190, 159, 14, 216, 41, 128, 143, 188, 48, 251, 239, + 231, 102, 253, 245, 174, 171, 190, 149, 90, 248, 5, 170, 199, 101, 38, 205, 66, 24, 120, 101, + 195, 107, 199, 25, 191, 20, 195, 54, 3, 190, 149, 34, 232, 6, 174, 198, 153, 194, 67, 57, 25, + 13, 66, 10, 104, 202, 169, 248, 127, 114, 195, 29, 6, 232, 245, 21, 133, 119, 106, 167, 44, 27, + 8, 27, 237, 138, 226, 59, 242, 120, 11, 188, 127, 143, 128, 243, 155, 247, 151, 78, 29, 103, + 194, 98, 234, 250, 167, 255, 241, 154, 113, 186, 85, 108, 240, 84, 111, 163, 238, 222, 68, 50, + 217, 122, 69, 188, 177, 88, 171, 3, 59, 238, 53, 27, 39, 235, 53, 93, 13, 190, 107, 174, 144, + 238, 168, 55, 70, 208, 2, 32, 1, 233, 234, 212, 219, 245, 192, 123, 144, 73, 15, 40, 185, 48, + 206, 143, 33, 112, 161, 73, 157, 3, 160, 114, 210, 143, 56, 4, 8, 165, 17, 9, 32, 4, 13, 241, + 238, 159, 178, 110, 42, 17, 135, 191, 138, 114, 16, 117, 167, 46, 153, 106, 118, 27, 51, 83, + 133, 25, 221, 99, 241, 25, 41, 228, 94, 2, 32, 6, 97, 207, 224, 224, 82, 69, 83, 207, 133, 120, + 91, 161, 171, 39, 132, 249, 198, 104, 32, 25, 172, 171, 108, 226, 123, 235, 117, 51, 115, 30, + 157, 32, 4, 73, 249, 195, 252, 248, 178, 231, 139, 175, 74, 150, 185, 254, 59, 95, 235, 210, + 55, 3, 15, 131, 6, 156, 43, 249, 193, 136, 234, 229, 164, 33, 8, 209, 209, 231, 125, 217, 21, + 253, 56, 232, 148, 168, 235, 251, 35, 20, 86, 24, 135, 7, 4, 215, 13, 47, 207, 5, 74, 174, 31, + 140, 70, 195, 40, 227, 252, 46, 119, 163, 198, 19, 57, 229, 116, 112, 182, 233, 55, 59, 100, + 87, 119, 110, 130, 33, 131, 93, 13, 238, 17, 121, 187, 215, 36, 180, 136, 108, 178, 97, 88, 78, + 182, 228, 173, 17, 44, 182, 31, 7, 181, 38, 217, 220, 243, 61, 83, 119, 57, 169, 54, 207, 163, + 122, 191, 230, 12, 144, 114, 23, 142, 0, 23, 70, 116, 106, 121, 99, 128, 210, 78, 29, 19, 195, + 22, 35, 246, 10, 106, 109, 28, 115, 181, 223, 237, 182, 4, 20, 128, 109, 23, 191, 22, 144, 60, + 121, 121, 64, 190, 105, 81, 199, 185, 217, 98, 169, 242, 90, 255, 155, 197, 57, 106, 49, 93, + 166, 34, 146, 145, 252, 153, 151, 197, 90, 125, 16, 25, 41, 188, 114, 58, 89, 239, 63, 64, 105, + 4, 102, 211, 3, 43, 180, 109, 69, 210, 168, 47, 199, 3, 231, 17, 41, 201, 253, 197, 66, 155, + 36, 12, 34, 61, 169, 95, 84, 39, 35, 149, 250, 234, 76, 165, 17, 124, 40, 205, 185, 201, 27, + 71, 191, 77, 148, 228, 244, 148, 31, 91, 252, 89, 141, 6, 13, 139, 105, 158, 82, 26, 166, 148, + 184, 91, 40, 185, 45, 214, 150, 78, 230, 134, 109, 250, 24, 78, 249, 49, 197, 186, 141, 6, 8, + 118, 57, 168, 189, 244, 16, 138, 153, 83, 38, 111, 182, 217, 196, 52, 183, 62, 175, 249, 132, + 106, 145, 120, 20, 4, 174, 223, 62, 198, 85, 252, 125, 227, 252, 46, 119, 163, 198, 19, 57, + 229, 116, 112, 182, 233, 55, 59, 100, 87, 119, 110, 130, 33, 131, 93, 13, 238, 17, 121, 187, + 215, 36, 180, 136, 108, 178, 97, 88, 78, 182, 228, 173, 17, 44, 182, 31, 7, 181, 38, 217, 220, + 243, 61, 83, 119, 57, 169, 54, 207, 163, 122, 191, 230, 12, 144, 114, 23, 142, 0, 23, 70, 116, + 106, 121, 99, 128, 210, 78, 29, 19, 195, 22, 35, 246, 10, 106, 109, 28, 115, 181, 223, 237, + 182, 4, 20, 128, 109, 23, 191, 22, 144, 60, 121, 121, 64, 190, 105, 81, 199, 185, 217, 98, 169, + 242, 90, 255, 155, 197, 57, 106, 49, 93, 166, 34, 146, 145, 252, 153, 151, 197, 90, 125, 16, + 25, 41, 188, 114, 58, 89, 239, 63, 64, 105, 4, 102, 211, 3, 43, 180, 109, 69, 210, 168, 47, + 199, 3, 231, 17, 41, 201, 253, 197, 66, 155, 36, 12, 34, 61, 169, 95, 84, 39, 35, 149, 250, + 234, 76, 165, 17, 124, 40, 205, 185, 201, 27, 71, 191, 77, 148, 228, 244, 148, 31, 91, 252, 89, + 141, 6, 13, 139, 105, 158, 82, 26, 166, 148, 184, 91, 40, 185, 45, 214, 150, 78, 230, 134, 109, + 250, 24, 78, 249, 49, 197, 186, 141, 6, 36, 32, 2, 86, 79, 48, 107, 126, 61, 24, 210, 237, 10, + 182, 195, 72, 125, 211, 70, 109, 185, 250, 240, 62, 4, 11, 187, 27, 143, 175, 57, 125, 94, 153, + 32, 4, 254, 53, 26, 124, 207, 53, 62, 121, 2, 88, 132, 0, 97, 194, 182, 196, 79, 153, 8, 70, + 245, 186, 240, 75, 93, 168, 248, 19, 186, 62, 103, 32, 5, 50, 131, 105, 65, 246, 226, 154, 89, + 66, 193, 219, 26, 205, 111, 208, 58, 45, 248, 251, 187, 199, 36, 32, 48, 190, 129, 96, 236, + 255, 27, 144, 32, 2, 126, 156, 56, 125, 48, 128, 55, 103, 124, 60, 236, 170, 251, 141, 254, 20, + 36, 194, 117, 113, 163, 84, 126, 60, 85, 40, 109, 188, 104, 8, 220, 32, 3, 126, 5, 243, 19, 15, + 100, 195, 248, 111, 24, 8, 66, 31, 206, 185, 250, 157, 253, 130, 250, 212, 215, 201, 109, 114, + 17, 77, 66, 30, 0, 249, 32, 3, 154, 240, 158, 216, 219, 127, 231, 252, 171, 182, 115, 22, 217, + 9, 140, 21, 181, 156, 129, 167, 167, 123, 236, 67, 123, 163, 210, 247, 60, 136, 238, 32, 0, + 254, 121, 218, 105, 91, 132, 149, 196, 172, 197, 116, 69, 212, 111, 22, 82, 149, 200, 67, 149, + 121, 140, 92, 64, 180, 116, 100, 77, 20, 67, 188, 32, 0, 127, 60, 237, 52, 173, 194, 74, 226, + 86, 98, 186, 34, 234, 55, 139, 41, 74, 228, 33, 202, 188, 198, 46, 32, 90, 58, 50, 38, 138, 33, + 222, 32, 1, 156, 125, 144, 121, 200, 214, 175, 83, 30, 242, 25, 116, 104, 215, 93, 104, 171, + 188, 133, 4, 42, 169, 117, 153, 138, 209, 222, 202, 208, 136, 242, 32, 6, 48, 159, 5, 1, 58, + 210, 88, 153, 24, 92, 235, 226, 37, 223, 99, 137, 6, 157, 34, 152, 232, 189, 224, 149, 31, 199, + 2, 151, 144, 185, 67, 32, 5, 206, 82, 119, 10, 68, 54, 55, 225, 149, 228, 6, 217, 106, 134, + 231, 90, 87, 223, 78, 249, 157, 89, 128, 203, 239, 34, 108, 70, 123, 85, 165, 32, 2, 145, 139, + 150, 155, 51, 193, 93, 125, 14, 213, 102, 125, 153, 114, 73, 183, 178, 229, 40, 208, 144, 116, + 237, 245, 160, 115, 173, 210, 176, 87, 209, 32, 0, 240, 122, 130, 165, 93, 45, 85, 72, 6, 20, + 188, 202, 113, 217, 76, 168, 135, 171, 184, 105, 187, 5, 233, 6, 170, 40, 143, 134, 226, 174, + 124, 32, 1, 196, 212, 101, 79, 31, 79, 200, 7, 65, 116, 197, 74, 156, 94, 49, 124, 61, 169, + 100, 156, 145, 60, 76, 120, 69, 103, 98, 224, 79, 90, 242, 32, 2, 63, 73, 76, 135, 1, 157, 109, + 229, 148, 123, 31, 8, 65, 234, 176, 146, 37, 31, 38, 109, 20, 228, 132, 197, 128, 104, 119, 39, + 179, 37, 124, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 32, 6, 139, 235, 255, 24, 24, 33, 103, 13, 72, 10, 211, 192, 220, 35, 80, + 251, 155, 139, 242, 230, 194, 24, 66, 255, 43, 225, 135, 94, 251, 253, 37, 32, 1, 79, 128, 11, + 14, 135, 225, 42, 145, 219, 39, 224, 255, 116, 61, 228, 148, 0, 243, 183, 243, 22, 179, 200, + 159, 244, 195, 212, 66, 113, 253, 139, 32, 6, 17, 100, 149, 214, 84, 144, 114, 104, 213, 128, + 180, 91, 168, 174, 4, 37, 176, 108, 92, 23, 212, 235, 10, 15, 53, 13, 233, 130, 249, 20, 35, + 32, 4, 83, 50, 27, 135, 53, 115, 119, 154, 213, 92, 213, 138, 24, 203, 214, 244, 25, 56, 3, + 133, 58, 148, 25, 255, 176, 224, 26, 54, 194, 200, 244, 32, 3, 213, 66, 151, 78, 219, 91, 212, + 110, 132, 197, 221, 39, 20, 141, 26, 60, 15, 173, 189, 139, 153, 167, 63, 2, 45, 177, 227, 31, + 255, 173, 165, 32, 3, 25, 13, 20, 144, 5, 177, 125, 69, 81, 86, 129, 196, 15, 169, 59, 79, 222, + 141, 94, 70, 91, 40, 246, 28, 73, 139, 76, 41, 210, 16, 101, 32, 0, 244, 217, 143, 120, 194, + 121, 76, 168, 204, 189, 77, 177, 162, 173, 32, 32, 193, 66, 161, 249, 145, 50, 88, 94, 224, 98, + 126, 164, 57, 51, 176, 32, 3, 230, 217, 81, 157, 54, 166, 224, 134, 217, 162, 132, 88, 91, 131, + 193, 92, 81, 218, 117, 236, 137, 37, 40, 30, 62, 5, 238, 210, 215, 155, 70, 32, 1, 127, 93, + 217, 48, 96, 65, 48, 106, 224, 86, 222, 228, 237, 0, 19, 219, 123, 119, 16, 121, 138, 249, 61, + 152, 169, 100, 100, 123, 165, 9, 242, 32, 4, 179, 94, 122, 0, 72, 6, 166, 115, 176, 37, 188, + 23, 240, 245, 124, 213, 186, 40, 198, 140, 102, 214, 140, 234, 197, 214, 116, 92, 4, 185, 26, + 32, 5, 192, 108, 139, 59, 1, 125, 224, 31, 75, 203, 90, 105, 3, 226, 101, 169, 51, 208, 195, + 151, 13, 53, 47, 165, 29, 63, 153, 29, 18, 100, 92, 32, 3, 169, 50, 79, 60, 7, 21, 201, 62, + 148, 46, 219, 133, 52, 176, 160, 22, 14, 190, 134, 31, 239, 109, 107, 22, 163, 225, 143, 229, + 1, 199, 164, 32, 0, 59, 200, 71, 79, 88, 64, 45, 134, 42, 27, 29, 120, 98, 25, 117, 212, 72, + 105, 114, 223, 119, 15, 194, 52, 248, 10, 33, 47, 71, 1, 148, 32, 1, 109, 133, 168, 234, 226, + 124, 40, 101, 236, 57, 226, 175, 84, 180, 216, 255, 182, 14, 154, 216, 196, 129, 93, 45, 40, + 73, 71, 158, 224, 63, 226, 32, 5, 160, 133, 29, 43, 68, 237, 171, 35, 4, 229, 78, 49, 111, 222, + 8, 152, 238, 210, 138, 215, 80, 218, 78, 181, 130, 230, 116, 254, 171, 145, 190, 32, 4, 87, + 170, 15, 89, 149, 175, 140, 133, 140, 106, 177, 191, 73, 245, 85, 147, 10, 30, 81, 104, 19, + 158, 112, 41, 149, 142, 181, 241, 115, 122, 248, 32, 2, 255, 253, 143, 178, 10, 253, 6, 181, + 113, 154, 36, 93, 242, 165, 18, 204, 157, 0, 73, 204, 95, 28, 235, 90, 13, 128, 113, 208, 35, + 110, 163, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, 255, 254, 239, 253, 240, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 255, 225, 32, - 4, 255, 27, 106, 201, 44, 14, 220, 142, 147, 181, 147, 92, 84, 188, 236, 193, 193, 195, 15, - 241, 66, 233, 116, 141, 86, 139, 199, 167, 232, 176, 147, 32, 2, 197, 182, 174, 133, 66, 71, - 72, 229, 155, 174, 201, 229, 163, 137, 195, 85, 146, 235, 121, 247, 90, 60, 231, 57, 183, 90, - 94, 113, 154, 16, 167, 32, 2, 197, 182, 174, 133, 66, 71, 72, 229, 155, 174, 201, 229, 163, - 137, 195, 85, 146, 235, 121, 247, 90, 60, 231, 57, 183, 90, 94, 113, 154, 16, 167, 32, 2, 197, - 182, 174, 133, 66, 71, 72, 229, 155, 174, 201, 229, 163, 137, 195, 85, 146, 235, 121, 247, 90, - 60, 231, 57, 183, 90, 94, 113, 154, 16, 167, 32, 3, 216, 129, 227, 99, 217, 37, 101, 177, 159, - 4, 68, 104, 53, 177, 144, 54, 161, 196, 95, 173, 37, 172, 185, 205, 160, 225, 241, 122, 207, - 151, 44, 32, 0, 151, 196, 232, 108, 31, 41, 218, 248, 56, 28, 0, 47, 145, 195, 83, 167, 0, 21, - 140, 161, 70, 182, 209, 251, 96, 47, 189, 141, 218, 120, 174, 32, 5, 218, 121, 61, 198, 233, - 31, 227, 46, 250, 36, 19, 192, 183, 74, 37, 124, 127, 172, 117, 156, 180, 82, 186, 102, 21, - 125, 254, 176, 55, 105, 229, 32, 0, 66, 81, 241, 105, 163, 250, 188, 235, 217, 81, 99, 55, 68, - 228, 61, 18, 31, 115, 17, 87, 54, 242, 22, 7, 208, 91, 186, 253, 13, 82, 251, 32, 1, 20, 132, - 180, 228, 44, 72, 229, 247, 135, 63, 165, 202, 219, 240, 244, 38, 4, 179, 222, 189, 16, 203, - 71, 9, 61, 56, 144, 17, 201, 254, 148, 32, 3, 69, 36, 86, 208, 249, 235, 179, 140, 137, 83, - 105, 165, 45, 9, 244, 93, 233, 208, 101, 205, 74, 146, 224, 3, 68, 130, 118, 129, 169, 59, 235, - 32, 3, 152, 236, 103, 230, 27, 36, 127, 113, 95, 102, 10, 80, 209, 181, 73, 9, 23, 128, 224, - 191, 194, 50, 108, 61, 231, 122, 228, 144, 59, 71, 43, 32, 3, 195, 245, 184, 131, 104, 30, 240, - 95, 138, 107, 160, 106, 48, 167, 206, 78, 59, 49, 82, 39, 106, 178, 127, 189, 25, 122, 24, 120, - 243, 176, 13, 32, 0, 152, 202, 196, 40, 41, 108, 106, 152, 101, 92, 98, 19, 118, 51, 124, 219, - 56, 201, 74, 176, 83, 34, 80, 240, 203, 10, 75, 131, 191, 244, 163, 32, 5, 24, 61, 127, 118, - 198, 162, 174, 121, 186, 135, 217, 19, 73, 185, 250, 202, 236, 224, 162, 198, 189, 15, 209, - 208, 0, 236, 186, 53, 110, 26, 168, 32, 7, 81, 111, 212, 40, 211, 55, 185, 196, 195, 10, 132, - 152, 219, 123, 133, 140, 45, 137, 71, 137, 6, 173, 126, 159, 104, 48, 105, 217, 11, 210, 57, - 32, 7, 7, 23, 225, 223, 72, 243, 0, 67, 72, 196, 246, 186, 154, 84, 106, 101, 54, 160, 107, - 246, 183, 193, 94, 161, 187, 215, 110, 129, 161, 222, 41, 32, 1, 193, 179, 20, 81, 131, 101, - 40, 64, 234, 174, 62, 220, 34, 247, 91, 253, 32, 32, 31, 99, 21, 179, 77, 211, 24, 216, 64, 42, - 73, 11, 236, 32, 4, 226, 156, 169, 253, 81, 66, 154, 157, 21, 27, 130, 39, 154, 209, 161, 182, - 166, 76, 104, 42, 170, 28, 190, 75, 108, 17, 143, 99, 219, 97, 62, 32, 5, 162, 163, 11, 168, - 89, 215, 21, 194, 75, 44, 194, 237, 200, 232, 14, 188, 194, 230, 218, 166, 16, 136, 241, 113, - 80, 208, 73, 230, 24, 183, 52, 32, 2, 247, 255, 140, 190, 199, 123, 7, 84, 62, 190, 185, 196, - 206, 247, 124, 22, 2, 121, 168, 46, 72, 242, 82, 57, 48, 65, 29, 36, 217, 120, 251, 32, 1, 87, - 162, 118, 169, 219, 68, 21, 63, 138, 12, 138, 221, 252, 200, 37, 232, 72, 95, 17, 38, 194, 86, - 153, 252, 153, 235, 69, 58, 54, 136, 37, 32, 6, 95, 111, 8, 103, 96, 11, 148, 205, 63, 52, 120, - 237, 43, 50, 119, 39, 30, 31, 194, 113, 102, 35, 0, 113, 238, 144, 204, 22, 135, 194, 106, 32, - 3, 36, 45, 197, 165, 153, 210, 223, 245, 43, 17, 221, 176, 180, 113, 225, 58, 81, 17, 118, 64, - 178, 161, 123, 35, 181, 250, 190, 251, 71, 203, 129, 1, 253, 0, 0, 0, 0, 0, 0, 0, 64, 32, 7, - 255, 255, 255, 255, 255, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 225, 32, 7, 255, 255, 255, 255, 255, - 185, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 251, 225, 32, 7, 255, 255, 255, 255, 255, 185, 240, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 255, 225, 36, + 32, 3, 84, 27, 87, 135, 15, 57, 172, 159, 3, 225, 109, 133, 42, 61, 83, 239, 131, 242, 200, + 160, 52, 87, 0, 181, 112, 46, 15, 194, 84, 77, 79, 32, 7, 188, 29, 48, 216, 71, 33, 24, 62, 97, + 7, 201, 197, 14, 255, 184, 35, 29, 162, 189, 156, 197, 76, 147, 189, 181, 145, 0, 192, 85, 120, + 140, 32, 4, 237, 117, 1, 36, 37, 198, 171, 163, 155, 46, 237, 50, 212, 159, 123, 185, 54, 50, + 139, 170, 38, 228, 93, 132, 37, 226, 7, 36, 24, 204, 146, 32, 2, 158, 107, 51, 60, 100, 199, + 69, 151, 241, 169, 217, 121, 125, 25, 189, 125, 73, 64, 140, 76, 137, 72, 48, 58, 239, 20, 108, + 177, 1, 42, 140, 32, 1, 27, 217, 253, 111, 140, 4, 203, 241, 56, 110, 161, 128, 74, 196, 11, + 182, 220, 79, 250, 214, 161, 124, 158, 229, 30, 51, 174, 44, 107, 72, 67, 32, 4, 153, 151, 232, + 60, 26, 126, 101, 141, 56, 139, 57, 28, 134, 96, 217, 66, 136, 81, 2, 67, 126, 15, 199, 50, 12, + 76, 246, 35, 86, 44, 242, 32, 3, 246, 142, 87, 128, 141, 14, 96, 192, 152, 44, 238, 57, 1, 104, + 95, 136, 108, 78, 158, 154, 235, 95, 208, 83, 70, 201, 93, 90, 51, 242, 224, 32, 1, 251, 71, + 43, 192, 70, 135, 48, 96, 76, 22, 119, 28, 128, 180, 47, 196, 54, 39, 79, 77, 117, 175, 232, + 41, 163, 100, 174, 173, 25, 249, 112, 32, 5, 142, 12, 152, 22, 101, 75, 87, 107, 36, 221, 184, + 156, 79, 103, 107, 54, 161, 252, 217, 43, 40, 176, 171, 17, 41, 79, 98, 12, 130, 33, 166, 32, + 2, 125, 144, 17, 30, 245, 147, 128, 169, 244, 237, 54, 193, 101, 235, 179, 161, 60, 54, 105, + 18, 238, 217, 113, 216, 65, 234, 4, 58, 249, 104, 149, 32, 4, 16, 135, 217, 85, 38, 82, 172, + 64, 178, 52, 209, 103, 45, 208, 116, 226, 72, 229, 214, 237, 246, 47, 101, 18, 24, 82, 9, 149, + 110, 125, 164, 32, 7, 242, 58, 112, 247, 159, 250, 25, 111, 180, 119, 101, 221, 251, 137, 188, + 136, 187, 207, 71, 129, 154, 222, 50, 212, 194, 77, 17, 131, 85, 58, 114, 32, 7, 242, 113, 171, + 124, 230, 188, 19, 112, 125, 108, 113, 249, 196, 21, 102, 26, 213, 94, 247, 45, 8, 49, 73, 70, + 202, 194, 115, 137, 241, 153, 130, 32, 2, 233, 210, 109, 6, 113, 40, 250, 51, 212, 11, 48, 172, + 148, 235, 19, 101, 195, 78, 78, 186, 191, 218, 144, 254, 26, 71, 179, 1, 10, 188, 133, 32, 2, + 5, 82, 56, 185, 122, 156, 51, 212, 232, 216, 21, 100, 89, 130, 221, 7, 104, 144, 88, 225, 205, + 197, 255, 123, 100, 192, 228, 54, 122, 131, 48, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 3, 9, 188, 25, 165, 212, 95, 130, 7, + 21, 107, 213, 216, 158, 56, 138, 38, 198, 134, 30, 23, 245, 212, 132, 231, 211, 138, 76, 59, + 129, 73, 152, 32, 6, 216, 91, 49, 138, 90, 228, 233, 118, 58, 26, 247, 71, 55, 137, 248, 148, + 211, 218, 94, 104, 141, 163, 120, 28, 78, 243, 38, 4, 144, 100, 218, 32, 1, 213, 30, 175, 120, + 136, 190, 30, 48, 198, 93, 127, 2, 167, 209, 159, 194, 197, 187, 42, 255, 152, 225, 10, 50, + 248, 236, 97, 168, 204, 36, 185, 32, 6, 116, 40, 157, 22, 58, 92, 233, 38, 170, 101, 43, 46, + 218, 197, 93, 104, 41, 210, 10, 37, 60, 43, 79, 149, 114, 172, 29, 190, 168, 251, 199, 32, 3, + 24, 39, 128, 93, 103, 196, 5, 79, 185, 117, 7, 146, 153, 90, 254, 5, 130, 12, 153, 185, 207, + 30, 14, 152, 200, 36, 42, 150, 145, 210, 27, 32, 1, 22, 73, 41, 59, 234, 26, 5, 135, 102, 92, + 167, 184, 165, 180, 231, 236, 178, 6, 250, 91, 234, 0, 126, 175, 131, 92, 216, 45, 119, 80, + 147, 32, 0, 24, 159, 203, 4, 157, 232, 213, 23, 28, 133, 127, 211, 180, 158, 133, 47, 30, 163, + 246, 49, 145, 99, 106, 178, 121, 228, 187, 189, 10, 235, 213, 32, 4, 99, 234, 125, 221, 33, + 133, 10, 52, 208, 236, 249, 75, 84, 168, 16, 251, 127, 242, 68, 20, 178, 122, 76, 88, 145, 157, + 217, 88, 247, 115, 214, 32, 2, 163, 178, 0, 139, 184, 59, 187, 162, 214, 144, 29, 205, 51, 123, + 107, 71, 190, 65, 51, 8, 57, 146, 214, 252, 83, 154, 220, 2, 107, 132, 195, 32, 4, 245, 208, + 72, 15, 125, 89, 230, 25, 67, 1, 21, 219, 221, 174, 195, 233, 136, 44, 168, 226, 226, 232, 109, + 145, 115, 34, 246, 46, 238, 31, 9, 32, 4, 5, 199, 250, 218, 169, 198, 26, 12, 113, 75, 31, 56, + 13, 183, 227, 40, 115, 178, 77, 19, 179, 62, 10, 22, 158, 203, 154, 28, 109, 3, 3, 32, 6, 147, + 245, 50, 239, 249, 33, 78, 188, 107, 146, 100, 193, 23, 35, 66, 146, 24, 208, 159, 121, 233, + 179, 230, 34, 79, 169, 107, 66, 239, 151, 71, 32, 2, 186, 75, 75, 101, 248, 183, 192, 47, 126, + 193, 171, 154, 78, 117, 212, 136, 151, 56, 73, 34, 108, 13, 84, 224, 233, 150, 212, 9, 223, + 221, 56, 32, 2, 225, 247, 82, 0, 223, 3, 14, 142, 45, 9, 147, 121, 101, 208, 194, 33, 160, 92, + 2, 218, 29, 73, 200, 95, 100, 35, 40, 82, 31, 85, 18, 32, 4, 209, 105, 159, 89, 180, 86, 117, + 227, 33, 207, 136, 30, 228, 226, 166, 27, 117, 209, 88, 34, 122, 137, 25, 36, 106, 240, 50, + 122, 131, 86, 26, 32, 1, 156, 156, 80, 200, 107, 146, 183, 124, 114, 132, 209, 146, 60, 127, + 13, 38, 239, 122, 154, 136, 79, 36, 20, 147, 163, 108, 55, 188, 107, 1, 218, 32, 1, 215, 126, + 4, 177, 226, 207, 95, 131, 141, 176, 153, 7, 39, 6, 7, 156, 2, 94, 137, 84, 5, 222, 197, 120, + 162, 201, 13, 50, 254, 47, 147, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, 255, 254, 239, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 251, 225, 32, 7, 255, 255, 255, 255, 255, 245, 112, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 97, 32, 7, 255, 255, - 255, 255, 255, 103, 16, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 247, 1, 1, 251, 251, 127, 1, 251, 1, 128, 0, 30, 32, 7, - 255, 255, 255, 255, 255, 239, 16, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 32, 7, 255, 255, 255, 255, 255, 253, - 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 225, 32, 7, 255, 255, 255, 255, 255, 213, 144, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, - 129, 32, 1, 176, 7, 112, 1, 17, 69, 82, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 246, 254, 176, 0, 112, 0, 16, 19, 35, 32, 7, 255, 255, 255, 255, 255, 194, - 112, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 252, 97, 32, 0, 0, 0, 0, 0, 0, 21, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 64, 32, 7, 255, 255, 255, 255, 255, 249, 176, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 161, 32, 6, 111, 252, 207, 252, 208, 76, 229, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 253, 223, 111, 255, 207, 255, 208, 4, 134, 32, 7, 255, 255, 255, - 255, 255, 198, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 252, 161, 32, 0, 0, 0, 0, 0, 0, 2, 32, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 32, 7, 255, 255, 255, 255, 255, 196, 144, + 239, 255, 225, 1, 8, 143, 175, 215, 219, 185, 111, 177, 75, 73, 66, 181, 115, 149, 60, 155, 36, + 64, 201, 97, 22, 44, 5, 199, 189, 156, 70, 107, 202, 123, 120, 195, 22, 53, 201, 115, 119, 123, + 31, 250, 105, 162, 10, 120, 21, 98, 82, 118, 173, 203, 237, 10, 153, 113, 191, 66, 88, 177, 10, + 231, 230, 41, 67, 192, 249, 207, 99, 1, 50, 32, 159, 23, 4, 177, 177, 25, 222, 245, 171, 142, + 54, 95, 204, 168, 62, 52, 171, 93, 250, 1, 204, 122, 117, 10, 52, 107, 186, 208, 168, 36, 249, + 140, 75, 236, 88, 39, 14, 52, 42, 174, 36, 186, 134, 137, 48, 43, 33, 53, 149, 217, 125, 46, + 95, 174, 91, 171, 36, 50, 83, 66, 229, 202, 144, 161, 214, 44, 14, 190, 143, 160, 9, 57, 35, + 119, 113, 194, 148, 35, 100, 168, 241, 151, 40, 203, 46, 56, 120, 109, 133, 0, 249, 165, 107, + 85, 164, 80, 243, 242, 36, 235, 226, 199, 191, 219, 14, 217, 84, 59, 78, 176, 165, 80, 100, + 218, 54, 140, 125, 197, 249, 99, 247, 17, 71, 160, 180, 121, 49, 249, 172, 16, 189, 110, 38, + 119, 42, 116, 95, 138, 195, 26, 201, 251, 87, 172, 17, 108, 168, 101, 136, 252, 118, 147, 242, + 33, 11, 16, 200, 158, 124, 254, 165, 5, 73, 12, 53, 221, 137, 122, 171, 78, 203, 75, 123, 4, + 181, 50, 42, 37, 32, 13, 234, 176, 114, 190, 190, 94, 157, 8, 225, 76, 17, 77, 187, 76, 17, + 108, 216, 38, 71, 160, 110, 32, 238, 61, 148, 165, 33, 215, 48, 170, 187, 219, 64, 213, 76, + 195, 223, 39, 9, 99, 53, 201, 115, 119, 123, 31, 250, 105, 162, 10, 120, 21, 98, 82, 118, 173, + 203, 237, 10, 153, 113, 191, 66, 88, 177, 10, 231, 230, 41, 67, 192, 249, 207, 99, 1, 50, 32, + 159, 23, 4, 177, 177, 25, 222, 245, 171, 142, 54, 95, 204, 168, 62, 52, 171, 93, 250, 1, 204, + 122, 117, 10, 52, 107, 186, 208, 168, 36, 249, 140, 75, 236, 88, 39, 14, 52, 42, 174, 36, 186, + 134, 137, 48, 43, 33, 53, 149, 217, 125, 46, 95, 174, 91, 171, 36, 50, 83, 66, 229, 202, 144, + 161, 214, 44, 14, 190, 143, 160, 9, 57, 35, 119, 113, 194, 148, 35, 100, 168, 241, 151, 40, + 203, 46, 56, 120, 109, 133, 0, 249, 165, 107, 85, 164, 80, 243, 242, 36, 235, 226, 199, 191, + 219, 14, 217, 84, 59, 78, 176, 165, 80, 100, 218, 54, 140, 125, 197, 249, 99, 247, 17, 71, 160, + 180, 121, 49, 249, 172, 16, 189, 110, 38, 119, 42, 116, 95, 138, 195, 26, 201, 251, 87, 172, + 17, 108, 168, 101, 136, 252, 118, 147, 242, 33, 11, 16, 200, 158, 124, 254, 165, 5, 73, 12, 53, + 221, 137, 122, 171, 78, 203, 75, 123, 4, 181, 50, 42, 37, 32, 13, 234, 176, 114, 190, 190, 94, + 157, 23, 32, 7, 142, 147, 6, 21, 123, 33, 253, 55, 126, 197, 126, 95, 171, 57, 134, 188, 164, + 221, 62, 106, 229, 100, 218, 190, 240, 169, 120, 192, 242, 21, 214, 32, 2, 101, 51, 207, 215, + 58, 21, 130, 193, 114, 124, 220, 41, 22, 255, 94, 247, 102, 86, 120, 184, 44, 249, 175, 191, + 29, 59, 152, 233, 95, 5, 160, 32, 2, 101, 51, 207, 215, 58, 21, 130, 193, 114, 124, 220, 41, + 22, 255, 94, 247, 102, 86, 120, 184, 44, 249, 175, 191, 29, 59, 152, 233, 95, 5, 160, 32, 2, + 101, 51, 207, 215, 58, 21, 130, 193, 114, 124, 220, 41, 22, 255, 94, 247, 102, 86, 120, 184, + 44, 249, 175, 191, 29, 59, 152, 233, 95, 5, 160, 32, 5, 198, 59, 14, 129, 9, 55, 157, 229, 85, + 214, 246, 241, 224, 2, 104, 97, 11, 102, 254, 74, 31, 2, 254, 77, 240, 254, 1, 102, 112, 220, + 7, 32, 1, 149, 3, 229, 123, 254, 53, 90, 180, 12, 177, 118, 229, 25, 101, 129, 174, 72, 233, + 55, 115, 47, 64, 99, 224, 87, 203, 224, 83, 2, 144, 43, 32, 0, 79, 20, 33, 32, 162, 37, 81, + 246, 73, 132, 117, 228, 12, 110, 16, 235, 7, 30, 180, 27, 160, 14, 240, 189, 46, 91, 21, 216, + 64, 203, 90, 32, 4, 207, 119, 72, 96, 117, 72, 171, 211, 147, 31, 198, 86, 198, 33, 202, 8, 6, + 63, 191, 98, 133, 58, 151, 176, 55, 207, 50, 174, 186, 248, 239, 32, 5, 102, 223, 99, 115, 159, + 58, 4, 232, 62, 106, 40, 104, 197, 201, 116, 203, 33, 53, 13, 177, 69, 201, 180, 236, 150, 241, + 181, 218, 146, 10, 116, 32, 1, 229, 236, 16, 65, 233, 5, 25, 255, 34, 148, 148, 207, 205, 111, + 18, 183, 183, 223, 158, 88, 223, 85, 195, 89, 193, 138, 56, 116, 139, 16, 214, 32, 2, 195, 67, + 230, 197, 69, 139, 144, 214, 26, 24, 185, 48, 100, 137, 109, 31, 149, 226, 191, 153, 160, 76, + 154, 187, 218, 196, 45, 88, 165, 156, 249, 32, 4, 150, 62, 232, 148, 88, 193, 152, 250, 109, + 51, 174, 177, 44, 230, 166, 218, 76, 178, 43, 40, 205, 68, 44, 145, 166, 158, 254, 174, 149, + 36, 242, 32, 3, 14, 240, 4, 89, 157, 192, 58, 248, 159, 116, 211, 142, 150, 142, 236, 73, 182, + 90, 7, 91, 87, 7, 107, 189, 244, 179, 122, 127, 12, 35, 200, 32, 0, 102, 8, 193, 109, 36, 67, + 152, 19, 67, 42, 213, 132, 150, 154, 75, 167, 254, 116, 92, 85, 7, 210, 141, 9, 152, 66, 190, + 21, 152, 91, 153, 32, 1, 91, 26, 194, 246, 49, 88, 141, 120, 56, 129, 221, 243, 22, 225, 191, + 169, 145, 249, 173, 166, 166, 109, 88, 242, 17, 47, 51, 59, 30, 66, 1, 32, 3, 232, 219, 46, 93, + 245, 81, 62, 168, 92, 214, 120, 226, 205, 59, 177, 47, 110, 187, 138, 89, 242, 38, 118, 227, + 133, 239, 79, 143, 41, 76, 195, 32, 6, 96, 42, 9, 76, 140, 157, 64, 61, 18, 171, 162, 200, 156, + 48, 30, 116, 232, 116, 105, 54, 195, 84, 138, 241, 187, 228, 228, 188, 169, 108, 50, 32, 3, + 242, 246, 201, 183, 94, 3, 21, 82, 94, 19, 165, 232, 178, 141, 204, 58, 254, 226, 79, 253, 255, + 197, 255, 106, 177, 227, 92, 44, 3, 16, 148, 32, 7, 172, 240, 10, 110, 252, 147, 209, 62, 144, + 69, 91, 110, 89, 49, 137, 243, 236, 80, 229, 176, 61, 77, 238, 156, 26, 209, 115, 132, 240, 40, + 37, 32, 5, 56, 62, 108, 119, 202, 132, 171, 82, 235, 152, 214, 103, 223, 3, 209, 46, 5, 142, + 250, 0, 208, 197, 29, 191, 211, 7, 115, 228, 33, 108, 220, 32, 4, 215, 67, 154, 69, 154, 135, + 111, 35, 73, 247, 166, 78, 184, 82, 69, 114, 206, 159, 233, 186, 58, 99, 189, 163, 35, 51, 194, + 129, 196, 70, 175, 32, 7, 143, 126, 248, 168, 217, 88, 140, 0, 12, 61, 63, 246, 166, 185, 11, + 35, 151, 132, 157, 153, 254, 16, 148, 180, 109, 175, 72, 53, 36, 149, 68, 32, 1, 236, 79, 252, + 134, 255, 218, 253, 131, 249, 54, 44, 115, 128, 13, 110, 84, 125, 169, 171, 149, 21, 25, 25, + 43, 50, 96, 96, 240, 46, 18, 209, 23, 32, 7, 123, 254, 206, 143, 220, 186, 173, 38, 118, 168, + 213, 206, 142, 252, 212, 241, 204, 119, 97, 230, 125, 163, 44, 15, 99, 53, 38, 247, 9, 221, 5, + 32, 6, 65, 81, 64, 213, 111, 197, 141, 191, 201, 239, 87, 166, 191, 113, 153, 69, 74, 58, 63, + 119, 180, 145, 88, 240, 125, 76, 249, 230, 100, 76, 33, 32, 6, 65, 81, 64, 213, 111, 197, 141, + 191, 201, 239, 87, 166, 191, 113, 153, 69, 74, 58, 63, 119, 180, 145, 88, 240, 125, 76, 249, + 230, 100, 76, 33, 32, 6, 65, 81, 64, 213, 111, 197, 141, 191, 201, 239, 87, 166, 191, 113, 153, + 69, 74, 58, 63, 119, 180, 145, 88, 240, 125, 76, 249, 230, 100, 76, 33, 32, 5, 127, 151, 0, + 195, 85, 156, 93, 220, 124, 93, 211, 199, 213, 219, 169, 232, 163, 164, 216, 153, 69, 217, 95, + 174, 160, 27, 153, 77, 255, 143, 195, 32, 4, 116, 170, 73, 12, 229, 17, 213, 164, 3, 80, 214, + 11, 225, 8, 113, 66, 120, 217, 249, 198, 26, 209, 159, 110, 255, 6, 156, 32, 8, 242, 30, 32, 7, + 26, 124, 12, 227, 29, 202, 131, 159, 181, 209, 82, 246, 47, 231, 207, 6, 85, 19, 192, 218, 191, + 52, 31, 181, 194, 26, 87, 121, 151, 59, 70, 32, 6, 200, 217, 53, 151, 217, 208, 244, 172, 30, + 181, 142, 206, 36, 5, 51, 32, 245, 208, 31, 16, 249, 47, 43, 56, 155, 151, 35, 209, 66, 61, + 201, 32, 6, 16, 20, 202, 32, 119, 52, 245, 191, 147, 160, 244, 114, 155, 169, 23, 156, 26, 161, + 0, 166, 196, 161, 182, 62, 6, 131, 246, 243, 86, 39, 221, 32, 5, 5, 163, 229, 128, 60, 138, + 169, 223, 143, 34, 170, 203, 110, 41, 108, 51, 90, 243, 113, 20, 215, 31, 32, 53, 113, 150, 0, + 195, 229, 198, 180, 32, 6, 44, 176, 222, 123, 156, 157, 231, 226, 48, 148, 190, 187, 175, 32, + 100, 111, 48, 114, 173, 212, 193, 168, 158, 31, 119, 154, 13, 145, 47, 219, 154, 32, 2, 113, + 130, 114, 142, 187, 124, 33, 124, 16, 20, 163, 7, 135, 128, 195, 232, 186, 201, 37, 31, 180, + 151, 145, 61, 119, 163, 98, 230, 222, 255, 131, 32, 4, 186, 228, 154, 64, 145, 17, 193, 44, + 173, 111, 162, 13, 97, 122, 140, 184, 126, 230, 252, 139, 174, 186, 116, 229, 185, 81, 167, + 227, 172, 78, 7, 32, 7, 240, 155, 140, 34, 182, 40, 232, 244, 88, 158, 211, 201, 54, 132, 55, + 150, 207, 180, 66, 185, 128, 54, 145, 39, 226, 7, 188, 8, 191, 147, 74, 32, 6, 134, 39, 224, + 177, 50, 236, 236, 153, 60, 185, 186, 40, 212, 76, 252, 143, 184, 252, 118, 71, 93, 162, 133, + 66, 219, 167, 72, 9, 242, 255, 32, 32, 1, 175, 41, 43, 235, 241, 178, 150, 134, 241, 2, 209, + 204, 164, 137, 113, 245, 194, 181, 3, 76, 210, 255, 165, 17, 108, 25, 255, 198, 149, 65, 57, + 32, 1, 37, 226, 94, 6, 217, 61, 207, 85, 133, 227, 150, 102, 59, 43, 254, 164, 172, 211, 111, + 135, 239, 44, 158, 9, 121, 176, 242, 38, 78, 211, 38, 32, 0, 241, 143, 25, 238, 182, 108, 169, + 79, 193, 187, 243, 204, 195, 168, 56, 11, 79, 106, 139, 211, 183, 216, 46, 199, 2, 17, 104, + 196, 30, 159, 3, 32, 7, 49, 117, 239, 61, 103, 243, 249, 84, 100, 93, 128, 212, 230, 191, 75, + 204, 66, 228, 160, 172, 95, 235, 108, 61, 50, 101, 122, 183, 172, 29, 135, 32, 1, 137, 98, 241, + 24, 90, 195, 105, 148, 86, 253, 61, 129, 148, 255, 117, 38, 9, 72, 11, 57, 196, 49, 184, 45, + 195, 139, 134, 127, 128, 122, 164, 32, 2, 51, 119, 88, 85, 182, 72, 99, 157, 75, 108, 161, 111, + 186, 148, 194, 23, 244, 174, 92, 16, 6, 2, 7, 195, 130, 59, 168, 128, 140, 224, 155, 32, 3, 32, + 246, 95, 74, 197, 162, 195, 50, 53, 214, 192, 20, 143, 82, 58, 124, 45, 255, 158, 204, 214, 62, + 53, 60, 204, 135, 216, 177, 141, 72, 119, 32, 1, 207, 226, 59, 11, 86, 156, 112, 88, 167, 104, + 228, 76, 20, 89, 3, 136, 180, 95, 81, 151, 169, 88, 234, 196, 64, 171, 110, 94, 18, 237, 93, 7, + 223, 176, 137, 56, 34, 20, 220, 117, 204, 42, 101, 37, 254, 127, 159, 184, 45, 146, 224, 216, + 213, 171, 14, 47, 51, 242, 104, 53, 159, 183, 189, 142, 20, 35, 156, 90, 14, 139, 81, 151, 85, + 223, 66, 250, 192, 71, 233, 46, 70, 17, 154, 249, 67, 242, 43, 218, 37, 219, 8, 233, 176, 45, + 219, 183, 202, 146, 92, 237, 231, 92, 225, 161, 236, 140, 152, 24, 136, 150, 9, 10, 132, 241, + 33, 38, 195, 190, 159, 14, 216, 41, 128, 143, 188, 48, 251, 239, 231, 102, 253, 245, 174, 171, + 190, 149, 90, 248, 5, 170, 199, 101, 38, 205, 66, 24, 120, 101, 195, 107, 199, 25, 191, 20, + 195, 54, 3, 190, 149, 34, 232, 6, 174, 198, 153, 194, 67, 57, 25, 13, 66, 10, 104, 202, 169, + 248, 127, 114, 195, 29, 6, 232, 245, 21, 133, 119, 106, 167, 44, 27, 8, 27, 237, 138, 226, 59, + 242, 120, 11, 188, 127, 143, 128, 243, 155, 247, 151, 78, 29, 103, 194, 98, 234, 250, 167, 255, + 241, 154, 113, 186, 85, 108, 240, 84, 111, 163, 238, 222, 68, 50, 217, 122, 69, 188, 177, 88, + 171, 3, 59, 238, 53, 27, 39, 235, 53, 93, 13, 190, 107, 174, 144, 238, 168, 55, 70, 208, 7, + 223, 176, 137, 56, 34, 20, 220, 117, 204, 42, 101, 37, 254, 127, 159, 184, 45, 146, 224, 216, + 213, 171, 14, 47, 51, 242, 104, 53, 159, 183, 189, 142, 20, 35, 156, 90, 14, 139, 81, 151, 85, + 223, 66, 250, 192, 71, 233, 46, 70, 17, 154, 249, 67, 242, 43, 218, 37, 219, 8, 233, 176, 45, + 219, 183, 202, 146, 92, 237, 231, 92, 225, 161, 236, 140, 152, 24, 136, 150, 9, 10, 132, 241, + 33, 38, 195, 190, 159, 14, 216, 41, 128, 143, 188, 48, 251, 239, 231, 102, 253, 245, 174, 171, + 190, 149, 90, 248, 5, 170, 199, 101, 38, 205, 66, 24, 120, 101, 195, 107, 199, 25, 191, 20, + 195, 54, 3, 190, 149, 34, 232, 6, 174, 198, 153, 194, 67, 57, 25, 13, 66, 10, 104, 202, 169, + 248, 127, 114, 195, 29, 6, 232, 245, 21, 133, 119, 106, 167, 44, 27, 8, 27, 237, 138, 226, 59, + 242, 120, 11, 188, 127, 143, 128, 243, 155, 247, 151, 78, 29, 103, 194, 98, 234, 250, 167, 255, + 241, 154, 113, 186, 85, 108, 240, 84, 111, 163, 238, 222, 68, 50, 217, 122, 69, 188, 177, 88, + 171, 3, 59, 238, 53, 27, 39, 235, 53, 93, 13, 190, 107, 174, 144, 238, 168, 55, 70, 208, 2, 32, + 3, 217, 169, 177, 240, 4, 196, 128, 119, 155, 197, 53, 156, 199, 129, 214, 199, 115, 49, 99, + 221, 93, 178, 149, 239, 66, 87, 206, 118, 3, 14, 60, 32, 6, 87, 144, 100, 92, 84, 221, 234, 68, + 28, 139, 255, 104, 182, 74, 187, 18, 52, 203, 153, 252, 214, 202, 207, 140, 188, 228, 92, 76, + 181, 68, 146, 2, 32, 7, 72, 105, 187, 61, 213, 88, 1, 200, 104, 118, 116, 175, 244, 180, 112, + 101, 129, 142, 225, 30, 53, 245, 14, 34, 102, 171, 248, 86, 76, 249, 145, 32, 0, 156, 162, 126, + 22, 103, 4, 195, 229, 54, 125, 189, 196, 218, 227, 45, 130, 192, 214, 57, 20, 131, 93, 239, + 121, 38, 1, 128, 14, 153, 174, 205, 8, 44, 48, 189, 171, 247, 67, 41, 148, 159, 94, 72, 103, + 197, 198, 82, 66, 242, 249, 236, 141, 6, 123, 74, 227, 249, 206, 124, 47, 6, 104, 38, 230, 94, + 67, 253, 217, 249, 92, 206, 177, 100, 149, 47, 9, 185, 213, 11, 122, 221, 185, 44, 217, 47, 88, + 67, 78, 150, 36, 16, 37, 108, 107, 8, 115, 164, 164, 228, 237, 174, 82, 103, 190, 174, 187, 38, + 84, 24, 36, 133, 6, 191, 42, 16, 136, 195, 125, 107, 122, 215, 96, 219, 72, 76, 145, 38, 227, + 23, 142, 0, 23, 70, 116, 106, 121, 99, 128, 210, 78, 29, 19, 195, 22, 35, 246, 10, 106, 109, + 28, 115, 181, 223, 237, 182, 4, 20, 128, 109, 23, 191, 22, 144, 60, 121, 121, 64, 190, 105, 81, + 199, 185, 217, 98, 169, 242, 90, 255, 155, 197, 57, 106, 49, 93, 166, 34, 146, 145, 252, 153, + 151, 197, 90, 125, 16, 25, 41, 188, 114, 58, 89, 239, 63, 64, 105, 4, 102, 211, 3, 43, 180, + 109, 69, 210, 168, 47, 199, 3, 231, 17, 41, 201, 253, 197, 66, 155, 36, 12, 34, 61, 169, 95, + 84, 39, 35, 149, 250, 234, 76, 165, 17, 124, 40, 205, 185, 201, 27, 71, 191, 77, 148, 228, 244, + 148, 31, 91, 252, 89, 141, 6, 13, 139, 105, 158, 82, 26, 166, 148, 184, 91, 40, 185, 45, 214, + 150, 78, 230, 134, 109, 250, 24, 78, 249, 49, 197, 186, 141, 6, 8, 6, 66, 38, 16, 51, 119, 153, + 33, 189, 204, 188, 70, 40, 192, 179, 242, 238, 41, 48, 183, 178, 43, 146, 114, 227, 246, 152, + 194, 231, 155, 121, 149, 94, 67, 253, 217, 249, 92, 206, 177, 100, 149, 47, 9, 185, 213, 11, + 122, 221, 185, 44, 217, 47, 88, 67, 78, 150, 36, 16, 37, 108, 107, 8, 115, 164, 164, 228, 237, + 174, 82, 103, 190, 174, 187, 38, 84, 24, 36, 133, 6, 191, 42, 16, 136, 195, 125, 107, 122, 215, + 96, 219, 72, 76, 145, 38, 227, 23, 142, 0, 23, 70, 116, 106, 121, 99, 128, 210, 78, 29, 19, + 195, 22, 35, 246, 10, 106, 109, 28, 115, 181, 223, 237, 182, 4, 20, 128, 109, 23, 191, 22, 144, + 60, 121, 121, 64, 190, 105, 81, 199, 185, 217, 98, 169, 242, 90, 255, 155, 197, 57, 106, 49, + 93, 166, 34, 146, 145, 252, 153, 151, 197, 90, 125, 16, 25, 41, 188, 114, 58, 89, 239, 63, 64, + 105, 4, 102, 211, 3, 43, 180, 109, 69, 210, 168, 47, 199, 3, 231, 17, 41, 201, 253, 197, 66, + 155, 36, 12, 34, 61, 169, 95, 84, 39, 35, 149, 250, 234, 76, 165, 17, 124, 40, 205, 185, 201, + 27, 71, 191, 77, 148, 228, 244, 148, 31, 91, 252, 89, 141, 6, 13, 139, 105, 158, 82, 26, 166, + 148, 184, 91, 40, 185, 45, 214, 150, 78, 230, 134, 109, 250, 24, 78, 249, 49, 197, 186, 141, 6, + 36, 32, 3, 93, 72, 164, 72, 101, 137, 91, 50, 183, 167, 161, 235, 151, 19, 164, 69, 67, 138, + 192, 137, 97, 219, 32, 20, 126, 228, 1, 243, 84, 45, 40, 32, 0, 158, 110, 8, 12, 109, 214, 114, + 60, 23, 15, 171, 234, 87, 10, 24, 85, 255, 37, 163, 17, 166, 2, 189, 34, 250, 167, 64, 199, + 231, 68, 55, 32, 5, 12, 1, 165, 225, 71, 142, 63, 199, 27, 214, 62, 4, 7, 215, 119, 29, 204, + 176, 115, 44, 72, 45, 87, 253, 79, 104, 116, 220, 180, 99, 147, 32, 4, 209, 23, 205, 180, 18, + 18, 5, 133, 190, 98, 214, 252, 241, 64, 183, 234, 66, 9, 110, 133, 133, 252, 73, 122, 227, 44, + 221, 238, 127, 5, 190, 32, 0, 205, 182, 86, 228, 119, 212, 109, 157, 248, 48, 151, 3, 27, 143, + 210, 167, 197, 226, 35, 215, 173, 105, 126, 98, 90, 144, 169, 200, 216, 4, 27, 32, 7, 182, 153, + 192, 164, 94, 213, 7, 81, 178, 161, 104, 1, 253, 131, 118, 69, 226, 98, 112, 103, 138, 99, 200, + 16, 8, 213, 118, 146, 174, 172, 238, 32, 3, 138, 128, 166, 158, 66, 88, 106, 238, 115, 17, 236, + 1, 17, 18, 205, 217, 56, 172, 81, 37, 107, 16, 246, 41, 64, 227, 56, 191, 103, 249, 87, 32, 5, + 197, 64, 83, 79, 33, 44, 61, 247, 57, 136, 246, 0, 136, 137, 102, 236, 156, 86, 40, 146, 181, + 136, 123, 20, 160, 113, 156, 95, 179, 252, 172, 32, 6, 97, 73, 120, 25, 217, 174, 109, 140, + 223, 119, 82, 47, 54, 201, 127, 136, 196, 162, 77, 83, 117, 2, 151, 62, 68, 58, 246, 118, 115, + 38, 101, 32, 2, 157, 38, 116, 98, 175, 2, 6, 138, 30, 94, 136, 19, 148, 154, 76, 125, 244, 11, + 40, 93, 81, 83, 119, 116, 203, 167, 224, 50, 191, 159, 159, 32, 0, 175, 156, 11, 75, 255, 239, + 88, 118, 72, 86, 193, 185, 17, 52, 234, 231, 153, 47, 173, 77, 247, 77, 216, 57, 191, 20, 5, + 200, 213, 74, 117, 32, 0, 62, 153, 65, 208, 118, 182, 23, 174, 164, 155, 159, 232, 5, 38, 51, + 63, 181, 127, 35, 243, 123, 219, 125, 192, 209, 221, 203, 76, 82, 98, 86, 32, 5, 22, 232, 170, + 86, 80, 89, 25, 218, 109, 61, 10, 143, 104, 0, 127, 160, 66, 115, 104, 106, 128, 168, 41, 111, + 29, 36, 98, 131, 0, 67, 246, 32, 1, 206, 169, 179, 80, 23, 138, 158, 196, 38, 80, 29, 56, 215, + 173, 212, 221, 84, 28, 18, 145, 203, 40, 27, 75, 188, 125, 92, 200, 191, 96, 148, 32, 4, 101, + 254, 40, 26, 84, 221, 157, 243, 85, 218, 229, 203, 94, 91, 182, 129, 32, 133, 66, 82, 255, 210, + 103, 89, 210, 64, 214, 170, 248, 216, 89, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 93, 83, 13, 39, 225, 51, 125, 213, 246, + 158, 232, 243, 193, 188, 211, 178, 58, 244, 128, 89, 225, 155, 190, 28, 228, 120, 50, 132, 29, + 234, 244, 32, 7, 219, 227, 185, 248, 229, 236, 137, 91, 50, 171, 67, 42, 191, 128, 161, 154, + 154, 107, 199, 194, 156, 33, 2, 143, 255, 67, 51, 24, 104, 133, 14, 32, 5, 146, 19, 155, 160, + 141, 85, 232, 35, 7, 21, 242, 138, 101, 227, 115, 172, 245, 197, 2, 96, 173, 89, 196, 94, 161, + 149, 172, 204, 213, 77, 241, 32, 7, 249, 80, 4, 89, 1, 199, 167, 27, 139, 62, 219, 86, 89, 72, + 123, 115, 154, 224, 30, 134, 235, 116, 44, 8, 144, 129, 32, 105, 174, 180, 51, 32, 3, 109, 79, + 177, 19, 59, 134, 178, 84, 88, 166, 192, 234, 24, 132, 50, 9, 52, 190, 175, 103, 0, 114, 130, + 129, 252, 85, 105, 80, 13, 215, 44, 32, 0, 65, 132, 220, 157, 57, 141, 161, 237, 95, 246, 72, + 187, 213, 187, 214, 26, 96, 82, 179, 52, 229, 229, 37, 214, 245, 159, 218, 49, 194, 193, 152, + 32, 3, 183, 108, 42, 159, 80, 215, 118, 17, 80, 122, 97, 61, 97, 29, 254, 39, 135, 228, 135, + 94, 107, 106, 221, 63, 255, 38, 138, 6, 196, 241, 139, 32, 4, 138, 187, 53, 147, 230, 88, 117, + 192, 126, 19, 155, 155, 156, 127, 50, 196, 43, 177, 64, 239, 117, 169, 171, 254, 53, 246, 1, 9, + 12, 34, 196, 32, 4, 160, 6, 194, 185, 109, 38, 218, 213, 228, 88, 0, 31, 246, 241, 176, 177, + 119, 122, 108, 5, 184, 189, 59, 198, 144, 139, 221, 103, 10, 165, 2, 32, 2, 111, 100, 250, 248, + 222, 100, 64, 48, 166, 146, 122, 2, 8, 68, 252, 92, 143, 51, 116, 45, 110, 98, 100, 44, 170, + 181, 38, 109, 65, 195, 204, 32, 1, 20, 223, 54, 185, 95, 217, 122, 110, 8, 220, 187, 76, 74, + 65, 22, 142, 193, 220, 144, 118, 178, 193, 68, 147, 34, 251, 2, 23, 86, 59, 84, 32, 4, 11, 211, + 18, 40, 27, 51, 227, 51, 84, 194, 0, 1, 238, 114, 180, 172, 52, 106, 108, 246, 211, 240, 67, + 31, 136, 195, 86, 130, 203, 152, 13, 32, 3, 59, 216, 203, 252, 156, 121, 46, 5, 129, 98, 56, + 105, 47, 215, 70, 255, 253, 194, 22, 92, 185, 162, 24, 189, 240, 19, 167, 129, 51, 118, 15, 32, + 6, 218, 40, 8, 230, 94, 223, 24, 55, 176, 253, 83, 23, 76, 163, 40, 55, 166, 131, 30, 122, 192, + 213, 155, 114, 135, 8, 36, 157, 222, 29, 33, 32, 7, 158, 22, 33, 155, 10, 170, 45, 59, 39, 103, + 96, 105, 160, 76, 244, 221, 24, 125, 27, 163, 37, 121, 209, 200, 87, 71, 74, 128, 187, 192, 77, + 32, 1, 233, 235, 60, 224, 219, 201, 97, 98, 250, 177, 160, 110, 194, 21, 88, 29, 170, 204, 12, + 223, 187, 86, 195, 43, 127, 188, 63, 33, 176, 119, 155, 32, 7, 144, 93, 56, 141, 120, 249, 210, + 19, 205, 141, 91, 105, 178, 112, 63, 196, 79, 204, 39, 17, 230, 127, 8, 93, 82, 101, 30, 208, + 100, 221, 55, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, 255, 254, 239, 253, 240, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 255, 225, 36, + 32, 4, 198, 99, 47, 102, 150, 146, 77, 205, 39, 228, 73, 208, 170, 131, 91, 229, 149, 154, 108, + 150, 155, 148, 154, 250, 98, 98, 240, 61, 154, 247, 24, 32, 4, 94, 5, 186, 86, 10, 188, 71, 33, + 154, 51, 118, 173, 32, 170, 225, 114, 150, 144, 214, 197, 235, 56, 211, 75, 119, 174, 112, 233, + 134, 174, 246, 32, 6, 214, 144, 50, 68, 104, 213, 16, 190, 137, 200, 55, 159, 110, 39, 163, 41, + 71, 7, 35, 94, 3, 92, 153, 218, 69, 52, 27, 30, 199, 213, 181, 32, 0, 116, 127, 190, 3, 87, + 233, 226, 70, 40, 211, 143, 245, 140, 24, 29, 26, 136, 122, 149, 187, 102, 54, 132, 255, 0, + 112, 16, 195, 254, 74, 70, 32, 3, 133, 116, 230, 181, 99, 254, 244, 122, 42, 252, 158, 208, + 173, 141, 214, 205, 60, 187, 213, 75, 112, 84, 154, 121, 184, 79, 230, 137, 204, 151, 160, 32, + 5, 110, 77, 198, 197, 214, 119, 53, 127, 26, 252, 4, 108, 154, 64, 215, 160, 192, 232, 92, 43, + 150, 104, 189, 48, 198, 58, 17, 232, 126, 121, 248, 32, 0, 96, 162, 110, 38, 151, 101, 178, 95, + 20, 2, 138, 205, 173, 58, 137, 128, 172, 7, 37, 18, 32, 29, 212, 234, 147, 6, 25, 14, 62, 61, + 42, 32, 0, 48, 81, 55, 19, 75, 178, 217, 47, 138, 1, 69, 102, 214, 157, 68, 192, 86, 3, 146, + 137, 16, 14, 234, 117, 73, 131, 12, 135, 31, 30, 149, 32, 0, 70, 215, 101, 166, 112, 230, 215, + 64, 31, 158, 62, 130, 47, 190, 212, 203, 151, 94, 13, 91, 191, 102, 229, 251, 161, 134, 218, + 86, 116, 251, 118, 32, 7, 148, 42, 133, 153, 156, 222, 114, 121, 161, 47, 246, 4, 104, 253, 82, + 143, 222, 175, 136, 17, 84, 9, 185, 73, 24, 157, 55, 77, 203, 206, 170, 32, 6, 95, 234, 173, + 56, 114, 7, 142, 82, 41, 132, 108, 170, 143, 82, 105, 193, 167, 44, 204, 178, 1, 244, 42, 127, + 140, 103, 23, 202, 169, 243, 42, 32, 2, 77, 249, 28, 214, 193, 240, 93, 31, 25, 106, 39, 150, + 196, 222, 90, 225, 108, 105, 128, 180, 141, 3, 101, 242, 62, 156, 7, 172, 206, 229, 231, 32, 3, + 46, 149, 238, 10, 122, 151, 202, 36, 91, 125, 53, 54, 22, 155, 163, 176, 198, 51, 163, 149, + 187, 117, 74, 51, 8, 165, 38, 14, 24, 245, 40, 32, 4, 239, 189, 161, 235, 217, 214, 24, 228, + 113, 16, 30, 82, 45, 123, 159, 104, 103, 91, 25, 207, 207, 250, 116, 228, 250, 245, 176, 93, 7, + 252, 107, 32, 2, 166, 141, 155, 18, 183, 248, 119, 26, 147, 37, 170, 247, 219, 46, 2, 31, 160, + 9, 208, 255, 31, 92, 171, 51, 122, 64, 44, 65, 105, 106, 97, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 2, 156, 46, 34, 115, 162, + 75, 166, 181, 116, 156, 220, 60, 49, 126, 243, 206, 46, 132, 61, 180, 42, 193, 57, 105, 171, + 49, 92, 216, 164, 255, 63, 32, 2, 111, 35, 213, 86, 255, 227, 39, 70, 26, 138, 73, 140, 112, + 19, 17, 180, 33, 56, 2, 82, 70, 96, 99, 52, 178, 58, 41, 184, 87, 179, 113, 32, 3, 147, 237, + 53, 138, 141, 166, 16, 37, 177, 164, 176, 191, 57, 129, 184, 47, 249, 237, 203, 85, 179, 192, + 68, 69, 59, 199, 14, 83, 181, 238, 61, 32, 1, 118, 29, 91, 8, 184, 237, 60, 139, 126, 142, 141, + 59, 114, 71, 146, 78, 93, 78, 242, 123, 222, 219, 252, 43, 225, 140, 28, 230, 172, 188, 58, 32, + 1, 84, 130, 32, 213, 78, 79, 117, 206, 162, 1, 8, 224, 147, 200, 238, 60, 27, 30, 226, 8, 3, + 71, 210, 50, 243, 97, 91, 91, 212, 126, 174, 32, 0, 224, 32, 20, 176, 200, 99, 47, 72, 141, 39, + 151, 121, 176, 140, 240, 31, 227, 173, 200, 71, 167, 168, 90, 97, 117, 3, 120, 21, 185, 211, + 224, 32, 1, 37, 68, 6, 241, 202, 81, 117, 47, 144, 18, 225, 165, 193, 17, 181, 203, 96, 21, + 248, 79, 44, 199, 18, 158, 228, 197, 98, 156, 192, 94, 80, 32, 5, 185, 214, 73, 108, 240, 134, + 9, 148, 89, 43, 205, 61, 124, 125, 98, 76, 85, 5, 182, 185, 175, 212, 9, 48, 201, 89, 45, 183, + 104, 130, 89, 32, 4, 230, 12, 106, 169, 13, 106, 222, 118, 104, 250, 215, 212, 230, 34, 131, 8, + 142, 71, 207, 35, 197, 114, 84, 3, 18, 90, 18, 92, 190, 198, 231, 32, 6, 129, 116, 116, 248, + 215, 104, 164, 63, 123, 158, 128, 206, 108, 34, 113, 238, 69, 104, 59, 68, 244, 166, 220, 58, + 209, 172, 56, 49, 246, 59, 251, 32, 5, 86, 230, 229, 219, 163, 126, 239, 151, 183, 79, 219, + 168, 206, 75, 82, 196, 130, 131, 63, 170, 158, 100, 118, 233, 237, 114, 173, 176, 238, 108, + 217, 32, 7, 244, 155, 40, 219, 168, 42, 187, 215, 244, 185, 179, 251, 71, 122, 223, 29, 143, + 140, 84, 83, 0, 21, 74, 227, 57, 248, 25, 153, 112, 255, 181, 32, 7, 207, 113, 72, 180, 24, + 231, 110, 224, 195, 157, 3, 64, 86, 79, 206, 101, 110, 184, 221, 7, 149, 11, 254, 197, 182, + 100, 80, 245, 58, 63, 147, 32, 5, 221, 109, 132, 218, 56, 223, 127, 0, 226, 252, 251, 156, 5, + 76, 45, 87, 250, 6, 120, 225, 77, 119, 106, 247, 192, 187, 108, 242, 39, 4, 167, 32, 5, 115, + 229, 56, 54, 6, 240, 24, 242, 136, 101, 92, 116, 84, 44, 239, 43, 133, 203, 167, 3, 81, 139, 9, + 192, 77, 237, 242, 251, 5, 68, 235, 32, 4, 114, 254, 69, 61, 206, 97, 32, 57, 35, 33, 228, 65, + 18, 161, 248, 118, 151, 197, 161, 100, 84, 220, 9, 162, 222, 214, 19, 5, 249, 5, 5, 32, 6, 72, + 194, 21, 30, 130, 163, 152, 41, 83, 40, 242, 19, 88, 251, 153, 240, 87, 52, 164, 156, 51, 138, + 64, 89, 145, 227, 141, 228, 120, 119, 186, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, 255, 254, 239, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 252, 129, 32, 6, 111, 252, 207, 252, 208, 76, 229, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 223, 111, 255, 207, 255, 208, 4, 134, 32, 7, - 255, 255, 255, 255, 255, 219, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 225, 32, 7, 144, 1, 16, 1, 16, 151, 69, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 238, 144, 0, - 16, 0, 16, 8, 230, 32, 7, 255, 255, 255, 255, 255, 202, 240, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 252, 225, 32, 0, - 176, 3, 48, 1, 17, 69, 116, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 246, 253, 176, 0, 48, 0, 16, 19, 37, 32, 7, 255, 255, 255, 255, 255, 245, 112, 255, + 255, 255, 239, 255, 225, 1, 8, 198, 23, 84, 255, 151, 170, 24, 10, 205, 81, 45, 252, 78, 227, + 104, 43, 34, 71, 48, 21, 221, 59, 83, 55, 230, 173, 205, 23, 165, 63, 164, 81, 214, 5, 201, + 144, 116, 141, 237, 183, 99, 166, 133, 98, 237, 150, 237, 117, 64, 62, 158, 103, 219, 38, 180, + 159, 216, 51, 118, 132, 190, 36, 174, 82, 184, 132, 162, 205, 221, 202, 214, 145, 217, 167, 46, + 204, 125, 71, 164, 57, 104, 83, 60, 227, 147, 221, 50, 19, 127, 168, 46, 215, 74, 229, 109, + 113, 208, 168, 36, 249, 140, 75, 236, 88, 39, 14, 52, 42, 174, 36, 186, 134, 137, 48, 43, 33, + 53, 149, 217, 125, 46, 95, 174, 91, 171, 36, 50, 83, 66, 229, 202, 144, 161, 214, 44, 14, 190, + 143, 160, 9, 57, 35, 119, 113, 194, 148, 35, 100, 168, 241, 151, 40, 203, 46, 56, 120, 109, + 133, 0, 249, 165, 107, 85, 164, 80, 243, 242, 36, 235, 226, 199, 191, 219, 14, 217, 84, 59, 78, + 176, 165, 80, 100, 218, 54, 140, 125, 197, 249, 99, 247, 17, 71, 160, 180, 121, 49, 249, 172, + 16, 189, 110, 38, 119, 42, 116, 95, 138, 195, 26, 201, 251, 87, 172, 17, 108, 168, 101, 136, + 252, 118, 147, 242, 33, 11, 16, 200, 158, 124, 254, 165, 5, 73, 12, 53, 221, 137, 122, 171, 78, + 203, 75, 123, 4, 181, 50, 42, 37, 32, 13, 234, 176, 114, 190, 190, 94, 157, 8, 170, 178, 81, + 208, 81, 111, 31, 28, 126, 253, 70, 188, 190, 185, 39, 217, 50, 111, 70, 67, 107, 101, 179, 74, + 138, 102, 107, 71, 148, 178, 73, 169, 214, 5, 201, 144, 116, 141, 237, 183, 99, 166, 133, 98, + 237, 150, 237, 117, 64, 62, 158, 103, 219, 38, 180, 159, 216, 51, 118, 132, 190, 36, 174, 82, + 184, 132, 162, 205, 221, 202, 214, 145, 217, 167, 46, 204, 125, 71, 164, 57, 104, 83, 60, 227, + 147, 221, 50, 19, 127, 168, 46, 215, 74, 229, 109, 113, 208, 168, 36, 249, 140, 75, 236, 88, + 39, 14, 52, 42, 174, 36, 186, 134, 137, 48, 43, 33, 53, 149, 217, 125, 46, 95, 174, 91, 171, + 36, 50, 83, 66, 229, 202, 144, 161, 214, 44, 14, 190, 143, 160, 9, 57, 35, 119, 113, 194, 148, + 35, 100, 168, 241, 151, 40, 203, 46, 56, 120, 109, 133, 0, 249, 165, 107, 85, 164, 80, 243, + 242, 36, 235, 226, 199, 191, 219, 14, 217, 84, 59, 78, 176, 165, 80, 100, 218, 54, 140, 125, + 197, 249, 99, 247, 17, 71, 160, 180, 121, 49, 249, 172, 16, 189, 110, 38, 119, 42, 116, 95, + 138, 195, 26, 201, 251, 87, 172, 17, 108, 168, 101, 136, 252, 118, 147, 242, 33, 11, 16, 200, + 158, 124, 254, 165, 5, 73, 12, 53, 221, 137, 122, 171, 78, 203, 75, 123, 4, 181, 50, 42, 37, + 32, 13, 234, 176, 114, 190, 190, 94, 157, 23, 32, 1, 226, 90, 177, 34, 62, 201, 180, 249, 9, + 215, 203, 139, 66, 88, 169, 207, 196, 116, 47, 36, 127, 53, 131, 194, 0, 71, 133, 68, 125, 203, + 156, 32, 5, 84, 147, 243, 72, 44, 231, 137, 95, 82, 224, 204, 154, 206, 188, 12, 47, 68, 76, + 47, 63, 17, 175, 206, 61, 111, 0, 64, 60, 146, 23, 95, 32, 5, 84, 147, 243, 72, 44, 231, 137, + 95, 82, 224, 204, 154, 206, 188, 12, 47, 68, 76, 47, 63, 17, 175, 206, 61, 111, 0, 64, 60, 146, + 23, 95, 32, 5, 84, 147, 243, 72, 44, 231, 137, 95, 82, 224, 204, 154, 206, 188, 12, 47, 68, 76, + 47, 63, 17, 175, 206, 61, 111, 0, 64, 60, 146, 23, 95, 32, 3, 90, 112, 38, 248, 174, 225, 80, + 47, 99, 208, 220, 166, 201, 154, 71, 62, 141, 42, 104, 172, 207, 250, 140, 201, 249, 74, 88, + 26, 166, 127, 212, 32, 5, 134, 245, 233, 202, 185, 178, 113, 74, 59, 208, 205, 65, 8, 236, 118, + 243, 40, 83, 202, 162, 159, 83, 0, 191, 255, 58, 82, 220, 255, 23, 29, 32, 6, 227, 171, 139, + 219, 139, 164, 90, 41, 98, 199, 188, 177, 9, 186, 202, 145, 64, 5, 126, 143, 190, 146, 169, 7, + 143, 169, 218, 35, 68, 235, 128, 32, 0, 130, 168, 205, 202, 184, 230, 169, 214, 27, 165, 47, + 104, 18, 54, 173, 167, 144, 26, 11, 42, 182, 168, 131, 255, 193, 120, 81, 92, 85, 21, 9, 32, 0, + 198, 81, 52, 192, 76, 178, 36, 188, 172, 159, 24, 73, 243, 74, 123, 37, 10, 41, 255, 32, 109, + 116, 92, 255, 49, 76, 67, 28, 250, 217, 188, 32, 1, 236, 236, 188, 57, 240, 250, 49, 136, 157, + 51, 197, 131, 190, 82, 109, 93, 176, 233, 189, 132, 124, 15, 150, 83, 200, 212, 101, 116, 234, + 125, 71, 32, 2, 65, 199, 238, 237, 253, 238, 0, 191, 87, 58, 174, 152, 1, 151, 187, 90, 87, + 125, 195, 41, 103, 49, 189, 18, 125, 62, 190, 24, 62, 181, 83, 32, 4, 198, 36, 193, 246, 195, + 182, 251, 63, 57, 13, 176, 164, 25, 122, 253, 188, 150, 158, 106, 136, 241, 239, 75, 249, 173, + 62, 56, 58, 118, 253, 98, 32, 7, 152, 239, 4, 75, 94, 140, 52, 205, 198, 243, 9, 254, 146, 160, + 50, 36, 61, 193, 169, 85, 180, 218, 212, 66, 18, 232, 128, 75, 217, 49, 104, 32, 4, 150, 204, + 12, 168, 216, 22, 200, 19, 209, 117, 116, 14, 1, 6, 73, 199, 148, 170, 40, 201, 65, 239, 108, + 97, 54, 161, 208, 73, 96, 40, 77, 32, 0, 89, 1, 107, 47, 187, 250, 147, 133, 134, 49, 97, 130, + 198, 199, 203, 218, 83, 147, 96, 23, 159, 218, 246, 198, 194, 81, 13, 28, 75, 53, 47, 32, 1, + 28, 219, 187, 19, 102, 9, 144, 68, 190, 184, 76, 169, 190, 127, 131, 71, 71, 99, 157, 255, 174, + 167, 193, 86, 169, 29, 171, 160, 168, 87, 20, 32, 4, 220, 160, 34, 62, 11, 188, 248, 175, 118, + 70, 24, 125, 79, 86, 37, 114, 155, 49, 100, 78, 8, 48, 75, 159, 236, 21, 197, 193, 244, 118, + 250, 32, 3, 55, 131, 240, 131, 7, 226, 70, 14, 219, 44, 30, 186, 18, 176, 152, 179, 224, 211, + 175, 71, 201, 115, 172, 16, 8, 196, 103, 206, 249, 150, 82, 32, 1, 77, 168, 185, 25, 164, 38, + 14, 193, 137, 113, 154, 54, 217, 60, 156, 38, 138, 122, 105, 254, 17, 102, 164, 221, 43, 37, + 170, 221, 72, 232, 151, 32, 3, 37, 118, 187, 49, 235, 31, 41, 121, 145, 157, 107, 21, 174, 81, + 106, 253, 208, 11, 111, 44, 169, 123, 199, 86, 247, 34, 146, 159, 78, 78, 134, 32, 6, 25, 172, + 62, 68, 76, 1, 151, 154, 104, 86, 6, 130, 165, 106, 33, 176, 185, 58, 216, 16, 206, 236, 152, + 171, 90, 91, 42, 229, 248, 62, 237, 32, 4, 6, 28, 244, 157, 110, 212, 16, 249, 146, 113, 14, + 89, 246, 188, 124, 152, 104, 62, 116, 143, 17, 229, 77, 42, 21, 54, 36, 127, 143, 46, 180, 32, + 6, 213, 30, 201, 15, 161, 166, 224, 201, 122, 48, 53, 149, 96, 216, 206, 129, 31, 134, 225, 26, + 111, 0, 127, 54, 100, 16, 33, 24, 239, 169, 118, 23, 32, 6, 5, 152, 2, 28, 99, 252, 217, 4, + 193, 210, 48, 97, 240, 47, 63, 19, 117, 140, 245, 109, 151, 204, 220, 129, 39, 253, 49, 242, + 168, 120, 151, 32, 5, 174, 60, 34, 214, 147, 223, 113, 120, 155, 177, 195, 231, 225, 156, 129, + 231, 208, 52, 152, 129, 190, 176, 115, 224, 38, 54, 197, 65, 184, 142, 232, 32, 5, 174, 60, 34, + 214, 147, 223, 113, 120, 155, 177, 195, 231, 225, 156, 129, 231, 208, 52, 152, 129, 190, 176, + 115, 224, 38, 54, 197, 65, 184, 142, 232, 32, 5, 174, 60, 34, 214, 147, 223, 113, 120, 155, + 177, 195, 231, 225, 156, 129, 231, 208, 52, 152, 129, 190, 176, 115, 224, 38, 54, 197, 65, 184, + 142, 232, 32, 7, 121, 221, 240, 129, 105, 88, 243, 253, 201, 60, 90, 139, 168, 81, 179, 130, + 76, 9, 95, 160, 104, 0, 244, 217, 146, 66, 101, 158, 81, 168, 54, 32, 3, 213, 73, 227, 177, + 207, 252, 173, 28, 118, 100, 147, 55, 66, 251, 107, 88, 24, 5, 238, 106, 72, 82, 218, 80, 172, + 99, 216, 235, 159, 151, 50, 32, 7, 166, 82, 123, 107, 43, 164, 75, 210, 180, 109, 36, 0, 35, + 152, 37, 8, 141, 249, 252, 44, 138, 67, 152, 2, 224, 75, 38, 106, 110, 253, 236, 32, 4, 41, 74, + 94, 251, 100, 113, 67, 170, 6, 100, 54, 157, 118, 217, 101, 75, 145, 69, 14, 165, 142, 150, + 236, 107, 5, 130, 59, 182, 189, 116, 1, 32, 7, 53, 87, 13, 68, 248, 17, 250, 175, 182, 241, + 183, 220, 59, 172, 179, 118, 165, 111, 58, 136, 7, 230, 99, 169, 178, 182, 245, 120, 169, 158, + 50, 32, 2, 86, 206, 226, 81, 43, 2, 142, 153, 222, 85, 106, 197, 123, 159, 24, 79, 55, 128, 92, + 124, 80, 81, 214, 171, 62, 227, 253, 77, 143, 102, 42, 32, 0, 71, 65, 188, 141, 114, 57, 192, + 29, 15, 199, 3, 116, 181, 84, 185, 134, 76, 5, 81, 13, 253, 171, 180, 121, 221, 223, 175, 88, + 166, 11, 196, 32, 6, 57, 225, 140, 207, 235, 77, 232, 93, 0, 172, 133, 213, 23, 104, 96, 146, + 118, 242, 175, 33, 244, 78, 83, 99, 173, 165, 213, 204, 30, 253, 95, 32, 4, 83, 171, 84, 96, + 90, 141, 133, 240, 109, 239, 84, 166, 217, 236, 205, 142, 191, 73, 197, 45, 188, 207, 222, 60, + 225, 187, 184, 238, 102, 84, 103, 32, 0, 248, 88, 98, 94, 207, 141, 75, 237, 61, 234, 162, 40, + 176, 186, 22, 52, 93, 138, 100, 151, 157, 182, 64, 247, 146, 176, 157, 203, 35, 208, 185, 32, + 6, 178, 43, 190, 231, 177, 156, 59, 89, 32, 7, 140, 237, 213, 218, 119, 18, 186, 119, 179, 205, + 10, 76, 209, 60, 72, 173, 79, 129, 116, 34, 212, 32, 7, 232, 242, 81, 73, 173, 219, 223, 87, 6, + 238, 88, 194, 11, 217, 198, 236, 17, 49, 50, 99, 140, 203, 66, 62, 38, 52, 62, 34, 39, 73, 208, + 32, 7, 164, 100, 246, 243, 192, 242, 11, 3, 187, 235, 185, 48, 141, 142, 105, 234, 220, 67, + 207, 112, 68, 78, 196, 178, 224, 209, 7, 183, 12, 184, 178, 32, 7, 134, 217, 46, 19, 216, 242, + 5, 148, 52, 225, 134, 144, 179, 143, 210, 3, 36, 53, 103, 68, 180, 1, 170, 39, 27, 89, 253, 40, + 180, 186, 55, 32, 2, 51, 50, 203, 226, 167, 22, 7, 220, 184, 62, 60, 166, 91, 148, 101, 64, 96, + 252, 158, 211, 27, 103, 227, 108, 209, 86, 179, 120, 142, 108, 125, 32, 0, 214, 90, 141, 11, + 83, 126, 234, 160, 200, 182, 133, 217, 156, 187, 122, 199, 33, 63, 147, 194, 184, 189, 155, + 189, 176, 50, 183, 77, 239, 118, 65, 32, 2, 220, 62, 108, 245, 46, 101, 64, 164, 180, 143, 208, + 135, 122, 178, 73, 13, 227, 145, 18, 129, 86, 111, 249, 131, 202, 162, 107, 222, 148, 145, 90, + 32, 0, 220, 193, 44, 213, 8, 251, 223, 112, 251, 65, 71, 90, 159, 20, 177, 98, 21, 184, 150, + 66, 54, 11, 248, 231, 48, 118, 114, 29, 66, 143, 26, 32, 4, 89, 205, 230, 246, 77, 248, 106, + 59, 250, 124, 66, 250, 146, 104, 183, 83, 64, 79, 182, 76, 27, 7, 149, 119, 76, 145, 118, 129, + 21, 32, 98, 7, 223, 176, 137, 56, 34, 20, 220, 117, 204, 42, 101, 37, 254, 127, 159, 184, 45, + 146, 224, 216, 213, 171, 14, 47, 51, 242, 104, 53, 159, 183, 189, 142, 20, 35, 156, 90, 14, + 139, 81, 151, 85, 223, 66, 250, 192, 71, 233, 46, 70, 17, 154, 249, 67, 242, 43, 218, 37, 219, + 8, 233, 176, 45, 219, 183, 202, 146, 92, 237, 231, 92, 225, 161, 236, 140, 152, 24, 136, 150, + 9, 10, 132, 241, 33, 38, 195, 190, 159, 14, 216, 41, 128, 143, 188, 48, 251, 239, 231, 102, + 253, 245, 174, 171, 190, 149, 90, 248, 5, 170, 199, 101, 38, 205, 66, 24, 120, 101, 195, 107, + 199, 25, 191, 20, 195, 54, 3, 190, 149, 34, 232, 6, 174, 198, 153, 194, 67, 57, 25, 13, 66, 10, + 104, 202, 169, 248, 127, 114, 195, 29, 6, 232, 245, 21, 133, 119, 106, 167, 44, 27, 8, 27, 237, + 138, 226, 59, 242, 120, 11, 188, 127, 143, 128, 243, 155, 247, 151, 78, 29, 103, 194, 98, 234, + 250, 167, 255, 241, 154, 113, 186, 85, 108, 240, 84, 111, 163, 238, 222, 68, 50, 217, 122, 69, + 188, 177, 88, 171, 3, 59, 238, 53, 27, 39, 235, 53, 93, 13, 190, 107, 174, 144, 238, 168, 55, + 70, 208, 7, 223, 176, 137, 56, 34, 20, 220, 117, 204, 42, 101, 37, 254, 127, 159, 184, 45, 146, + 224, 216, 213, 171, 14, 47, 51, 242, 104, 53, 159, 183, 189, 142, 20, 35, 156, 90, 14, 139, 81, + 151, 85, 223, 66, 250, 192, 71, 233, 46, 70, 17, 154, 249, 67, 242, 43, 218, 37, 219, 8, 233, + 176, 45, 219, 183, 202, 146, 92, 237, 231, 92, 225, 161, 236, 140, 152, 24, 136, 150, 9, 10, + 132, 241, 33, 38, 195, 190, 159, 14, 216, 41, 128, 143, 188, 48, 251, 239, 231, 102, 253, 245, + 174, 171, 190, 149, 90, 248, 5, 170, 199, 101, 38, 205, 66, 24, 120, 101, 195, 107, 199, 25, + 191, 20, 195, 54, 3, 190, 149, 34, 232, 6, 174, 198, 153, 194, 67, 57, 25, 13, 66, 10, 104, + 202, 169, 248, 127, 114, 195, 29, 6, 232, 245, 21, 133, 119, 106, 167, 44, 27, 8, 27, 237, 138, + 226, 59, 242, 120, 11, 188, 127, 143, 128, 243, 155, 247, 151, 78, 29, 103, 194, 98, 234, 250, + 167, 255, 241, 154, 113, 186, 85, 108, 240, 84, 111, 163, 238, 222, 68, 50, 217, 122, 69, 188, + 177, 88, 171, 3, 59, 238, 53, 27, 39, 235, 53, 93, 13, 190, 107, 174, 144, 238, 168, 55, 70, + 208, 2, 32, 3, 217, 169, 177, 240, 4, 196, 128, 119, 155, 197, 53, 156, 199, 129, 214, 199, + 115, 49, 99, 221, 93, 178, 149, 239, 66, 87, 206, 118, 3, 14, 60, 32, 6, 87, 144, 100, 92, 84, + 221, 234, 68, 28, 139, 255, 104, 182, 74, 187, 18, 52, 203, 153, 252, 214, 202, 207, 140, 188, + 228, 92, 76, 181, 68, 146, 2, 32, 7, 72, 105, 187, 61, 213, 88, 1, 200, 104, 118, 116, 175, + 244, 180, 112, 101, 129, 142, 225, 30, 53, 245, 14, 34, 102, 171, 248, 86, 76, 249, 145, 32, 0, + 156, 162, 126, 22, 103, 4, 195, 229, 54, 125, 189, 196, 218, 227, 45, 130, 192, 214, 57, 20, + 131, 93, 239, 121, 38, 1, 128, 14, 153, 174, 205, 8, 44, 48, 189, 171, 247, 67, 41, 148, 159, + 94, 72, 103, 197, 198, 82, 66, 242, 249, 236, 141, 6, 123, 74, 227, 249, 206, 124, 47, 6, 104, + 38, 230, 94, 67, 253, 217, 249, 92, 206, 177, 100, 149, 47, 9, 185, 213, 11, 122, 221, 185, 44, + 217, 47, 88, 67, 78, 150, 36, 16, 37, 108, 107, 8, 115, 164, 164, 228, 237, 174, 82, 103, 190, + 174, 187, 38, 84, 24, 36, 133, 6, 191, 42, 16, 136, 195, 125, 107, 122, 215, 96, 219, 72, 76, + 145, 38, 227, 23, 142, 0, 23, 70, 116, 106, 121, 99, 128, 210, 78, 29, 19, 195, 22, 35, 246, + 10, 106, 109, 28, 115, 181, 223, 237, 182, 4, 20, 128, 109, 23, 191, 22, 144, 60, 121, 121, 64, + 190, 105, 81, 199, 185, 217, 98, 169, 242, 90, 255, 155, 197, 57, 106, 49, 93, 166, 34, 146, + 145, 252, 153, 151, 197, 90, 125, 16, 25, 41, 188, 114, 58, 89, 239, 63, 64, 105, 4, 102, 211, + 3, 43, 180, 109, 69, 210, 168, 47, 199, 3, 231, 17, 41, 201, 253, 197, 66, 155, 36, 12, 34, 61, + 169, 95, 84, 39, 35, 149, 250, 234, 76, 165, 17, 124, 40, 205, 185, 201, 27, 71, 191, 77, 148, + 228, 244, 148, 31, 91, 252, 89, 141, 6, 13, 139, 105, 158, 82, 26, 166, 148, 184, 91, 40, 185, + 45, 214, 150, 78, 230, 134, 109, 250, 24, 78, 249, 49, 197, 186, 141, 6, 8, 6, 66, 38, 16, 51, + 119, 153, 33, 189, 204, 188, 70, 40, 192, 179, 242, 238, 41, 48, 183, 178, 43, 146, 114, 227, + 246, 152, 194, 231, 155, 121, 149, 94, 67, 253, 217, 249, 92, 206, 177, 100, 149, 47, 9, 185, + 213, 11, 122, 221, 185, 44, 217, 47, 88, 67, 78, 150, 36, 16, 37, 108, 107, 8, 115, 164, 164, + 228, 237, 174, 82, 103, 190, 174, 187, 38, 84, 24, 36, 133, 6, 191, 42, 16, 136, 195, 125, 107, + 122, 215, 96, 219, 72, 76, 145, 38, 227, 23, 142, 0, 23, 70, 116, 106, 121, 99, 128, 210, 78, + 29, 19, 195, 22, 35, 246, 10, 106, 109, 28, 115, 181, 223, 237, 182, 4, 20, 128, 109, 23, 191, + 22, 144, 60, 121, 121, 64, 190, 105, 81, 199, 185, 217, 98, 169, 242, 90, 255, 155, 197, 57, + 106, 49, 93, 166, 34, 146, 145, 252, 153, 151, 197, 90, 125, 16, 25, 41, 188, 114, 58, 89, 239, + 63, 64, 105, 4, 102, 211, 3, 43, 180, 109, 69, 210, 168, 47, 199, 3, 231, 17, 41, 201, 253, + 197, 66, 155, 36, 12, 34, 61, 169, 95, 84, 39, 35, 149, 250, 234, 76, 165, 17, 124, 40, 205, + 185, 201, 27, 71, 191, 77, 148, 228, 244, 148, 31, 91, 252, 89, 141, 6, 13, 139, 105, 158, 82, + 26, 166, 148, 184, 91, 40, 185, 45, 214, 150, 78, 230, 134, 109, 250, 24, 78, 249, 49, 197, + 186, 141, 6, 36, 32, 3, 93, 72, 164, 72, 101, 137, 91, 50, 183, 167, 161, 235, 151, 19, 164, + 69, 67, 138, 192, 137, 97, 219, 32, 20, 126, 228, 1, 243, 84, 45, 40, 32, 0, 158, 110, 8, 12, + 109, 214, 114, 60, 23, 15, 171, 234, 87, 10, 24, 85, 255, 37, 163, 17, 166, 2, 189, 34, 250, + 167, 64, 199, 231, 68, 55, 32, 5, 12, 1, 165, 225, 71, 142, 63, 199, 27, 214, 62, 4, 7, 215, + 119, 29, 204, 176, 115, 44, 72, 45, 87, 253, 79, 104, 116, 220, 180, 99, 147, 32, 4, 209, 23, + 205, 180, 18, 18, 5, 133, 190, 98, 214, 252, 241, 64, 183, 234, 66, 9, 110, 133, 133, 252, 73, + 122, 227, 44, 221, 238, 127, 5, 190, 32, 0, 205, 182, 86, 228, 119, 212, 109, 157, 248, 48, + 151, 3, 27, 143, 210, 167, 197, 226, 35, 215, 173, 105, 126, 98, 90, 144, 169, 200, 216, 4, 27, + 32, 7, 182, 153, 192, 164, 94, 213, 7, 81, 178, 161, 104, 1, 253, 131, 118, 69, 226, 98, 112, + 103, 138, 99, 200, 16, 8, 213, 118, 146, 174, 172, 238, 32, 3, 138, 128, 166, 158, 66, 88, 106, + 238, 115, 17, 236, 1, 17, 18, 205, 217, 56, 172, 81, 37, 107, 16, 246, 41, 64, 227, 56, 191, + 103, 249, 87, 32, 5, 197, 64, 83, 79, 33, 44, 61, 247, 57, 136, 246, 0, 136, 137, 102, 236, + 156, 86, 40, 146, 181, 136, 123, 20, 160, 113, 156, 95, 179, 252, 172, 32, 6, 97, 73, 120, 25, + 217, 174, 109, 140, 223, 119, 82, 47, 54, 201, 127, 136, 196, 162, 77, 83, 117, 2, 151, 62, 68, + 58, 246, 118, 115, 38, 101, 32, 2, 157, 38, 116, 98, 175, 2, 6, 138, 30, 94, 136, 19, 148, 154, + 76, 125, 244, 11, 40, 93, 81, 83, 119, 116, 203, 167, 224, 50, 191, 159, 159, 32, 0, 175, 156, + 11, 75, 255, 239, 88, 118, 72, 86, 193, 185, 17, 52, 234, 231, 153, 47, 173, 77, 247, 77, 216, + 57, 191, 20, 5, 200, 213, 74, 117, 32, 0, 62, 153, 65, 208, 118, 182, 23, 174, 164, 155, 159, + 232, 5, 38, 51, 63, 181, 127, 35, 243, 123, 219, 125, 192, 209, 221, 203, 76, 82, 98, 86, 32, + 5, 22, 232, 170, 86, 80, 89, 25, 218, 109, 61, 10, 143, 104, 0, 127, 160, 66, 115, 104, 106, + 128, 168, 41, 111, 29, 36, 98, 131, 0, 67, 246, 32, 1, 206, 169, 179, 80, 23, 138, 158, 196, + 38, 80, 29, 56, 215, 173, 212, 221, 84, 28, 18, 145, 203, 40, 27, 75, 188, 125, 92, 200, 191, + 96, 148, 32, 4, 101, 254, 40, 26, 84, 221, 157, 243, 85, 218, 229, 203, 94, 91, 182, 129, 32, + 133, 66, 82, 255, 210, 103, 89, 210, 64, 214, 170, 248, 216, 89, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 93, 83, 13, 39, + 225, 51, 125, 213, 246, 158, 232, 243, 193, 188, 211, 178, 58, 244, 128, 89, 225, 155, 190, 28, + 228, 120, 50, 132, 29, 234, 244, 32, 7, 219, 227, 185, 248, 229, 236, 137, 91, 50, 171, 67, 42, + 191, 128, 161, 154, 154, 107, 199, 194, 156, 33, 2, 143, 255, 67, 51, 24, 104, 133, 14, 32, 5, + 146, 19, 155, 160, 141, 85, 232, 35, 7, 21, 242, 138, 101, 227, 115, 172, 245, 197, 2, 96, 173, + 89, 196, 94, 161, 149, 172, 204, 213, 77, 241, 32, 7, 249, 80, 4, 89, 1, 199, 167, 27, 139, 62, + 219, 86, 89, 72, 123, 115, 154, 224, 30, 134, 235, 116, 44, 8, 144, 129, 32, 105, 174, 180, 51, + 32, 3, 109, 79, 177, 19, 59, 134, 178, 84, 88, 166, 192, 234, 24, 132, 50, 9, 52, 190, 175, + 103, 0, 114, 130, 129, 252, 85, 105, 80, 13, 215, 44, 32, 0, 65, 132, 220, 157, 57, 141, 161, + 237, 95, 246, 72, 187, 213, 187, 214, 26, 96, 82, 179, 52, 229, 229, 37, 214, 245, 159, 218, + 49, 194, 193, 152, 32, 3, 183, 108, 42, 159, 80, 215, 118, 17, 80, 122, 97, 61, 97, 29, 254, + 39, 135, 228, 135, 94, 107, 106, 221, 63, 255, 38, 138, 6, 196, 241, 139, 32, 4, 138, 187, 53, + 147, 230, 88, 117, 192, 126, 19, 155, 155, 156, 127, 50, 196, 43, 177, 64, 239, 117, 169, 171, + 254, 53, 246, 1, 9, 12, 34, 196, 32, 4, 160, 6, 194, 185, 109, 38, 218, 213, 228, 88, 0, 31, + 246, 241, 176, 177, 119, 122, 108, 5, 184, 189, 59, 198, 144, 139, 221, 103, 10, 165, 2, 32, 2, + 111, 100, 250, 248, 222, 100, 64, 48, 166, 146, 122, 2, 8, 68, 252, 92, 143, 51, 116, 45, 110, + 98, 100, 44, 170, 181, 38, 109, 65, 195, 204, 32, 1, 20, 223, 54, 185, 95, 217, 122, 110, 8, + 220, 187, 76, 74, 65, 22, 142, 193, 220, 144, 118, 178, 193, 68, 147, 34, 251, 2, 23, 86, 59, + 84, 32, 4, 11, 211, 18, 40, 27, 51, 227, 51, 84, 194, 0, 1, 238, 114, 180, 172, 52, 106, 108, + 246, 211, 240, 67, 31, 136, 195, 86, 130, 203, 152, 13, 32, 3, 59, 216, 203, 252, 156, 121, 46, + 5, 129, 98, 56, 105, 47, 215, 70, 255, 253, 194, 22, 92, 185, 162, 24, 189, 240, 19, 167, 129, + 51, 118, 15, 32, 6, 218, 40, 8, 230, 94, 223, 24, 55, 176, 253, 83, 23, 76, 163, 40, 55, 166, + 131, 30, 122, 192, 213, 155, 114, 135, 8, 36, 157, 222, 29, 33, 32, 7, 158, 22, 33, 155, 10, + 170, 45, 59, 39, 103, 96, 105, 160, 76, 244, 221, 24, 125, 27, 163, 37, 121, 209, 200, 87, 71, + 74, 128, 187, 192, 77, 32, 1, 233, 235, 60, 224, 219, 201, 97, 98, 250, 177, 160, 110, 194, 21, + 88, 29, 170, 204, 12, 223, 187, 86, 195, 43, 127, 188, 63, 33, 176, 119, 155, 32, 7, 144, 93, + 56, 141, 120, 249, 210, 19, 205, 141, 91, 105, 178, 112, 63, 196, 79, 204, 39, 17, 230, 127, 8, + 93, 82, 101, 30, 208, 100, 221, 55, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, 255, 254, 239, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 97, 32, 0, 15, 252, 208, 1, 16, 6, 197, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 223, 15, 255, 208, 0, 16, 0, 102, 32, 7, 255, 255, 255, - 255, 255, 241, 48, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 33, 32, 2, 47, 252, 208, 1, 17, 69, 65, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 246, 255, 47, 255, 208, 0, 16, 19, - 34, 32, 7, 255, 255, 255, 255, 255, 230, 144, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 129, 32, 7, 255, 255, 255, - 255, 255, 245, 112, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 97, 32, 7, 255, 255, 255, 255, 255, 200, 208, 255, 255, + 255, 239, 255, 225, 36, 32, 4, 198, 99, 47, 102, 150, 146, 77, 205, 39, 228, 73, 208, 170, 131, + 91, 229, 149, 154, 108, 150, 155, 148, 154, 250, 98, 98, 240, 61, 154, 247, 24, 32, 4, 94, 5, + 186, 86, 10, 188, 71, 33, 154, 51, 118, 173, 32, 170, 225, 114, 150, 144, 214, 197, 235, 56, + 211, 75, 119, 174, 112, 233, 134, 174, 246, 32, 6, 214, 144, 50, 68, 104, 213, 16, 190, 137, + 200, 55, 159, 110, 39, 163, 41, 71, 7, 35, 94, 3, 92, 153, 218, 69, 52, 27, 30, 199, 213, 181, + 32, 0, 116, 127, 190, 3, 87, 233, 226, 70, 40, 211, 143, 245, 140, 24, 29, 26, 136, 122, 149, + 187, 102, 54, 132, 255, 0, 112, 16, 195, 254, 74, 70, 32, 3, 133, 116, 230, 181, 99, 254, 244, + 122, 42, 252, 158, 208, 173, 141, 214, 205, 60, 187, 213, 75, 112, 84, 154, 121, 184, 79, 230, + 137, 204, 151, 160, 32, 5, 110, 77, 198, 197, 214, 119, 53, 127, 26, 252, 4, 108, 154, 64, 215, + 160, 192, 232, 92, 43, 150, 104, 189, 48, 198, 58, 17, 232, 126, 121, 248, 32, 0, 96, 162, 110, + 38, 151, 101, 178, 95, 20, 2, 138, 205, 173, 58, 137, 128, 172, 7, 37, 18, 32, 29, 212, 234, + 147, 6, 25, 14, 62, 61, 42, 32, 0, 48, 81, 55, 19, 75, 178, 217, 47, 138, 1, 69, 102, 214, 157, + 68, 192, 86, 3, 146, 137, 16, 14, 234, 117, 73, 131, 12, 135, 31, 30, 149, 32, 0, 70, 215, 101, + 166, 112, 230, 215, 64, 31, 158, 62, 130, 47, 190, 212, 203, 151, 94, 13, 91, 191, 102, 229, + 251, 161, 134, 218, 86, 116, 251, 118, 32, 7, 148, 42, 133, 153, 156, 222, 114, 121, 161, 47, + 246, 4, 104, 253, 82, 143, 222, 175, 136, 17, 84, 9, 185, 73, 24, 157, 55, 77, 203, 206, 170, + 32, 6, 95, 234, 173, 56, 114, 7, 142, 82, 41, 132, 108, 170, 143, 82, 105, 193, 167, 44, 204, + 178, 1, 244, 42, 127, 140, 103, 23, 202, 169, 243, 42, 32, 2, 77, 249, 28, 214, 193, 240, 93, + 31, 25, 106, 39, 150, 196, 222, 90, 225, 108, 105, 128, 180, 141, 3, 101, 242, 62, 156, 7, 172, + 206, 229, 231, 32, 3, 46, 149, 238, 10, 122, 151, 202, 36, 91, 125, 53, 54, 22, 155, 163, 176, + 198, 51, 163, 149, 187, 117, 74, 51, 8, 165, 38, 14, 24, 245, 40, 32, 4, 239, 189, 161, 235, + 217, 214, 24, 228, 113, 16, 30, 82, 45, 123, 159, 104, 103, 91, 25, 207, 207, 250, 116, 228, + 250, 245, 176, 93, 7, 252, 107, 32, 2, 166, 141, 155, 18, 183, 248, 119, 26, 147, 37, 170, 247, + 219, 46, 2, 31, 160, 9, 208, 255, 31, 92, 171, 51, 122, 64, 44, 65, 105, 106, 97, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 2, + 156, 46, 34, 115, 162, 75, 166, 181, 116, 156, 220, 60, 49, 126, 243, 206, 46, 132, 61, 180, + 42, 193, 57, 105, 171, 49, 92, 216, 164, 255, 63, 32, 2, 111, 35, 213, 86, 255, 227, 39, 70, + 26, 138, 73, 140, 112, 19, 17, 180, 33, 56, 2, 82, 70, 96, 99, 52, 178, 58, 41, 184, 87, 179, + 113, 32, 3, 147, 237, 53, 138, 141, 166, 16, 37, 177, 164, 176, 191, 57, 129, 184, 47, 249, + 237, 203, 85, 179, 192, 68, 69, 59, 199, 14, 83, 181, 238, 61, 32, 1, 118, 29, 91, 8, 184, 237, + 60, 139, 126, 142, 141, 59, 114, 71, 146, 78, 93, 78, 242, 123, 222, 219, 252, 43, 225, 140, + 28, 230, 172, 188, 58, 32, 1, 84, 130, 32, 213, 78, 79, 117, 206, 162, 1, 8, 224, 147, 200, + 238, 60, 27, 30, 226, 8, 3, 71, 210, 50, 243, 97, 91, 91, 212, 126, 174, 32, 0, 224, 32, 20, + 176, 200, 99, 47, 72, 141, 39, 151, 121, 176, 140, 240, 31, 227, 173, 200, 71, 167, 168, 90, + 97, 117, 3, 120, 21, 185, 211, 224, 32, 1, 37, 68, 6, 241, 202, 81, 117, 47, 144, 18, 225, 165, + 193, 17, 181, 203, 96, 21, 248, 79, 44, 199, 18, 158, 228, 197, 98, 156, 192, 94, 80, 32, 5, + 185, 214, 73, 108, 240, 134, 9, 148, 89, 43, 205, 61, 124, 125, 98, 76, 85, 5, 182, 185, 175, + 212, 9, 48, 201, 89, 45, 183, 104, 130, 89, 32, 4, 230, 12, 106, 169, 13, 106, 222, 118, 104, + 250, 215, 212, 230, 34, 131, 8, 142, 71, 207, 35, 197, 114, 84, 3, 18, 90, 18, 92, 190, 198, + 231, 32, 6, 129, 116, 116, 248, 215, 104, 164, 63, 123, 158, 128, 206, 108, 34, 113, 238, 69, + 104, 59, 68, 244, 166, 220, 58, 209, 172, 56, 49, 246, 59, 251, 32, 5, 86, 230, 229, 219, 163, + 126, 239, 151, 183, 79, 219, 168, 206, 75, 82, 196, 130, 131, 63, 170, 158, 100, 118, 233, 237, + 114, 173, 176, 238, 108, 217, 32, 7, 244, 155, 40, 219, 168, 42, 187, 215, 244, 185, 179, 251, + 71, 122, 223, 29, 143, 140, 84, 83, 0, 21, 74, 227, 57, 248, 25, 153, 112, 255, 181, 32, 7, + 207, 113, 72, 180, 24, 231, 110, 224, 195, 157, 3, 64, 86, 79, 206, 101, 110, 184, 221, 7, 149, + 11, 254, 197, 182, 100, 80, 245, 58, 63, 147, 32, 5, 221, 109, 132, 218, 56, 223, 127, 0, 226, + 252, 251, 156, 5, 76, 45, 87, 250, 6, 120, 225, 77, 119, 106, 247, 192, 187, 108, 242, 39, 4, + 167, 32, 5, 115, 229, 56, 54, 6, 240, 24, 242, 136, 101, 92, 116, 84, 44, 239, 43, 133, 203, + 167, 3, 81, 139, 9, 192, 77, 237, 242, 251, 5, 68, 235, 32, 4, 114, 254, 69, 61, 206, 97, 32, + 57, 35, 33, 228, 65, 18, 161, 248, 118, 151, 197, 161, 100, 84, 220, 9, 162, 222, 214, 19, 5, + 249, 5, 5, 32, 6, 72, 194, 21, 30, 130, 163, 152, 41, 83, 40, 242, 19, 88, 251, 153, 240, 87, + 52, 164, 156, 51, 138, 64, 89, 145, 227, 141, 228, 120, 119, 186, 32, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, + 255, 254, 239, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 239, 255, 225, 1, 8, 198, 23, 84, 255, 151, 170, 24, 10, + 205, 81, 45, 252, 78, 227, 104, 43, 34, 71, 48, 21, 221, 59, 83, 55, 230, 173, 205, 23, 165, + 63, 164, 81, 214, 5, 201, 144, 116, 141, 237, 183, 99, 166, 133, 98, 237, 150, 237, 117, 64, + 62, 158, 103, 219, 38, 180, 159, 216, 51, 118, 132, 190, 36, 174, 82, 184, 132, 162, 205, 221, + 202, 214, 145, 217, 167, 46, 204, 125, 71, 164, 57, 104, 83, 60, 227, 147, 221, 50, 19, 127, + 168, 46, 215, 74, 229, 109, 113, 208, 168, 36, 249, 140, 75, 236, 88, 39, 14, 52, 42, 174, 36, + 186, 134, 137, 48, 43, 33, 53, 149, 217, 125, 46, 95, 174, 91, 171, 36, 50, 83, 66, 229, 202, + 144, 161, 214, 44, 14, 190, 143, 160, 9, 57, 35, 119, 113, 194, 148, 35, 100, 168, 241, 151, + 40, 203, 46, 56, 120, 109, 133, 0, 249, 165, 107, 85, 164, 80, 243, 242, 36, 235, 226, 199, + 191, 219, 14, 217, 84, 59, 78, 176, 165, 80, 100, 218, 54, 140, 125, 197, 249, 99, 247, 17, 71, + 160, 180, 121, 49, 249, 172, 16, 189, 110, 38, 119, 42, 116, 95, 138, 195, 26, 201, 251, 87, + 172, 17, 108, 168, 101, 136, 252, 118, 147, 242, 33, 11, 16, 200, 158, 124, 254, 165, 5, 73, + 12, 53, 221, 137, 122, 171, 78, 203, 75, 123, 4, 181, 50, 42, 37, 32, 13, 234, 176, 114, 190, + 190, 94, 157, 8, 170, 178, 81, 208, 81, 111, 31, 28, 126, 253, 70, 188, 190, 185, 39, 217, 50, + 111, 70, 67, 107, 101, 179, 74, 138, 102, 107, 71, 148, 178, 73, 169, 214, 5, 201, 144, 116, + 141, 237, 183, 99, 166, 133, 98, 237, 150, 237, 117, 64, 62, 158, 103, 219, 38, 180, 159, 216, + 51, 118, 132, 190, 36, 174, 82, 184, 132, 162, 205, 221, 202, 214, 145, 217, 167, 46, 204, 125, + 71, 164, 57, 104, 83, 60, 227, 147, 221, 50, 19, 127, 168, 46, 215, 74, 229, 109, 113, 208, + 168, 36, 249, 140, 75, 236, 88, 39, 14, 52, 42, 174, 36, 186, 134, 137, 48, 43, 33, 53, 149, + 217, 125, 46, 95, 174, 91, 171, 36, 50, 83, 66, 229, 202, 144, 161, 214, 44, 14, 190, 143, 160, + 9, 57, 35, 119, 113, 194, 148, 35, 100, 168, 241, 151, 40, 203, 46, 56, 120, 109, 133, 0, 249, + 165, 107, 85, 164, 80, 243, 242, 36, 235, 226, 199, 191, 219, 14, 217, 84, 59, 78, 176, 165, + 80, 100, 218, 54, 140, 125, 197, 249, 99, 247, 17, 71, 160, 180, 121, 49, 249, 172, 16, 189, + 110, 38, 119, 42, 116, 95, 138, 195, 26, 201, 251, 87, 172, 17, 108, 168, 101, 136, 252, 118, + 147, 242, 33, 11, 16, 200, 158, 124, 254, 165, 5, 73, 12, 53, 221, 137, 122, 171, 78, 203, 75, + 123, 4, 181, 50, 42, 37, 32, 13, 234, 176, 114, 190, 190, 94, 157, 23, 32, 1, 226, 90, 177, 34, + 62, 201, 180, 249, 9, 215, 203, 139, 66, 88, 169, 207, 196, 116, 47, 36, 127, 53, 131, 194, 0, + 71, 133, 68, 125, 203, 156, 32, 5, 84, 147, 243, 72, 44, 231, 137, 95, 82, 224, 204, 154, 206, + 188, 12, 47, 68, 76, 47, 63, 17, 175, 206, 61, 111, 0, 64, 60, 146, 23, 95, 32, 5, 84, 147, + 243, 72, 44, 231, 137, 95, 82, 224, 204, 154, 206, 188, 12, 47, 68, 76, 47, 63, 17, 175, 206, + 61, 111, 0, 64, 60, 146, 23, 95, 32, 5, 84, 147, 243, 72, 44, 231, 137, 95, 82, 224, 204, 154, + 206, 188, 12, 47, 68, 76, 47, 63, 17, 175, 206, 61, 111, 0, 64, 60, 146, 23, 95, 32, 3, 90, + 112, 38, 248, 174, 225, 80, 47, 99, 208, 220, 166, 201, 154, 71, 62, 141, 42, 104, 172, 207, + 250, 140, 201, 249, 74, 88, 26, 166, 127, 212, 32, 5, 134, 245, 233, 202, 185, 178, 113, 74, + 59, 208, 205, 65, 8, 236, 118, 243, 40, 83, 202, 162, 159, 83, 0, 191, 255, 58, 82, 220, 255, + 23, 29, 32, 6, 227, 171, 139, 219, 139, 164, 90, 41, 98, 199, 188, 177, 9, 186, 202, 145, 64, + 5, 126, 143, 190, 146, 169, 7, 143, 169, 218, 35, 68, 235, 128, 32, 0, 130, 168, 205, 202, 184, + 230, 169, 214, 27, 165, 47, 104, 18, 54, 173, 167, 144, 26, 11, 42, 182, 168, 131, 255, 193, + 120, 81, 92, 85, 21, 9, 32, 0, 198, 81, 52, 192, 76, 178, 36, 188, 172, 159, 24, 73, 243, 74, + 123, 37, 10, 41, 255, 32, 109, 116, 92, 255, 49, 76, 67, 28, 250, 217, 188, 32, 1, 236, 236, + 188, 57, 240, 250, 49, 136, 157, 51, 197, 131, 190, 82, 109, 93, 176, 233, 189, 132, 124, 15, + 150, 83, 200, 212, 101, 116, 234, 125, 71, 32, 2, 65, 199, 238, 237, 253, 238, 0, 191, 87, 58, + 174, 152, 1, 151, 187, 90, 87, 125, 195, 41, 103, 49, 189, 18, 125, 62, 190, 24, 62, 181, 83, + 32, 4, 198, 36, 193, 246, 195, 182, 251, 63, 57, 13, 176, 164, 25, 122, 253, 188, 150, 158, + 106, 136, 241, 239, 75, 249, 173, 62, 56, 58, 118, 253, 98, 32, 7, 152, 239, 4, 75, 94, 140, + 52, 205, 198, 243, 9, 254, 146, 160, 50, 36, 61, 193, 169, 85, 180, 218, 212, 66, 18, 232, 128, + 75, 217, 49, 104, 32, 4, 150, 204, 12, 168, 216, 22, 200, 19, 209, 117, 116, 14, 1, 6, 73, 199, + 148, 170, 40, 201, 65, 239, 108, 97, 54, 161, 208, 73, 96, 40, 77, 32, 0, 89, 1, 107, 47, 187, + 250, 147, 133, 134, 49, 97, 130, 198, 199, 203, 218, 83, 147, 96, 23, 159, 218, 246, 198, 194, + 81, 13, 28, 75, 53, 47, 32, 1, 28, 219, 187, 19, 102, 9, 144, 68, 190, 184, 76, 169, 190, 127, + 131, 71, 71, 99, 157, 255, 174, 167, 193, 86, 169, 29, 171, 160, 168, 87, 20, 32, 4, 220, 160, + 34, 62, 11, 188, 248, 175, 118, 70, 24, 125, 79, 86, 37, 114, 155, 49, 100, 78, 8, 48, 75, 159, + 236, 21, 197, 193, 244, 118, 250, 32, 3, 55, 131, 240, 131, 7, 226, 70, 14, 219, 44, 30, 186, + 18, 176, 152, 179, 224, 211, 175, 71, 201, 115, 172, 16, 8, 196, 103, 206, 249, 150, 82, 32, 1, + 77, 168, 185, 25, 164, 38, 14, 193, 137, 113, 154, 54, 217, 60, 156, 38, 138, 122, 105, 254, + 17, 102, 164, 221, 43, 37, 170, 221, 72, 232, 151, 32, 3, 37, 118, 187, 49, 235, 31, 41, 121, + 145, 157, 107, 21, 174, 81, 106, 253, 208, 11, 111, 44, 169, 123, 199, 86, 247, 34, 146, 159, + 78, 78, 134, 32, 6, 25, 172, 62, 68, 76, 1, 151, 154, 104, 86, 6, 130, 165, 106, 33, 176, 185, + 58, 216, 16, 206, 236, 152, 171, 90, 91, 42, 229, 248, 62, 237, 32, 4, 6, 28, 244, 157, 110, + 212, 16, 249, 146, 113, 14, 89, 246, 188, 124, 152, 104, 62, 116, 143, 17, 229, 77, 42, 21, 54, + 36, 127, 143, 46, 180, 32, 6, 213, 30, 201, 15, 161, 166, 224, 201, 122, 48, 53, 149, 96, 216, + 206, 129, 31, 134, 225, 26, 111, 0, 127, 54, 100, 16, 33, 24, 239, 169, 118, 23, 32, 6, 5, 152, + 2, 28, 99, 252, 217, 4, 193, 210, 48, 97, 240, 47, 63, 19, 117, 140, 245, 109, 151, 204, 220, + 129, 39, 253, 49, 242, 168, 120, 151, 32, 5, 174, 60, 34, 214, 147, 223, 113, 120, 155, 177, + 195, 231, 225, 156, 129, 231, 208, 52, 152, 129, 190, 176, 115, 224, 38, 54, 197, 65, 184, 142, + 232, 32, 5, 174, 60, 34, 214, 147, 223, 113, 120, 155, 177, 195, 231, 225, 156, 129, 231, 208, + 52, 152, 129, 190, 176, 115, 224, 38, 54, 197, 65, 184, 142, 232, 32, 5, 174, 60, 34, 214, 147, + 223, 113, 120, 155, 177, 195, 231, 225, 156, 129, 231, 208, 52, 152, 129, 190, 176, 115, 224, + 38, 54, 197, 65, 184, 142, 232, 32, 7, 121, 221, 240, 129, 105, 88, 243, 253, 201, 60, 90, 139, + 168, 81, 179, 130, 76, 9, 95, 160, 104, 0, 244, 217, 146, 66, 101, 158, 81, 168, 54, 32, 3, + 213, 73, 227, 177, 207, 252, 173, 28, 118, 100, 147, 55, 66, 251, 107, 88, 24, 5, 238, 106, 72, + 82, 218, 80, 172, 99, 216, 235, 159, 151, 50, 32, 7, 166, 82, 123, 107, 43, 164, 75, 210, 180, + 109, 36, 0, 35, 152, 37, 8, 141, 249, 252, 44, 138, 67, 152, 2, 224, 75, 38, 106, 110, 253, + 236, 32, 4, 41, 74, 94, 251, 100, 113, 67, 170, 6, 100, 54, 157, 118, 217, 101, 75, 145, 69, + 14, 165, 142, 150, 236, 107, 5, 130, 59, 182, 189, 116, 1, 32, 7, 53, 87, 13, 68, 248, 17, 250, + 175, 182, 241, 183, 220, 59, 172, 179, 118, 165, 111, 58, 136, 7, 230, 99, 169, 178, 182, 245, + 120, 169, 158, 50, 32, 2, 86, 206, 226, 81, 43, 2, 142, 153, 222, 85, 106, 197, 123, 159, 24, + 79, 55, 128, 92, 124, 80, 81, 214, 171, 62, 227, 253, 77, 143, 102, 42, 32, 0, 71, 65, 188, + 141, 114, 57, 192, 29, 15, 199, 3, 116, 181, 84, 185, 134, 76, 5, 81, 13, 253, 171, 180, 121, + 221, 223, 175, 88, 166, 11, 196, 32, 6, 57, 225, 140, 207, 235, 77, 232, 93, 0, 172, 133, 213, + 23, 104, 96, 146, 118, 242, 175, 33, 244, 78, 83, 99, 173, 165, 213, 204, 30, 253, 95, 32, 4, + 83, 171, 84, 96, 90, 141, 133, 240, 109, 239, 84, 166, 217, 236, 205, 142, 191, 73, 197, 45, + 188, 207, 222, 60, 225, 187, 184, 238, 102, 84, 103, 32, 0, 248, 88, 98, 94, 207, 141, 75, 237, + 61, 234, 162, 40, 176, 186, 22, 52, 93, 138, 100, 151, 157, 182, 64, 247, 146, 176, 157, 203, + 35, 208, 185, 32, 6, 178, 43, 190, 231, 177, 156, 59, 89, 32, 7, 140, 237, 213, 218, 119, 18, + 186, 119, 179, 205, 10, 76, 209, 60, 72, 173, 79, 129, 116, 34, 212, 32, 7, 232, 242, 81, 73, + 173, 219, 223, 87, 6, 238, 88, 194, 11, 217, 198, 236, 17, 49, 50, 99, 140, 203, 66, 62, 38, + 52, 62, 34, 39, 73, 208, 32, 7, 164, 100, 246, 243, 192, 242, 11, 3, 187, 235, 185, 48, 141, + 142, 105, 234, 220, 67, 207, 112, 68, 78, 196, 178, 224, 209, 7, 183, 12, 184, 178, 32, 7, 134, + 217, 46, 19, 216, 242, 5, 148, 52, 225, 134, 144, 179, 143, 210, 3, 36, 53, 103, 68, 180, 1, + 170, 39, 27, 89, 253, 40, 180, 186, 55, 32, 2, 51, 50, 203, 226, 167, 22, 7, 220, 184, 62, 60, + 166, 91, 148, 101, 64, 96, 252, 158, 211, 27, 103, 227, 108, 209, 86, 179, 120, 142, 108, 125, + 32, 0, 214, 90, 141, 11, 83, 126, 234, 160, 200, 182, 133, 217, 156, 187, 122, 199, 33, 63, + 147, 194, 184, 189, 155, 189, 176, 50, 183, 77, 239, 118, 65, 32, 2, 220, 62, 108, 245, 46, + 101, 64, 164, 180, 143, 208, 135, 122, 178, 73, 13, 227, 145, 18, 129, 86, 111, 249, 131, 202, + 162, 107, 222, 148, 145, 90, 32, 0, 220, 193, 44, 213, 8, 251, 223, 112, 251, 65, 71, 90, 159, + 20, 177, 98, 21, 184, 150, 66, 54, 11, 248, 231, 48, 118, 114, 29, 66, 143, 26, 32, 4, 89, 205, + 230, 246, 77, 248, 106, 59, 250, 124, 66, 250, 146, 104, 183, 83, 64, 79, 182, 76, 27, 7, 149, + 119, 76, 145, 118, 129, 21, 32, 98, 1, 1, 32, 7, 255, 255, 255, 255, 255, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 252, 193, 32, 6, 47, 252, 208, 5, 81, 69, 218, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 246, 251, 47, 255, 208, 0, 80, 19, 43, 32, 7, 255, 255, 255, - 255, 255, 207, 48, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 253, 33, 32, 5, 176, 7, 112, 9, 145, 69, 235, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 246, 250, 176, 0, 112, 0, 144, 19, - 44, 32, 7, 255, 255, 255, 255, 255, 192, 80, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 252, 65, 32, 7, 144, 1, 16, 1, 16, - 151, 69, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 238, - 144, 0, 16, 0, 16, 8, 230, 32, 7, 255, 255, 255, 255, 255, 234, 208, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 193, - 32, 7, 255, 255, 255, 255, 255, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 225, 32, 7, 255, 255, 255, - 255, 255, 222, 16, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 254, 1, 32, 7, 255, 255, 255, 255, 255, 228, 112, 255, 255, + 255, 255, 225, 32, 7, 255, 255, 255, 255, 255, 185, 240, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 225, 32, 7, + 255, 255, 255, 255, 255, 185, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 225, 32, 7, 255, 255, 255, 255, 255, + 245, 112, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 97, 32, 7, 255, 255, 255, 255, 255, 103, 16, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 247, + 1, 1, 251, 251, 127, 1, 251, 1, 128, 2, 2, 1, 5, 3, 33, 72, 32, 32, 7, 255, 255, 255, 255, 255, + 190, 48, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 252, 33, 32, 7, 255, 255, 255, 255, 255, 185, 240, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, + 225, 32, 7, 255, 255, 255, 255, 255, 239, 16, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 32, 7, 255, 255, 255, 255, + 255, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 225, 32, 7, 255, 255, 255, 255, 255, 213, 144, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 254, 97, 32, 7, 255, 255, 255, 255, 255, 224, 48, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 33, 32, 2, 47, 252, - 208, 1, 17, 35, 65, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 247, 255, 47, 255, 208, 0, 16, 17, 34, 32, 7, 255, 255, 255, 255, 255, 247, 144, 255, 255, 255, + 255, 253, 129, 32, 1, 176, 7, 112, 1, 17, 69, 82, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 246, 254, 176, 0, 112, 0, 16, 19, 35, 32, 7, 255, 255, 255, 255, + 255, 194, 112, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 252, 97, 32, 0, 0, 0, 0, 0, 0, 21, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 64, 32, 7, 255, 255, 255, 255, 255, 249, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 129, 32, 7, 255, 255, 255, 255, 255, 247, 144, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 129, 32, 7, 255, - 255, 255, 255, 255, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 225, 32, 0, 15, 252, 208, 1, 16, 20, 81, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127, 15, 255, 208, - 0, 16, 1, 50, 32, 7, 255, 255, 255, 255, 255, 243, 80, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 65, 32, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, - 255, 255, 255, 255, 232, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 161, 32, 2, 47, 252, 208, 1, 17, 69, 65, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 246, 255, 47, 255, 208, - 0, 16, 19, 34, 32, 7, 255, 255, 255, 255, 255, 205, 16, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 1, 32, 1, 176, 7, - 112, 1, 17, 69, 82, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 246, 254, 176, 0, 112, 0, 16, 19, 35, 32, 7, 255, 255, 255, 255, 255, 211, 112, 255, 255, 255, + 255, 255, 255, 161, 32, 6, 111, 252, 207, 252, 208, 76, 229, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 253, 223, 111, 255, 207, 255, 208, 4, 134, 32, 7, 255, + 255, 255, 255, 255, 198, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 252, 161, 32, 0, 0, 0, 0, 0, 0, 2, 32, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 32, 7, 255, 255, 255, 255, 255, + 196, 144, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 252, 129, 32, 6, 111, 252, 207, 252, 208, 76, 229, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 223, 111, 255, 207, 255, 208, 4, + 134, 32, 7, 255, 255, 255, 255, 255, 219, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 225, 32, 7, 144, 1, 16, + 1, 16, 151, 69, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, + 238, 144, 0, 16, 0, 16, 8, 230, 32, 7, 255, 255, 255, 255, 255, 202, 240, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 252, + 225, 32, 0, 176, 3, 48, 1, 17, 69, 116, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 246, 253, 176, 0, 48, 0, 16, 19, 37, 32, 7, 255, 255, 255, 255, 255, 241, + 48, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 33, 32, 2, 47, 252, 208, 1, 17, 69, 65, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 246, 255, 47, 255, 208, 0, 16, 19, 34, 32, 7, 255, + 255, 255, 255, 255, 245, 112, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 97, 32, 0, 15, 252, 208, 1, 16, 6, 197, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 223, 15, 255, 208, + 0, 16, 0, 102, 32, 7, 255, 255, 255, 255, 255, 230, 144, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 129, 32, 7, + 255, 255, 255, 255, 255, 245, 112, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 97, 32, 7, 255, 255, 255, 255, 255, 200, + 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 252, 193, 32, 6, 47, 252, 208, 5, 81, 69, 218, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 246, 251, 47, 255, 208, 0, 80, 19, 43, 32, 7, + 255, 255, 255, 255, 255, 207, 48, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 33, 32, 5, 176, 7, 112, 9, 145, 69, 235, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 246, 250, 176, 0, + 112, 0, 144, 19, 44, 32, 7, 255, 255, 255, 255, 255, 192, 80, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 252, 65, 32, 7, + 144, 1, 16, 1, 16, 151, 69, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 251, 238, 144, 0, 16, 0, 16, 8, 230, 32, 7, 255, 255, 255, 255, 255, 234, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 253, 97, 32, 1, 176, 7, 112, 1, 17, 69, 82, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 246, 254, 176, 0, 112, 0, 16, 19, 35, 32, 7, 255, 255, 255, 255, 255, + 255, 255, 254, 193, 32, 7, 255, 255, 255, 255, 255, 253, 240, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 225, 32, + 7, 255, 255, 255, 255, 255, 222, 16, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 1, 32, 7, 255, 255, 255, 255, 255, 228, 112, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 254, 97, 32, 6, 111, 252, 207, 252, 208, 76, 229, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 223, 111, 255, 207, 255, 208, 4, - 134, 32, 7, 255, 255, 255, 255, 255, 236, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 225, 32, 2, 47, 252, 208, + 255, 255, 255, 255, 255, 254, 97, 32, 7, 255, 255, 255, 255, 255, 224, 48, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, + 33, 32, 2, 47, 252, 208, 1, 17, 35, 65, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 247, 255, 47, 255, 208, 0, 16, 17, 34, 32, 7, 255, 255, 255, 255, 255, 247, + 144, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 129, 32, 7, 255, 255, 255, 255, 255, 247, 144, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 129, 32, 7, 255, 255, 255, 255, 255, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 225, 32, 0, 15, 252, 208, + 1, 16, 20, 81, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 127, 15, 255, 208, 0, 16, 1, 50, 32, 7, 255, 255, 255, 255, 255, 243, 80, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 65, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 32, 7, 255, 255, 255, 255, 255, 232, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 161, 32, 2, 47, 252, 208, 1, 17, 69, 65, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 246, + 255, 47, 255, 208, 0, 16, 19, 34, 32, 7, 255, 255, 255, 255, 255, 205, 16, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, + 1, 32, 1, 176, 7, 112, 1, 17, 69, 82, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 246, 254, 176, 0, 112, 0, 16, 19, 35, 32, 7, 255, 255, 255, 255, 255, 211, + 112, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 253, 97, 32, 1, 176, 7, 112, 1, 17, 69, 82, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 246, 254, 176, 0, 112, 0, 16, 19, 35, 32, 7, 255, + 255, 255, 255, 255, 188, 16, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 252, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, 255, 255, 255, 228, + 112, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 254, 97, 32, 6, 111, 252, 207, 252, 208, 76, 229, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 223, 111, 255, 207, 255, 208, 4, 134, + 32, 7, 255, 255, 255, 255, 255, 236, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 225, 32, 2, 47, 252, 208, 1, + 17, 69, 65, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 246, 255, 47, 255, 208, 0, 16, 19, 34, 32, 7, 255, 255, 255, 255, 255, 226, 80, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 65, 32, 7, 255, 255, 255, 255, 255, 245, 112, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, @@ -1234,5 +1236,5 @@ static PROOF: [u8; 25396] = [ 234, 32, 7, 255, 255, 255, 255, 255, 215, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 161, 32, 7, 255, 255, 255, 255, 255, 245, 112, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 97, 64, 30, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 97, 64, ]; diff --git a/provers/stark/src/proof/stark.rs b/provers/stark/src/proof/stark.rs index 4a0a57ddb0..cabb193232 100644 --- a/provers/stark/src/proof/stark.rs +++ b/provers/stark/src/proof/stark.rs @@ -3,8 +3,9 @@ use std::collections::{BTreeSet, HashMap, HashSet}; use lambdaworks_crypto::merkle_tree::proof::Proof; use lambdaworks_math::{ field::{ - element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - traits::IsField, + element::FieldElement, + fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, + traits::{IsField, IsSubFieldOf}, }, traits::Serializable, }; @@ -22,38 +23,47 @@ use crate::{ use super::options::ProofOptions; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub struct DeepPolynomialOpening { - pub lde_composition_poly_proof: Proof, - pub lde_composition_poly_parts_evaluation: Vec>, - pub lde_trace_merkle_proofs: Vec>, - pub lde_trace_evaluations: Vec>, +pub struct PolynomialOpenings { + pub proof: Proof, + pub proof_sym: Proof, + pub evaluations: Vec>, + pub evaluations_sym: Vec>, } -pub type DeepPolynomialOpenings = Vec>; +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct DeepPolynomialOpening, E: IsField> { + pub composition_poly: PolynomialOpenings, + pub main_trace_polys: PolynomialOpenings, + pub aux_trace_polys: Option>, +} + +pub type DeepPolynomialOpenings = Vec>; #[derive(Debug, serde::Serialize, serde::Deserialize)] -pub struct StarkProof { +pub struct StarkProof, E: IsField> { // Length of the execution trace pub trace_length: usize, // Commitments of the trace columns // [tβ±Ό] - pub lde_trace_merkle_roots: Vec, + pub lde_trace_main_merkle_root: Commitment, + // Commitments of auxiliary trace columns + // [tβ±Ό] + pub lde_trace_aux_merkle_root: Option, // tβ±Ό(zgᡏ) - pub trace_ood_evaluations: Table, + pub trace_ood_evaluations: Table, // Commitments to Hα΅’ pub composition_poly_root: Commitment, // Hα΅’(z^N) - pub composition_poly_parts_ood_evaluation: Vec>, + pub composition_poly_parts_ood_evaluation: Vec>, // [pβ‚–] pub fri_layers_merkle_roots: Vec, // pβ‚™ - pub fri_last_value: FieldElement, + pub fri_last_value: FieldElement, // Open(pβ‚–(Dβ‚–), βˆ’πœβ‚›^(2ᡏ)) - pub query_list: Vec>, + pub query_list: Vec>, // Open(H₁(D_LDE, 𝜐ᡒ), Open(Hβ‚‚(D_LDE, 𝜐ᡒ), Open(tβ±Ό(D_LDE), 𝜐ᡒ) - pub deep_poly_openings: DeepPolynomialOpenings, // Open(H₁(D_LDE, -𝜐ᡒ), Open(Hβ‚‚(D_LDE, -𝜐ᡒ), Open(tβ±Ό(D_LDE), -𝜐ᡒ) - pub deep_poly_openings_sym: DeepPolynomialOpenings, + pub deep_poly_openings: DeepPolynomialOpenings, // nonce obtained from grinding pub nonce: Option, } @@ -64,7 +74,7 @@ pub struct StoneCompatibleSerializer; impl StoneCompatibleSerializer { pub fn serialize_proof( - proof: &StarkProof, + proof: &StarkProof, public_inputs: &A::PublicInputs, options: &ProofOptions, ) -> Vec @@ -89,20 +99,20 @@ impl StoneCompatibleSerializer { /// Appends the root bytes of the Merkle tree for the main trace, and if there is a RAP round, /// it also appends the root bytes of the Merkle tree for the extended columns. - fn append_trace_commitment(proof: &StarkProof, output: &mut Vec) { - output.extend_from_slice( - &proof - .lde_trace_merkle_roots - .iter() - .flatten() - .cloned() - .collect::>(), - ); + fn append_trace_commitment( + proof: &StarkProof, + output: &mut Vec, + ) { + output.extend_from_slice(&proof.lde_trace_main_merkle_root); + + if let Some(lde_trace_aux_merkle_root) = proof.lde_trace_aux_merkle_root { + output.extend_from_slice(&lde_trace_aux_merkle_root); + } } /// Appends the root bytes of the Merkle tree for the composition polynomial. fn append_composition_polynomial_commitment( - proof: &StarkProof, + proof: &StarkProof, output: &mut Vec, ) { output.extend_from_slice(&proof.composition_poly_root); @@ -118,7 +128,7 @@ impl StoneCompatibleSerializer { /// /// Here, K is the length of the frame size. fn append_out_of_domain_evaluations( - proof: &StarkProof, + proof: &StarkProof, output: &mut Vec, ) { for i in 0..proof.trace_ood_evaluations.width { @@ -134,7 +144,7 @@ impl StoneCompatibleSerializer { /// Appends the commitments to the inner layers of FRI followed by the element of the last layer. fn append_fri_commit_phase_commitments( - proof: &StarkProof, + proof: &StarkProof, output: &mut Vec, ) { output.extend_from_slice( @@ -151,7 +161,10 @@ impl StoneCompatibleSerializer { /// Appends the proof of work nonce in case there is one. There could be none if the `grinding_factor` /// was set to 0 during proof generation. In that case nothing is appended. - fn append_proof_of_work_nonce(proof: &StarkProof, output: &mut Vec) { + fn append_proof_of_work_nonce( + proof: &StarkProof, + output: &mut Vec, + ) { if let Some(nonce_value) = proof.nonce { output.extend_from_slice(&nonce_value.to_be_bytes()); } @@ -163,10 +176,10 @@ impl StoneCompatibleSerializer { /// Each FRI query index `i` determines a pair of elements `d_i` and `-d_i` on the domain of the /// first layer. /// Let BT_i be the concatenation of the bytes of the following values - /// t_1(d_i), t_1(-d_i), t_2(d_i), t_2(-d_i), ..., t_m(d_i), t_m(-d_i), + /// t_1(d_i), t_2(d_i), ..., t_m(d_i), t_1(-d_i), t_2(-d_i), ..., t_m(-d_i), /// where m is the total number of columns, including RAP extended ones. /// Similarly, let BH_i be the concatenation of the bytes of the following elements - /// H_1(d_i), H_1(-d_i), ..., H_s(d_i), H_s(-d_i), + /// H_1(d_i), ..., H_s(d_i), H_1(-d_i), ..., H_s(-d_i), /// where s is the number of parts into which the composition polynomial was broken. /// /// If i_1, ..., i_k are all the FRI query indexes sorted in increasing order and without repeated @@ -183,14 +196,13 @@ impl StoneCompatibleSerializer { /// following to the output: /// `BT_1 | BT_2 | BT_3 | BT_5 | TraceMergedPaths | BH_1 | BH_2 | BH_3 | BH_5 | CompositionMergedPaths` fn append_fri_query_phase_first_layer( - proof: &StarkProof, + proof: &StarkProof, fri_query_indexes: &[usize], output: &mut Vec, ) { let mut fri_first_layer_openings: Vec<_> = proof .deep_poly_openings .iter() - .zip(proof.deep_poly_openings_sym.iter()) .zip(fri_query_indexes.iter()) .collect(); // Remove repeated values @@ -200,13 +212,24 @@ impl StoneCompatibleSerializer { fri_first_layer_openings.sort_by(|a, b| a.1.cmp(b.1)); // Append BT_{i_1} | BT_{i_2} | ... | BT_{i_k} - for ((opening, opening_sym), _) in fri_first_layer_openings.iter() { - for elem in opening.lde_trace_evaluations.iter() { + for (opening, _) in fri_first_layer_openings.iter() { + for elem in opening.main_trace_polys.evaluations.iter() { output.extend_from_slice(&elem.serialize()); } - for elem in opening_sym.lde_trace_evaluations.iter() { + if let Some(aux) = &opening.aux_trace_polys { + for elem in aux.evaluations.iter() { + output.extend_from_slice(&elem.serialize()); + } + } + + for elem in opening.main_trace_polys.evaluations_sym.iter() { output.extend_from_slice(&elem.serialize()); } + if let Some(aux) = &opening.aux_trace_polys { + for elem in aux.evaluations_sym.iter() { + output.extend_from_slice(&elem.serialize()); + } + } } let fri_trace_query_indexes: Vec<_> = fri_query_indexes @@ -215,18 +238,34 @@ impl StoneCompatibleSerializer { .collect(); // Append TraceMergedPaths - for i in 0..proof.deep_poly_openings[0].lde_trace_merkle_proofs.len() { - let fri_trace_paths: Vec<_> = proof - .deep_poly_openings - .iter() - .zip(proof.deep_poly_openings_sym.iter()) - .flat_map(|(opening, opening_sym)| { - vec![ - &opening.lde_trace_merkle_proofs[i], - &opening_sym.lde_trace_merkle_proofs[i], - ] - }) - .collect(); + // Main trace + let fri_trace_paths: Vec<_> = proof + .deep_poly_openings + .iter() + .flat_map(|opening| { + vec![ + &opening.main_trace_polys.proof, + &opening.main_trace_polys.proof_sym, + ] + }) + .collect(); + let nodes = Self::merge_authentication_paths(&fri_trace_paths, &fri_trace_query_indexes); + for node in nodes.iter() { + output.extend_from_slice(node); + } + + // Aux trace + let mut all_openings_aux_trace_polys_are_some = true; + let mut fri_trace_paths: Vec<&Proof> = Vec::new(); + for opening in proof.deep_poly_openings.iter() { + if let Some(aux_trace_polys) = &opening.aux_trace_polys { + fri_trace_paths.push(&aux_trace_polys.proof); + fri_trace_paths.push(&aux_trace_polys.proof_sym); + } else { + all_openings_aux_trace_polys_are_some = false; + } + } + if all_openings_aux_trace_polys_are_some { let nodes = Self::merge_authentication_paths(&fri_trace_paths, &fri_trace_query_indexes); for node in nodes.iter() { @@ -235,11 +274,11 @@ impl StoneCompatibleSerializer { } // Append BH_{i_1} | BH_{i_2} | ... | B_{i_k} - for ((opening, opening_sym), _) in fri_first_layer_openings.iter() { - for elem in opening.lde_composition_poly_parts_evaluation.iter() { + for (opening, _) in fri_first_layer_openings.iter() { + for elem in opening.composition_poly.evaluations.iter() { output.extend_from_slice(&elem.serialize()); } - for elem in opening_sym.lde_composition_poly_parts_evaluation.iter() { + for elem in opening.composition_poly.evaluations_sym.iter() { output.extend_from_slice(&elem.serialize()); } } @@ -248,7 +287,7 @@ impl StoneCompatibleSerializer { let fri_composition_paths: Vec<_> = proof .deep_poly_openings .iter() - .map(|opening| &opening.lde_composition_poly_proof) + .map(|opening| &opening.composition_poly.proof) .collect(); let nodes = Self::merge_authentication_paths(&fri_composition_paths, fri_query_indexes); for node in nodes.iter() { @@ -278,7 +317,7 @@ impl StoneCompatibleSerializer { /// /// where n is the total number of FRI layers. fn append_fri_query_phase_inner_layers( - proof: &StarkProof, + proof: &StarkProof, fri_query_indexes: &[usize], output: &mut Vec, ) { @@ -404,7 +443,7 @@ impl StoneCompatibleSerializer { result } fn get_fri_query_indexes( - proof: &StarkProof, + proof: &StarkProof, public_inputs: &A::PublicInputs, proof_options: &ProofOptions, ) -> Vec @@ -454,7 +493,7 @@ mod tests { claimed_index, }; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, @@ -532,7 +571,7 @@ mod tests { claimed_index, }; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, @@ -624,7 +663,7 @@ mod tests { claimed_index, }; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, @@ -888,7 +927,7 @@ mod tests { claimed_index, }; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, @@ -966,7 +1005,7 @@ mod tests { claimed_index, }; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, diff --git a/provers/stark/src/prover.rs b/provers/stark/src/prover.rs index ac275fd761..7c68c01f75 100644 --- a/provers/stark/src/prover.rs +++ b/provers/stark/src/prover.rs @@ -1,10 +1,10 @@ +use std::marker::PhantomData; #[cfg(feature = "instruments")] use std::time::Instant; -use lambdaworks_crypto::merkle_tree::proof::Proof; use lambdaworks_math::fft::cpu::bit_reversing::{in_place_bit_reverse_permute, reverse_index}; use lambdaworks_math::fft::errors::FFTError; -use lambdaworks_math::field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField; + use lambdaworks_math::field::traits::{IsField, IsSubFieldOf}; use lambdaworks_math::traits::Serializable; use lambdaworks_math::{ @@ -19,7 +19,7 @@ use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelI #[cfg(debug_assertions)] use crate::debug::validate_trace; use crate::fri; -use crate::proof::stark::DeepPolynomialOpenings; +use crate::proof::stark::{DeepPolynomialOpenings, PolynomialOpenings}; use crate::table::Table; use crate::transcript::IsStarkTranscript; @@ -33,30 +33,77 @@ use super::proof::stark::{DeepPolynomialOpening, StarkProof}; use super::trace::TraceTable; use super::traits::AIR; -pub struct Prover; - -impl IsStarkProver for Prover { - type Field = Stark252PrimeField; - type FieldExtension = Stark252PrimeField; +pub struct Prover { + phantom: PhantomData, } +impl IsStarkProver for Prover {} + #[derive(Debug)] pub enum ProvingError { WrongParameter(String), } +pub struct Round1CommitmentData +where + F: IsField, + FieldElement: Serializable + Send + Sync, +{ + pub(crate) trace_polys: Vec>>, + pub(crate) lde_trace: TraceTable, + pub(crate) lde_trace_merkle_tree: BatchedMerkleTree, + pub(crate) lde_trace_merkle_root: Commitment, +} + pub struct Round1 where A: AIR, FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { - pub(crate) trace_polys: Vec>>, - pub(crate) lde_trace: TraceTable, - pub(crate) lde_trace_merkle_trees: Vec>, - pub(crate) lde_trace_merkle_roots: Vec, + pub(crate) main: Round1CommitmentData, + pub(crate) aux: Option>, pub(crate) rap_challenges: A::RAPChallenges, } +impl Round1 +where + A: AIR, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, +{ + fn all_trace_polys(&self) -> Vec>> { + let mut trace_polys: Vec<_> = self + .main + .trace_polys + .clone() + .into_iter() + .map(|poly| poly.to_extension()) + .collect(); + + if let Some(aux) = &self.aux { + trace_polys.extend_from_slice(&aux.trace_polys.to_owned()) + } + trace_polys + } + + fn all_evaluations(&self) -> TraceTable { + let mut evaluations: Vec>> = self + .main + .lde_trace + .columns() + .clone() + .into_iter() + .map(|col| col.into_iter().map(|x| x.to_extension()).collect()) + .collect(); + + if let Some(aux) = &self.aux { + evaluations.extend_from_slice(&aux.lde_trace.columns()) + } + + TraceTable::from_columns(evaluations, A::STEP_SIZE) + } +} pub struct Round2 where F: IsField, @@ -73,12 +120,11 @@ pub struct Round3 { composition_poly_parts_ood_evaluation: Vec>, } -pub struct Round4 { - fri_last_value: FieldElement, +pub struct Round4, E: IsField> { + fri_last_value: FieldElement, fri_layers_merkle_roots: Vec, - deep_poly_openings: DeepPolynomialOpenings, - deep_poly_openings_sym: DeepPolynomialOpenings, - query_list: Vec>, + deep_poly_openings: DeepPolynomialOpenings, + query_list: Vec>, nonce: Option, } pub fn evaluate_polynomial_on_lde_domain( @@ -99,39 +145,39 @@ where } } -pub trait IsStarkProver { - type Field: IsFFTField + IsSubFieldOf; - type FieldExtension: IsField; - - fn batch_commit( - vectors: &[Vec>], - ) -> (BatchedMerkleTree, Commitment) +pub trait IsStarkProver { + fn batch_commit(vectors: &[Vec>]) -> (BatchedMerkleTree, Commitment) where - FieldElement: Serializable + Sync + Send, - FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, + E: IsSubFieldOf, + A::Field: IsSubFieldOf, { - let tree = BatchedMerkleTree::::build(vectors); + let tree = BatchedMerkleTree::::build(vectors); let commitment = tree.root; (tree, commitment) } #[allow(clippy::type_complexity)] - fn interpolate_and_commit( - trace: &TraceTable, - domain: &Domain, - transcript: &mut impl IsStarkTranscript, + fn interpolate_and_commit( + trace: &TraceTable, + domain: &Domain, + transcript: &mut impl IsStarkTranscript, ) -> ( - Vec>>, - Vec>>, - BatchedMerkleTree, + Vec>>, + Vec>>, + BatchedMerkleTree, Commitment, ) where - A: AIR, - FieldElement: Serializable + Send + Sync, - FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, + E: IsSubFieldOf, + A::Field: IsSubFieldOf, { - let trace_polys = trace.compute_trace_polys::(); + let trace_polys = trace.compute_trace_polys::(); // Evaluate those polynomials t_j on the large domain D_LDE. let lde_trace_evaluations = Self::compute_lde_trace_evaluations(&trace_polys, domain); @@ -157,13 +203,15 @@ pub trait IsStarkProver { ) } - fn compute_lde_trace_evaluations( - trace_polys: &[Polynomial>], - domain: &Domain, - ) -> Vec>> + fn compute_lde_trace_evaluations( + trace_polys: &[Polynomial>], + domain: &Domain, + ) -> Vec>> where - FieldElement: Send + Sync, - FieldElement: Send + Sync, + FieldElement: Send + Sync, + FieldElement: Send + Sync, + E: IsSubFieldOf, + A::Field: IsSubFieldOf, { #[cfg(not(feature = "parallel"))] let trace_polys_iter = trace_polys.iter(); @@ -179,57 +227,60 @@ pub trait IsStarkProver { &domain.coset_offset, ) }) - .collect::>>, FFTError>>() + .collect::>>, FFTError>>() .unwrap() } - fn round_1_randomized_air_with_preprocessing( + fn round_1_randomized_air_with_preprocessing( air: &A, - main_trace: &TraceTable, - domain: &Domain, - transcript: &mut impl IsStarkTranscript, + main_trace: &TraceTable, + domain: &Domain, + transcript: &mut impl IsStarkTranscript, ) -> Result, ProvingError> where - A: AIR, - FieldElement: Serializable + Send + Sync, - FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, { - let (mut trace_polys, mut evaluations, main_merkle_tree, main_merkle_root) = - Self::interpolate_and_commit::(main_trace, domain, transcript); + let (trace_polys, evaluations, main_merkle_tree, main_merkle_root) = + Self::interpolate_and_commit::(main_trace, domain, transcript); + + let main = Round1CommitmentData:: { + trace_polys, + lde_trace: TraceTable::from_columns(evaluations, A::STEP_SIZE), + lde_trace_merkle_tree: main_merkle_tree, + lde_trace_merkle_root: main_merkle_root, + }; let rap_challenges = air.build_rap_challenges(transcript); let aux_trace = air.build_auxiliary_trace(main_trace, &rap_challenges); - - let mut lde_trace_merkle_trees = vec![main_merkle_tree]; - let mut lde_trace_merkle_roots = vec![main_merkle_root]; - if !aux_trace.is_empty() { + let aux = if !aux_trace.is_empty() { // Check that this is valid for interpolation let (aux_trace_polys, aux_trace_polys_evaluations, aux_merkle_tree, aux_merkle_root) = - Self::interpolate_and_commit::(&aux_trace, domain, transcript); - trace_polys.extend_from_slice(&aux_trace_polys); - evaluations.extend_from_slice(&aux_trace_polys_evaluations); - lde_trace_merkle_trees.push(aux_merkle_tree); - lde_trace_merkle_roots.push(aux_merkle_root); - } - - let lde_trace = TraceTable::from_columns(evaluations, A::STEP_SIZE); + Self::interpolate_and_commit(&aux_trace, domain, transcript); + Some(Round1CommitmentData:: { + trace_polys: aux_trace_polys, + lde_trace: TraceTable::from_columns(aux_trace_polys_evaluations, A::STEP_SIZE), + lde_trace_merkle_tree: aux_merkle_tree, + lde_trace_merkle_root: aux_merkle_root, + }) + } else { + None + }; Ok(Round1 { - trace_polys, - lde_trace, - lde_trace_merkle_roots, - lde_trace_merkle_trees, + main, + aux, rap_challenges, }) } fn commit_composition_polynomial( - lde_composition_poly_parts_evaluations: &[Vec>], - ) -> (BatchedMerkleTree, Commitment) + lde_composition_poly_parts_evaluations: &[Vec>], + ) -> (BatchedMerkleTree, Commitment) where - FieldElement: Serializable + Sync + Send, - FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { // TODO: Remove clones let mut lde_composition_poly_evaluations = Vec::new(); @@ -253,25 +304,25 @@ pub trait IsStarkProver { Self::batch_commit(&lde_composition_poly_evaluations_merged) } - fn round_2_compute_composition_polynomial( + fn round_2_compute_composition_polynomial( air: &A, - domain: &Domain, + domain: &Domain, round_1_result: &Round1, - transition_coefficients: &[FieldElement], - boundary_coefficients: &[FieldElement], - ) -> Round2 + transition_coefficients: &[FieldElement], + boundary_coefficients: &[FieldElement], + ) -> Round2 where - A: AIR + Send + Sync, + A: Send + Sync, A::RAPChallenges: Send + Sync, - FieldElement: Serializable + Send + Sync, - FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, { // Create evaluation table let evaluator = ConstraintEvaluator::new(air, &round_1_result.rap_challenges); let constraint_evaluations = evaluator.evaluate( air, - &round_1_result.lde_trace, + &round_1_result.all_evaluations(), domain, transition_coefficients, boundary_coefficients, @@ -310,17 +361,16 @@ pub trait IsStarkProver { } } - fn round_3_evaluate_polynomials_in_out_of_domain_element( + fn round_3_evaluate_polynomials_in_out_of_domain_element( air: &A, - domain: &Domain, + domain: &Domain, round_1_result: &Round1, - round_2_result: &Round2, - z: &FieldElement, - ) -> Round3 + round_2_result: &Round2, + z: &FieldElement, + ) -> Round3 where - FieldElement: Serializable + Sync + Send, - FieldElement: Serializable + Sync + Send, - A: AIR, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { let z_power = z.pow(round_2_result.composition_poly_parts.len()); @@ -339,8 +389,9 @@ pub trait IsStarkProver { // // In the fibonacci example, the ood frame is simply the evaluations `[t(z), t(z * g), t(z * g^2)]`, where `t` is the trace // polynomial and `g` is the primitive root of unity used when interpolating `t`. + let trace_ood_evaluations = crate::trace::get_trace_evaluations( - &round_1_result.trace_polys, + &round_1_result.all_trace_polys(), z, &air.context().transition_offsets, &domain.trace_primitive_root, @@ -352,22 +403,21 @@ pub trait IsStarkProver { } } - fn round_4_compute_and_run_fri_on_the_deep_composition_polynomial( + fn round_4_compute_and_run_fri_on_the_deep_composition_polynomial( air: &A, - domain: &Domain, + domain: &Domain, round_1_result: &Round1, - round_2_result: &Round2, - round_3_result: &Round3, - z: &FieldElement, - transcript: &mut impl IsStarkTranscript, - ) -> Round4 + round_2_result: &Round2, + round_3_result: &Round3, + z: &FieldElement, + transcript: &mut impl IsStarkTranscript, + ) -> Round4 where - FieldElement: Serializable + Send + Sync, - FieldElement: Serializable + Send + Sync, - A: AIR, + FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, { let coset_offset_u64 = air.context().proof_options.coset_offset; - let coset_offset = FieldElement::::from(coset_offset_u64); + let coset_offset = FieldElement::::from(coset_offset_u64); let gamma = transcript.sample_field_element(); let n_terms_composition_poly = round_2_result.lde_composition_poly_evaluations.len(); @@ -389,7 +439,7 @@ pub trait IsStarkProver { // Compute pβ‚€ (deep composition polynomial) let deep_composition_poly = Self::compute_deep_composition_poly( air, - &round_1_result.trace_polys, + &round_1_result.all_trace_polys(), round_2_result, round_3_result, z, @@ -401,7 +451,7 @@ pub trait IsStarkProver { let domain_size = domain.lde_roots_of_unity_coset.len(); // FRI commit and query phases - let (fri_last_value, fri_layers) = fri::commit_phase::( + let (fri_last_value, fri_layers) = fri::commit_phase::( domain.root_order as usize, deep_composition_poly, transcript, @@ -428,14 +478,13 @@ pub trait IsStarkProver { .map(|layer| layer.merkle_tree.root) .collect(); - let (deep_poly_openings, deep_poly_openings_sym) = + let deep_poly_openings = Self::open_deep_composition_poly(domain, round_1_result, round_2_result, &iotas); Round4 { fri_last_value, fri_layers_merkle_roots, deep_poly_openings, - deep_poly_openings_sym, query_list, nonce, } @@ -443,8 +492,8 @@ pub trait IsStarkProver { fn sample_query_indexes( number_of_queries: usize, - domain: &Domain, - transcript: &mut impl IsStarkTranscript, + domain: &Domain, + transcript: &mut impl IsStarkTranscript, ) -> Vec { let domain_size = domain.lde_roots_of_unity_coset.len() as u64; (0..number_of_queries) @@ -456,20 +505,19 @@ pub trait IsStarkProver { /// FRI. This polynomial is a linear combination of the trace polynomial and the /// composition polynomial, with coefficients sampled by the verifier (i.e. using Fiat-Shamir). #[allow(clippy::too_many_arguments)] - fn compute_deep_composition_poly( + fn compute_deep_composition_poly( air: &A, - trace_polys: &[Polynomial>], - round_2_result: &Round2, - round_3_result: &Round3, - z: &FieldElement, - primitive_root: &FieldElement, - composition_poly_gammas: &[FieldElement], - trace_terms_gammas: &[FieldElement], - ) -> Polynomial> + trace_polys: &[Polynomial>], + round_2_result: &Round2, + round_3_result: &Round3, + z: &FieldElement, + primitive_root: &FieldElement, + composition_poly_gammas: &[FieldElement], + trace_terms_gammas: &[FieldElement], + ) -> Polynomial> where - A: AIR, - FieldElement: Serializable + Send + Sync, - FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, { let z_power = z.pow(round_2_result.composition_poly_parts.len()); @@ -537,20 +585,17 @@ pub trait IsStarkProver { } fn compute_trace_term( - trace_terms: &Polynomial>, - (i, t_j): (usize, &Polynomial>), + trace_terms: &Polynomial>, + (i, t_j): (usize, &Polynomial>), trace_frame_length: usize, - trace_terms_gammas: &[FieldElement], - trace_frame_evaluations: &[Vec>], + trace_terms_gammas: &[FieldElement], + trace_frame_evaluations: &[Vec>], transition_offsets: &[usize], - (z, primitive_root): ( - &FieldElement, - &FieldElement, - ), - ) -> Polynomial> + (z, primitive_root): (&FieldElement, &FieldElement), + ) -> Polynomial> where - FieldElement: Serializable + Send + Sync, - FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, { let i_times_trace_frame_evaluation = i * trace_frame_length; let iter_trace_gammas = trace_terms_gammas @@ -577,13 +622,13 @@ pub trait IsStarkProver { } fn open_composition_poly( - composition_poly_merkle_tree: &BatchedMerkleTree, - lde_composition_poly_evaluations: &[Vec>], + composition_poly_merkle_tree: &BatchedMerkleTree, + lde_composition_poly_evaluations: &[Vec>], index: usize, - ) -> (Proof, Vec>) + ) -> PolynomialOpenings where - FieldElement: Serializable + Sync + Send, - FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { let proof = composition_poly_merkle_tree .get_proof_by_pos(index) @@ -599,119 +644,109 @@ pub trait IsStarkProver { }) .collect(); - (proof, lde_composition_poly_parts_evaluation) + PolynomialOpenings { + proof: proof.clone(), + proof_sym: proof, + evaluations: lde_composition_poly_parts_evaluation + .clone() + .into_iter() + .step_by(2) + .collect(), + evaluations_sym: lde_composition_poly_parts_evaluation + .into_iter() + .skip(1) + .step_by(2) + .collect(), + } } - fn open_trace_polys( - domain: &Domain, - lde_trace_merkle_trees: &[BatchedMerkleTree], - lde_trace: &TraceTable, - index: usize, - ) -> ( - Vec>, - Vec>, - ) + fn open_trace_polys( + domain: &Domain, + tree: &BatchedMerkleTree, + lde_trace: &TraceTable, + challenge: usize, + ) -> PolynomialOpenings where - FieldElement: Serializable + Sync + Send, - FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, + A::Field: IsSubFieldOf, + E: IsField, { let domain_size = domain.lde_roots_of_unity_coset.len(); - let lde_trace_evaluations = lde_trace - .get_row(reverse_index(index, domain_size as u64)) - .to_vec(); - // Trace polynomials openings - #[cfg(feature = "parallel")] - let merkle_trees_iter = lde_trace_merkle_trees.par_iter(); - #[cfg(not(feature = "parallel"))] - let merkle_trees_iter = lde_trace_merkle_trees.iter(); - - let lde_trace_merkle_proofs: Vec> = merkle_trees_iter - .map(|tree| tree.get_proof_by_pos(index).unwrap()) - .collect(); - - (lde_trace_merkle_proofs, lde_trace_evaluations) + let index = challenge * 2; + let index_sym = challenge * 2 + 1; + PolynomialOpenings { + proof: tree.get_proof_by_pos(index).unwrap(), + proof_sym: tree.get_proof_by_pos(index_sym).unwrap(), + evaluations: lde_trace + .get_row(reverse_index(index, domain_size as u64)) + .to_vec(), + evaluations_sym: lde_trace + .get_row(reverse_index(index_sym, domain_size as u64)) + .to_vec(), + } } /// Open the deep composition polynomial on a list of indexes /// and their symmetric elements. - fn open_deep_composition_poly( - domain: &Domain, + fn open_deep_composition_poly( + domain: &Domain, round_1_result: &Round1, - round_2_result: &Round2, + round_2_result: &Round2, indexes_to_open: &[usize], - ) -> ( - DeepPolynomialOpenings, - DeepPolynomialOpenings, - ) + ) -> DeepPolynomialOpenings where - FieldElement: Serializable + Send + Sync, - FieldElement: Serializable + Send + Sync, - A: AIR, + FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, { let mut openings = Vec::new(); - let mut openings_symmetric = Vec::new(); for index in indexes_to_open.iter() { - let (lde_trace_merkle_proofs, lde_trace_evaluations) = Self::open_trace_polys( + let main_trace_opening = Self::open_trace_polys::( domain, - &round_1_result.lde_trace_merkle_trees, - &round_1_result.lde_trace, - index * 2, + &round_1_result.main.lde_trace_merkle_tree, + &round_1_result.main.lde_trace, + *index, ); - let (lde_trace_sym_merkle_proofs, lde_trace_sym_evaluations) = Self::open_trace_polys( - domain, - &round_1_result.lde_trace_merkle_trees, - &round_1_result.lde_trace, - index * 2 + 1, + let composition_openings = Self::open_composition_poly( + &round_2_result.composition_poly_merkle_tree, + &round_2_result.lde_composition_poly_evaluations, + *index, ); - let (lde_composition_poly_proof, lde_composition_poly_parts_evaluation) = - Self::open_composition_poly( - &round_2_result.composition_poly_merkle_tree, - &round_2_result.lde_composition_poly_evaluations, + let aux_trace_polys = round_1_result.aux.as_ref().map(|aux| { + Self::open_trace_polys::( + domain, + &aux.lde_trace_merkle_tree, + &aux.lde_trace, *index, - ); - - openings.push(DeepPolynomialOpening { - lde_composition_poly_proof: lde_composition_poly_proof.clone(), - lde_composition_poly_parts_evaluation: lde_composition_poly_parts_evaluation - .clone() - .into_iter() - .step_by(2) - .collect(), - lde_trace_merkle_proofs, - lde_trace_evaluations, + ) }); - openings_symmetric.push(DeepPolynomialOpening { - lde_composition_poly_proof, - lde_composition_poly_parts_evaluation: lde_composition_poly_parts_evaluation - .into_iter() - .skip(1) - .step_by(2) - .collect(), - lde_trace_merkle_proofs: lde_trace_sym_merkle_proofs, - lde_trace_evaluations: lde_trace_sym_evaluations, + openings.push(DeepPolynomialOpening { + composition_poly: composition_openings, + main_trace_polys: main_trace_opening, + aux_trace_polys, }); } - (openings, openings_symmetric) + openings } // FIXME remove unwrap() calls and return errors - fn prove( - main_trace: &TraceTable, + fn prove( + main_trace: &TraceTable, pub_inputs: &A::PublicInputs, proof_options: &ProofOptions, - mut transcript: impl IsStarkTranscript, - ) -> Result, ProvingError> + mut transcript: impl IsStarkTranscript, + ) -> Result, ProvingError> where - A: AIR + Send + Sync, + A: Send + Sync, A::RAPChallenges: Send + Sync, - FieldElement: Serializable + Send + Sync, - FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, { info!("Started proof generation..."); #[cfg(feature = "instruments")] @@ -719,16 +754,6 @@ pub trait IsStarkProver { #[cfg(feature = "instruments")] let timer0 = Instant::now(); - let main_trace = TraceTable::::from_columns( - main_trace - .columns() - .clone() - .into_iter() - .map(|col| col.into_iter().map(|x| x.to_extension()).collect()) - .collect(), - A::STEP_SIZE, - ); - let air = A::new(main_trace.n_rows(), pub_inputs, proof_options); let domain = Domain::new(&air); @@ -746,9 +771,9 @@ pub trait IsStarkProver { #[cfg(feature = "instruments")] let timer1 = Instant::now(); - let round_1_result = Self::round_1_randomized_air_with_preprocessing::( + let round_1_result = Self::round_1_randomized_air_with_preprocessing( &air, - &main_trace, + main_trace, &domain, &mut transcript, )?; @@ -756,7 +781,7 @@ pub trait IsStarkProver { #[cfg(debug_assertions)] validate_trace( &air, - &round_1_result.trace_polys, + &round_1_result.all_trace_polys(), &domain, &round_1_result.rap_challenges, ); @@ -897,12 +922,14 @@ pub trait IsStarkProver { .into_iter() .flatten() .collect(), - round_1_result.trace_polys.len(), + air.context().trace_columns, ); - Ok(StarkProof:: { + Ok(StarkProof:: { + // [tβ±Ό] + lde_trace_main_merkle_root: round_1_result.main.lde_trace_merkle_root, // [tβ±Ό] - lde_trace_merkle_roots: round_1_result.lde_trace_merkle_roots, + lde_trace_aux_merkle_root: round_1_result.aux.map(|x| x.lde_trace_merkle_root), // tβ±Ό(zgᡏ) trace_ood_evaluations, // [H₁] and [Hβ‚‚] @@ -917,9 +944,8 @@ pub trait IsStarkProver { // Open(pβ‚€(Dβ‚€), πœβ‚›), Open(pβ‚–(Dβ‚–), βˆ’πœβ‚›^(2ᡏ)) query_list: round_4_result.query_list, // Open(H₁(D_LDE, πœβ‚€), Open(Hβ‚‚(D_LDE, πœβ‚€), Open(tβ±Ό(D_LDE), πœβ‚€) + // Open(H₁(D_LDE, -𝜐ᡒ), Open(Hβ‚‚(D_LDE, -𝜐ᡒ), Open(tβ±Ό(D_LDE), -𝜐ᡒ) deep_poly_openings: round_4_result.deep_poly_openings, - // Open(H₁(D_LDE, πœβ‚€), Open(Hβ‚‚(D_LDE, πœβ‚€), Open(tβ±Ό(D_LDE), πœβ‚€) - deep_poly_openings_sym: round_4_result.deep_poly_openings_sym, // nonce obtained from grinding nonce: round_4_result.nonce, @@ -1052,7 +1078,7 @@ mod tests { } fn proof_parts_stone_compatibility_case_1() -> ( - StarkProof, + StarkProof, fibonacci_2_cols_shifted::PublicInputs, ProofOptions, [u8; 4], @@ -1074,7 +1100,7 @@ mod tests { let transcript_init_seed = [0xca, 0xfe, 0xca, 0xfe]; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, @@ -1084,7 +1110,7 @@ mod tests { (proof, pub_inputs, proof_options, transcript_init_seed) } - fn stone_compatibility_case_1_proof() -> StarkProof { + fn stone_compatibility_case_1_proof() -> StarkProof { let (proof, _, _, _) = proof_parts_stone_compatibility_case_1(); proof } @@ -1106,7 +1132,7 @@ mod tests { #[test] fn stone_compatibility_case_1_proof_is_valid() { let (proof, public_inputs, options, seed) = proof_parts_stone_compatibility_case_1(); - assert!(Verifier::verify::>( + assert!(Verifier::>::verify( &proof, &public_inputs, &options, @@ -1119,7 +1145,7 @@ mod tests { let proof = stone_compatibility_case_1_proof(); assert_eq!( - proof.lde_trace_merkle_roots[0].to_vec(), + proof.lde_trace_main_merkle_root.to_vec(), decode_hex("0eb9dcc0fb1854572a01236753ce05139d392aa3aeafe72abff150fe21175594").unwrap() ); } @@ -1283,7 +1309,7 @@ mod tests { // Trace Col 0 assert_eq!( - proof.deep_poly_openings[0].lde_trace_evaluations[0], + proof.deep_poly_openings[0].main_trace_polys.evaluations[0], FieldElement::from_hex_unchecked( "4de0d56f9cf97dff326c26592fbd4ae9ee756080b12c51cfe4864e9b8734f43" ) @@ -1291,7 +1317,7 @@ mod tests { // Trace Col 1 assert_eq!( - proof.deep_poly_openings[0].lde_trace_evaluations[1], + proof.deep_poly_openings[0].main_trace_polys.evaluations[1], FieldElement::from_hex_unchecked( "1bc1aadf39f2faee64d84cb25f7a95d3dceac1016258a39fc90c9d370e69e8e" ) @@ -1299,7 +1325,7 @@ mod tests { // Trace Col 0 symmetric assert_eq!( - proof.deep_poly_openings_sym[0].lde_trace_evaluations[0], + proof.deep_poly_openings[0].main_trace_polys.evaluations_sym[0], FieldElement::from_hex_unchecked( "321f2a9063068310cd93d9a6d042b516118a9f7f4ed3ae301b79b16478cb0c6" ) @@ -1307,7 +1333,7 @@ mod tests { // Trace Col 1 symmetric assert_eq!( - proof.deep_poly_openings_sym[0].lde_trace_evaluations[1], + proof.deep_poly_openings[0].main_trace_polys.evaluations_sym[1], FieldElement::from_hex_unchecked( "643e5520c60d06219b27b34da0856a2c23153efe9da75c6036f362c8f196186" ) @@ -1320,19 +1346,31 @@ mod tests { // Trace poly auth path level 1 assert_eq!( - proof.deep_poly_openings[0].lde_trace_merkle_proofs[0].merkle_path[1].to_vec(), + proof.deep_poly_openings[0] + .main_trace_polys + .proof + .merkle_path[1] + .to_vec(), decode_hex("91b0c0b24b9d00067b0efab50832b76cf97192091624d42b86740666c5d369e6").unwrap() ); // Trace poly auth path level 2 assert_eq!( - proof.deep_poly_openings[0].lde_trace_merkle_proofs[0].merkle_path[2].to_vec(), + proof.deep_poly_openings[0] + .main_trace_polys + .proof + .merkle_path[2] + .to_vec(), decode_hex("993b044db22444c0c0ebf1095b9a51faeb001c9b4dea36abe905f7162620dbbd").unwrap() ); // Trace poly auth path level 3 assert_eq!( - proof.deep_poly_openings[0].lde_trace_merkle_proofs[0].merkle_path[3].to_vec(), + proof.deep_poly_openings[0] + .main_trace_polys + .proof + .merkle_path[3] + .to_vec(), decode_hex("5017abeca33fa82576b5c5c2c61792693b48c9d4414a407eef66b6029dae07ea").unwrap() ); } @@ -1343,14 +1381,14 @@ mod tests { // Composition poly assert_eq!( - proof.deep_poly_openings[0].lde_composition_poly_parts_evaluation[0], + proof.deep_poly_openings[0].composition_poly.evaluations[0], FieldElement::from_hex_unchecked( "2b54852557db698e97253e9d110d60e9bf09f1d358b4c1a96f9f3cf9d2e8755" ) ); // Composition poly sym assert_eq!( - proof.deep_poly_openings_sym[0].lde_composition_poly_parts_evaluation[0], + proof.deep_poly_openings[0].composition_poly.evaluations_sym[0], FieldElement::from_hex_unchecked( "190f1b0acb7858bd3f5285b68befcf32b436a5f1e3a280e1f42565c1f35c2c3" ) @@ -1364,7 +1402,8 @@ mod tests { // Composition poly auth path level 0 assert_eq!( proof.deep_poly_openings[0] - .lde_composition_poly_proof + .composition_poly + .proof .merkle_path[0] .to_vec(), decode_hex("403b75a122eaf90a298e5d3db2cc7ca096db478078122379a6e3616e72da7546").unwrap() @@ -1373,7 +1412,8 @@ mod tests { // Composition poly auth path level 1 assert_eq!( proof.deep_poly_openings[0] - .lde_composition_poly_proof + .composition_poly + .proof .merkle_path[1] .to_vec(), decode_hex("07950888c0355c204a1e83ecbee77a0a6a89f93d41cc2be6b39ddd1e727cc965").unwrap() @@ -1382,7 +1422,8 @@ mod tests { // Composition poly auth path level 2 assert_eq!( proof.deep_poly_openings[0] - .lde_composition_poly_proof + .composition_poly + .proof .merkle_path[2] .to_vec(), decode_hex("58befe2c5de74cc5a002aa82ea219c5b242e761b45fd266eb95521e9f53f44eb").unwrap() @@ -1433,7 +1474,7 @@ mod tests { } fn proof_parts_stone_compatibility_case_2() -> ( - StarkProof, + StarkProof, fibonacci_2_cols_shifted::PublicInputs, ProofOptions, [u8; 4], @@ -1455,7 +1496,7 @@ mod tests { let transcript_init_seed = [0xfa, 0xfa, 0xfa, 0xee]; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, @@ -1465,7 +1506,7 @@ mod tests { (proof, pub_inputs, proof_options, transcript_init_seed) } - fn stone_compatibility_case_2_proof() -> StarkProof { + fn stone_compatibility_case_2_proof() -> StarkProof { let (proof, _, _, _) = proof_parts_stone_compatibility_case_2(); proof } @@ -1489,7 +1530,7 @@ mod tests { let proof = stone_compatibility_case_2_proof(); assert_eq!( - proof.lde_trace_merkle_roots[0].to_vec(), + proof.lde_trace_main_merkle_root.to_vec(), decode_hex("6d31dd00038974bde5fe0c5e3a765f8ddc822a5df3254fca85a1950ae0208cbe").unwrap() ); } diff --git a/provers/stark/src/tests/integration_tests.rs b/provers/stark/src/tests/integration_tests.rs index c1fd2c7022..9cdafccf62 100644 --- a/provers/stark/src/tests/integration_tests.rs +++ b/provers/stark/src/tests/integration_tests.rs @@ -30,14 +30,14 @@ fn test_prove_fib() { a1: Felt252::one(), }; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, StoneProverTranscript::new(&[]), ) .unwrap(); - assert!(Verifier::verify::>( + assert!(Verifier::>::verify( &proof, &pub_inputs, &proof_options, @@ -62,14 +62,14 @@ fn test_prove_fib17() { a1: FE::one(), }; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, StoneProverTranscript::new(&[]), ) .unwrap(); - assert!(Verifier::verify::>( + assert!(Verifier::>::verify( &proof, &pub_inputs, &proof_options, @@ -88,14 +88,14 @@ fn test_prove_simple_periodic_8() { a1: Felt252::from(8), }; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, StoneProverTranscript::new(&[]), ) .unwrap(); - assert!(Verifier::verify::>( + assert!(Verifier::>::verify( &proof, &pub_inputs, &proof_options, @@ -114,14 +114,14 @@ fn test_prove_simple_periodic_32() { a1: Felt252::from(32768), }; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, StoneProverTranscript::new(&[]), ) .unwrap(); - assert!(Verifier::verify::>( + assert!(Verifier::>::verify( &proof, &pub_inputs, &proof_options, @@ -140,14 +140,14 @@ fn test_prove_fib_2_cols() { a1: Felt252::one(), }; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, StoneProverTranscript::new(&[]), ) .unwrap(); - assert!(Verifier::verify::>( + assert!(Verifier::>::verify( &proof, &pub_inputs, &proof_options, @@ -168,14 +168,14 @@ fn test_prove_fib_2_cols_shifted() { claimed_index, }; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, StoneProverTranscript::new(&[]), ) .unwrap(); - assert!(Verifier::verify::>( + assert!(Verifier::>::verify( &proof, &pub_inputs, &proof_options, @@ -193,14 +193,14 @@ fn test_prove_quadratic() { a0: Felt252::from(3), }; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, StoneProverTranscript::new(&[]), ) .unwrap(); - assert!(Verifier::verify::>( + assert!(Verifier::>::verify( &proof, &pub_inputs, &proof_options, @@ -221,14 +221,14 @@ fn test_prove_rap_fib() { a1: Felt252::one(), }; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, StoneProverTranscript::new(&[]), ) .unwrap(); - assert!(Verifier::verify::>( + assert!(Verifier::>::verify( &proof, &pub_inputs, &proof_options, @@ -244,9 +244,9 @@ fn test_prove_dummy() { let proof_options = ProofOptions::default_test_options(); let proof = - Prover::prove::(&trace, &(), &proof_options, StoneProverTranscript::new(&[])) + Prover::::prove(&trace, &(), &proof_options, StoneProverTranscript::new(&[])) .unwrap(); - assert!(Verifier::verify::( + assert!(Verifier::::verify( &proof, &(), &proof_options, diff --git a/provers/stark/src/traits.rs b/provers/stark/src/traits.rs index cb2ffdfb8b..94e028bfdb 100644 --- a/provers/stark/src/traits.rs +++ b/provers/stark/src/traits.rs @@ -32,7 +32,7 @@ pub trait AIR { fn build_auxiliary_trace( &self, - main_trace: &TraceTable, + main_trace: &TraceTable, rap_challenges: &Self::RAPChallenges, ) -> TraceTable; diff --git a/provers/stark/src/verifier.rs b/provers/stark/src/verifier.rs index 084ef5b42e..7be6148ad1 100644 --- a/provers/stark/src/verifier.rs +++ b/provers/stark/src/verifier.rs @@ -1,3 +1,4 @@ +use std::marker::PhantomData; #[cfg(feature = "instruments")] use std::time::Instant; @@ -13,7 +14,6 @@ use lambdaworks_math::{ fft::cpu::bit_reversing::reverse_index, field::{ element::FieldElement, - fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, traits::{IsFFTField, IsField, IsSubFieldOf}, }, traits::Serializable, @@ -28,13 +28,12 @@ use super::{ traits::AIR, }; -pub struct Verifier {} - -impl IsStarkVerifier for Verifier { - type Field = Stark252PrimeField; - type FieldExtension = Stark252PrimeField; +pub struct Verifier { + phantom: PhantomData, } +impl IsStarkVerifier for Verifier {} + pub struct Challenges where F: IsField, @@ -53,14 +52,11 @@ where pub type DeepPolynomialEvaluations = (Vec>, Vec>); -pub trait IsStarkVerifier { - type Field: IsFFTField + IsSubFieldOf; - type FieldExtension: IsField; - +pub trait IsStarkVerifier { fn sample_query_indexes( number_of_queries: usize, - domain: &Domain, - transcript: &mut impl IsStarkTranscript, + domain: &Domain, + transcript: &mut impl IsStarkTranscript, ) -> Vec { let domain_size = domain.lde_roots_of_unity_coset.len() as u64; (0..number_of_queries) @@ -68,28 +64,27 @@ pub trait IsStarkVerifier { .collect::>() } - fn step_1_replay_rounds_and_recover_challenges( + fn step_1_replay_rounds_and_recover_challenges( air: &A, - proof: &StarkProof, - domain: &Domain, - transcript: &mut impl IsStarkTranscript, - ) -> Challenges + proof: &StarkProof, + domain: &Domain, + transcript: &mut impl IsStarkTranscript, + ) -> Challenges where - FieldElement: Serializable, - FieldElement: Serializable, - A: AIR, + FieldElement: Serializable, + FieldElement: Serializable, { // =================================== // ==========| Round 1 |========== // =================================== // <<<< Receive commitments:[tβ±Ό] - transcript.append_bytes(&proof.lde_trace_merkle_roots[0]); + transcript.append_bytes(&proof.lde_trace_main_merkle_root); let rap_challenges = air.build_rap_challenges(transcript); - if let Some(root) = proof.lde_trace_merkle_roots.get(1) { - transcript.append_bytes(root); + if let Some(root) = proof.lde_trace_aux_merkle_root { + transcript.append_bytes(&root); } // =================================== @@ -168,7 +163,7 @@ pub trait IsStarkVerifier { transcript.append_bytes(root); element }) - .collect::>>(); + .collect::>>(); // >>>> Send challenge πœβ‚™β‚‹β‚ zetas.push(transcript.sample_field_element()); @@ -204,15 +199,12 @@ pub trait IsStarkVerifier { } } - fn step_2_verify_claimed_composition_polynomial( + fn step_2_verify_claimed_composition_polynomial( air: &A, - proof: &StarkProof, - domain: &Domain, - challenges: &Challenges, - ) -> bool - where - A: AIR, - { + proof: &StarkProof, + domain: &Domain, + challenges: &Challenges, + ) -> bool { let boundary_constraints = air.boundary_constraints(&challenges.rap_challenges); let trace_length = air.trace_length(); @@ -220,8 +212,8 @@ pub trait IsStarkVerifier { #[allow(clippy::type_complexity)] let (boundary_c_i_evaluations_num, mut boundary_c_i_evaluations_den): ( - Vec>, - Vec>, + Vec>, + Vec>, ) = (0..number_of_b_constraints) .map(|index| { let step = boundary_constraints.constraints[index].step; @@ -244,21 +236,19 @@ pub trait IsStarkVerifier { FieldElement::inplace_batch_inverse(&mut boundary_c_i_evaluations_den).unwrap(); - let boundary_quotient_ood_evaluation: FieldElement = + let boundary_quotient_ood_evaluation: FieldElement = boundary_c_i_evaluations_num .iter() .zip(&boundary_c_i_evaluations_den) .zip(&challenges.boundary_coeffs) .map(|((num, den), beta)| num * den * beta) - .fold(FieldElement::::zero(), |acc, x| { - acc + x - }); + .fold(FieldElement::::zero(), |acc, x| acc + x); let periodic_values = air .get_periodic_column_polynomials() .iter() .map(|poly| poly.evaluate(&challenges.z)) - .collect::>>(); + .collect::>>(); let transition_ood_frame_evaluations = air.compute_transition( &(proof.trace_ood_evaluations).into_frame(A::STEP_SIZE), @@ -266,7 +256,7 @@ pub trait IsStarkVerifier { &challenges.rap_challenges, ); - let denominator = (-FieldElement::::one() + &challenges.z.pow(trace_length)) + let denominator = (-FieldElement::::one() + &challenges.z.pow(trace_length)) .inv() .unwrap(); @@ -276,7 +266,7 @@ pub trait IsStarkVerifier { ) .iter() .map(|poly| poly.evaluate(&challenges.z)) - .collect::>>(); + .collect::>>(); let unity = &FieldElement::one(); let transition_c_i_evaluations_sum = transition_ood_frame_evaluations @@ -305,15 +295,14 @@ pub trait IsStarkVerifier { composition_poly_claimed_ood_evaluation == composition_poly_ood_evaluation } - fn step_3_verify_fri( - proof: &StarkProof, - domain: &Domain, - challenges: &Challenges, + fn step_3_verify_fri( + proof: &StarkProof, + domain: &Domain, + challenges: &Challenges, ) -> bool where - FieldElement: Serializable + Sync + Send, - FieldElement: Serializable + Sync + Send, - A: AIR, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { let (deep_poly_evaluations, deep_poly_evaluations_sym) = Self::reconstruct_deep_composition_poly_evaluations_for_all_queries( @@ -325,7 +314,7 @@ pub trait IsStarkVerifier { .iotas .iter() .map(|iota| Self::query_challenge_to_evaluation_point(*iota, domain)) - .collect::>>(); + .collect::>>(); FieldElement::inplace_batch_inverse(&mut evaluation_point_inverse).unwrap(); proof .query_list @@ -350,8 +339,8 @@ pub trait IsStarkVerifier { fn query_challenge_to_evaluation_point( iota: usize, - domain: &Domain, - ) -> FieldElement { + domain: &Domain, + ) -> FieldElement { domain.lde_roots_of_unity_coset [reverse_index(iota * 2, domain.lde_roots_of_unity_coset.len() as u64)] .clone() @@ -359,151 +348,139 @@ pub trait IsStarkVerifier { fn query_challenge_to_evaluation_point_sym( iota: usize, - domain: &Domain, - ) -> FieldElement { + domain: &Domain, + ) -> FieldElement { domain.lde_roots_of_unity_coset [reverse_index(iota * 2 + 1, domain.lde_roots_of_unity_coset.len() as u64)] .clone() } - fn verify_opening( + fn verify_opening( proof: &Proof, root: &Commitment, index: usize, - value: &[FieldElement], + value: &[FieldElement], ) -> bool where - FieldElement: Serializable + Sync + Send, - FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, + E: IsField, + A::Field: IsSubFieldOf, { - proof.verify::>( - root, - index, - &value.to_owned(), - ) + proof.verify::>(root, index, &value.to_owned()) } /// Verify opening Open(tβ±Ό(D_LDE), 𝜐) and Open(tβ±Ό(D_LDE), -𝜐) for all trace polynomials tβ±Ό, /// where 𝜐 and -𝜐 are the elements corresponding to the index challenge `iota`. fn verify_trace_openings( - num_main_columns: usize, - proof: &StarkProof, - deep_poly_openings: &DeepPolynomialOpening, - deep_poly_openings_sym: &DeepPolynomialOpening, + proof: &StarkProof, + deep_poly_openings: &DeepPolynomialOpening, iota: usize, ) -> bool where - FieldElement: Serializable + Sync + Send, - FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { - let lde_trace_evaluations = vec![ - deep_poly_openings.lde_trace_evaluations[..num_main_columns].to_vec(), - deep_poly_openings.lde_trace_evaluations[num_main_columns..].to_vec(), - ]; - let index = iota * 2; - let openings_are_valid = proof - .lde_trace_merkle_roots - .iter() - .zip(&deep_poly_openings.lde_trace_merkle_proofs) - .zip(lde_trace_evaluations) - .fold(true, |acc, ((merkle_root, merkle_proof), evaluation)| { - acc & Self::verify_opening(merkle_proof, merkle_root, index, &evaluation) - }); + let index_sym = iota * 2 + 1; + let mut result = true; - let lde_trace_evaluations_sym = vec![ - deep_poly_openings_sym.lde_trace_evaluations[..num_main_columns].to_vec(), - deep_poly_openings_sym.lde_trace_evaluations[num_main_columns..].to_vec(), - ]; + result &= Self::verify_opening::( + &deep_poly_openings.main_trace_polys.proof, + &proof.lde_trace_main_merkle_root, + index, + &deep_poly_openings.main_trace_polys.evaluations, + ); + result &= Self::verify_opening::( + &deep_poly_openings.main_trace_polys.proof_sym, + &proof.lde_trace_main_merkle_root, + index_sym, + &deep_poly_openings.main_trace_polys.evaluations_sym, + ); - let index_sym = iota * 2 + 1; - let openings_sym_are_valid = proof - .lde_trace_merkle_roots - .iter() - .zip(&deep_poly_openings_sym.lde_trace_merkle_proofs) - .zip(lde_trace_evaluations_sym) - .fold(true, |acc, ((merkle_root, merkle_proof), evaluation)| { - acc & Self::verify_opening(merkle_proof, merkle_root, index_sym, &evaluation) - }); - openings_are_valid & openings_sym_are_valid + match ( + proof.lde_trace_aux_merkle_root, + &deep_poly_openings.aux_trace_polys, + ) { + (None, Some(_)) => result = false, + (Some(_), None) => result = false, + (Some(aux_root), Some(aux_trace_polys_opening)) => { + result &= Self::verify_opening::( + &aux_trace_polys_opening.proof, + &aux_root, + index, + &aux_trace_polys_opening.evaluations, + ); + result &= Self::verify_opening::( + &aux_trace_polys_opening.proof_sym, + &aux_root, + index_sym, + &aux_trace_polys_opening.evaluations_sym, + ); + } + _ => {} + } + + result } /// Verify opening Open(Hα΅’(D_LDE), 𝜐) and Open(Hα΅’(D_LDE), -𝜐) for all parts Hα΅’of the composition /// polynomial, where 𝜐 and -𝜐 are the elements corresponding to the index challenge `iota`. fn verify_composition_poly_opening( - deep_poly_openings: &DeepPolynomialOpening, - deep_poly_openings_sym: &DeepPolynomialOpening, + deep_poly_openings: &DeepPolynomialOpening, composition_poly_merkle_root: &Commitment, iota: &usize, ) -> bool where - FieldElement: Serializable + Sync + Send, - FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { - let mut value = deep_poly_openings - .lde_composition_poly_parts_evaluation - .clone(); - value.extend_from_slice(&deep_poly_openings_sym.lde_composition_poly_parts_evaluation); + let mut value = deep_poly_openings.composition_poly.evaluations.clone(); + value.extend_from_slice(&deep_poly_openings.composition_poly.evaluations_sym); deep_poly_openings - .lde_composition_poly_proof - .verify::>( + .composition_poly + .proof + .verify::>( composition_poly_merkle_root, *iota, &value, ) } - fn step_4_verify_trace_and_composition_openings< - A: AIR, - >( - air: &A, - proof: &StarkProof, - challenges: &Challenges, + fn step_4_verify_trace_and_composition_openings( + proof: &StarkProof, + challenges: &Challenges, ) -> bool where - FieldElement: Serializable + Sync + Send, - FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { - challenges - .iotas - .iter() - .zip(&proof.deep_poly_openings) - .zip(&proof.deep_poly_openings_sym) - .fold( - true, - |mut result, ((iota_n, deep_poly_opening), deep_poly_openings_sym)| { - result &= Self::verify_composition_poly_opening( - deep_poly_opening, - deep_poly_openings_sym, - &proof.composition_poly_root, - iota_n, - ); + challenges.iotas.iter().zip(&proof.deep_poly_openings).fold( + true, + |mut result, (iota_n, deep_poly_opening)| { + result &= Self::verify_composition_poly_opening( + deep_poly_opening, + &proof.composition_poly_root, + iota_n, + ); - let num_main_columns = - air.context().trace_columns - air.number_auxiliary_rap_columns(); - result &= Self::verify_trace_openings( - num_main_columns, - proof, - deep_poly_opening, - deep_poly_openings_sym, - *iota_n, - ); - result - }, - ) + result &= Self::verify_trace_openings(proof, deep_poly_opening, *iota_n); + result + }, + ) } fn verify_fri_layer_openings( merkle_root: &Commitment, auth_path_sym: &Proof, - evaluation: &FieldElement, - evaluation_sym: &FieldElement, + evaluation: &FieldElement, + evaluation_sym: &FieldElement, iota: usize, ) -> bool where - FieldElement: Serializable + Sync + Send, - FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { let evaluations = if iota % 2 == 1 { vec![evaluation_sym.clone(), evaluation.clone()] @@ -511,7 +488,7 @@ pub trait IsStarkVerifier { vec![evaluation.clone(), evaluation_sym.clone()] }; - auth_path_sym.verify::>( + auth_path_sym.verify::>( merkle_root, iota >> 1, &evaluations, @@ -527,20 +504,20 @@ pub trait IsStarkVerifier { /// `deep_composition_evaluation`: precomputed value of pβ‚€(𝜐), where pβ‚€ is the deep composition polynomial. /// `deep_composition_evaluation_sym`: precomputed value of pβ‚€(-𝜐), where pβ‚€ is the deep composition polynomial. fn verify_query_and_sym_openings( - proof: &StarkProof, - zetas: &[FieldElement], + proof: &StarkProof, + zetas: &[FieldElement], iota: usize, - fri_decommitment: &FriDecommitment, - evaluation_point_inv: FieldElement, - deep_composition_evaluation: &FieldElement, - deep_composition_evaluation_sym: &FieldElement, + fri_decommitment: &FriDecommitment, + evaluation_point_inv: FieldElement, + deep_composition_evaluation: &FieldElement, + deep_composition_evaluation_sym: &FieldElement, ) -> bool where - FieldElement: Serializable + Sync + Send, - FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { let fri_layers_merkle_roots = &proof.fri_layers_merkle_roots; - let evaluation_point_vec: Vec> = + let evaluation_point_vec: Vec> = core::iter::successors(Some(evaluation_point_inv.square()), |evaluation_point| { Some(evaluation_point.square()) }) @@ -600,19 +577,27 @@ pub trait IsStarkVerifier { ) } - fn reconstruct_deep_composition_poly_evaluations_for_all_queries( - challenges: &Challenges, - domain: &Domain, - proof: &StarkProof, - ) -> DeepPolynomialEvaluations - where - A: AIR, - { + fn reconstruct_deep_composition_poly_evaluations_for_all_queries( + challenges: &Challenges, + domain: &Domain, + proof: &StarkProof, + ) -> DeepPolynomialEvaluations { let mut deep_poly_evaluations = Vec::new(); let mut deep_poly_evaluations_sym = Vec::new(); for (i, iota) in challenges.iotas.iter().enumerate() { let primitive_root = - &Self::Field::get_primitive_root_of_unity(domain.root_order as u64).unwrap(); + &A::Field::get_primitive_root_of_unity(domain.root_order as u64).unwrap(); + + let mut evaluations: Vec> = proof.deep_poly_openings[i] + .main_trace_polys + .evaluations + .clone() + .into_iter() + .map(|x| x.to_extension()) + .collect(); + if let Some(aux_trace_polys) = &proof.deep_poly_openings[i].aux_trace_polys { + evaluations.extend_from_slice(&aux_trace_polys.evaluations); + } let evaluation_point = Self::query_challenge_to_evaluation_point(*iota, domain); deep_poly_evaluations.push(Self::reconstruct_deep_composition_poly_evaluation( @@ -620,36 +605,46 @@ pub trait IsStarkVerifier { &evaluation_point, primitive_root, challenges, - &proof.deep_poly_openings[i].lde_trace_evaluations, - &proof.deep_poly_openings[i].lde_composition_poly_parts_evaluation, + &evaluations, + &proof.deep_poly_openings[i].composition_poly.evaluations, )); + let mut evaluations_sym: Vec> = proof + .deep_poly_openings[i] + .main_trace_polys + .evaluations_sym + .clone() + .into_iter() + .map(|x| x.to_extension()) + .collect(); + if let Some(aux_trace_polys) = &proof.deep_poly_openings[i].aux_trace_polys { + evaluations_sym.extend_from_slice(&aux_trace_polys.evaluations_sym); + } + let evaluation_point = Self::query_challenge_to_evaluation_point_sym(*iota, domain); deep_poly_evaluations_sym.push(Self::reconstruct_deep_composition_poly_evaluation( proof, &evaluation_point, primitive_root, challenges, - &proof.deep_poly_openings_sym[i].lde_trace_evaluations, - &proof.deep_poly_openings_sym[i].lde_composition_poly_parts_evaluation, + &evaluations_sym, + &proof.deep_poly_openings[i].composition_poly.evaluations_sym, )); } (deep_poly_evaluations, deep_poly_evaluations_sym) } - fn reconstruct_deep_composition_poly_evaluation< - A: AIR, - >( - proof: &StarkProof, - evaluation_point: &FieldElement, - primitive_root: &FieldElement, - challenges: &Challenges, - lde_trace_evaluations: &[FieldElement], - lde_composition_poly_parts_evaluation: &[FieldElement], - ) -> FieldElement { + fn reconstruct_deep_composition_poly_evaluation( + proof: &StarkProof, + evaluation_point: &FieldElement, + primitive_root: &FieldElement, + challenges: &Challenges, + lde_trace_evaluations: &[FieldElement], + lde_composition_poly_parts_evaluation: &[FieldElement], + ) -> FieldElement { let mut denoms_trace = (0..proof.trace_ood_evaluations.height) .map(|row_idx| evaluation_point - primitive_root.pow(row_idx as u64) * &challenges.z) - .collect::>>(); + .collect::>>(); FieldElement::inplace_batch_inverse(&mut denoms_trace).unwrap(); let trace_term = (0..proof.trace_ood_evaluations.width) @@ -682,16 +677,15 @@ pub trait IsStarkVerifier { trace_term + h_terms } - fn verify( - proof: &StarkProof, + fn verify( + proof: &StarkProof, pub_input: &A::PublicInputs, proof_options: &ProofOptions, - mut transcript: impl IsStarkTranscript, + mut transcript: impl IsStarkTranscript, ) -> bool where - A: AIR, - FieldElement: Serializable + Sync + Send, - FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { // Verify there are enough queries if proof.query_list.len() < proof_options.fri_number_of_queries { @@ -767,7 +761,7 @@ pub trait IsStarkVerifier { let timer4 = Instant::now(); #[allow(clippy::let_and_return)] - if !Self::step_4_verify_trace_and_composition_openings(&air, proof, &challenges) { + if !Self::step_4_verify_trace_and_composition_openings(proof, &challenges) { error!("DEEP Composition Polynomial verification failed"); return false; } diff --git a/winterfell_adapter/benches/proving.rs b/winterfell_adapter/benches/proving.rs index 6a828d6712..0a05aa356c 100644 --- a/winterfell_adapter/benches/proving.rs +++ b/winterfell_adapter/benches/proving.rs @@ -6,11 +6,12 @@ use miden_processor::DefaultHost; use miden_processor::{self as processor}; use miden_prover::prove; use processor::ExecutionTrace; -use stark_platinum_prover::{proof::options::ProofOptions, prover::IsStarkProver}; +use stark_platinum_prover::proof::options::ProofOptions; +use stark_platinum_prover::prover::{IsStarkProver, Prover}; use winter_air::FieldExtension; use winter_prover::Trace; use winterfell_adapter::adapter::public_inputs::AirAdapterPublicInputs; -use winterfell_adapter::adapter::{air::AirAdapter, Prover, Transcript}; +use winterfell_adapter::adapter::{air::AirAdapter, Transcript}; use winterfell_adapter::examples::miden_vm::ExecutionTraceMetadata; struct BenchInstance { @@ -102,7 +103,7 @@ pub fn bench_prove_miden_fibonacci(c: &mut Criterion) { ); let _proof = black_box( - Prover::prove::>( + Prover::>::prove( &trace, &pub_inputs, &instance.lambda_proof_options, diff --git a/winterfell_adapter/src/adapter/mod.rs b/winterfell_adapter/src/adapter/mod.rs index 0b26961528..c0ef319c7f 100644 --- a/winterfell_adapter/src/adapter/mod.rs +++ b/winterfell_adapter/src/adapter/mod.rs @@ -1,27 +1,12 @@ use lambdaworks_math::traits::ByteConversion; use miden_core::Felt; use sha3::{Digest, Keccak256}; -use stark_platinum_prover::{ - fri::FieldElement, prover::IsStarkProver, transcript::IsStarkTranscript, - verifier::IsStarkVerifier, -}; +use stark_platinum_prover::{fri::FieldElement, transcript::IsStarkTranscript}; use winter_math::StarkField; pub mod air; pub mod public_inputs; -pub struct Prover; -impl IsStarkProver for Prover { - type Field = Felt; - type FieldExtension = Felt; -} - -pub struct Verifier {} -impl IsStarkVerifier for Verifier { - type Field = Felt; - type FieldExtension = Felt; -} - pub struct Transcript { hasher: Keccak256, } diff --git a/winterfell_adapter/src/examples/cubic.rs b/winterfell_adapter/src/examples/cubic.rs index ea8320c507..c9ac74a0d8 100644 --- a/winterfell_adapter/src/examples/cubic.rs +++ b/winterfell_adapter/src/examples/cubic.rs @@ -74,15 +74,15 @@ pub fn build_trace(sequence_length: usize) -> TraceTable { mod tests { use miden_core::Felt; use stark_platinum_prover::{ - proof::options::ProofOptions, prover::IsStarkProver, verifier::IsStarkVerifier, + proof::options::ProofOptions, + prover::{IsStarkProver, Prover}, + verifier::{IsStarkVerifier, Verifier}, }; use winter_air::TraceInfo; use winter_prover::{Trace, TraceTable}; use crate::{ - adapter::{ - air::AirAdapter, public_inputs::AirAdapterPublicInputs, Prover, Transcript, Verifier, - }, + adapter::{air::AirAdapter, public_inputs::AirAdapterPublicInputs, Transcript}, examples::cubic::{self, Cubic}, }; @@ -101,7 +101,7 @@ mod tests { metadata: (), }; - let proof = Prover::prove::, Felt, _>>( + let proof = Prover::, Felt, _>>::prove( &trace, &pub_inputs, &lambda_proof_options, @@ -109,7 +109,7 @@ mod tests { ) .unwrap(); assert!( - Verifier::verify::, Felt, _>>( + Verifier::, Felt, _>>::verify( &proof, &pub_inputs, &lambda_proof_options, diff --git a/winterfell_adapter/src/examples/fibonacci_2_terms.rs b/winterfell_adapter/src/examples/fibonacci_2_terms.rs index babd4f9de3..49e8d9fd41 100644 --- a/winterfell_adapter/src/examples/fibonacci_2_terms.rs +++ b/winterfell_adapter/src/examples/fibonacci_2_terms.rs @@ -87,15 +87,15 @@ pub fn build_trace(sequence_length: usize) -> TraceTable { mod tests { use miden_core::Felt; use stark_platinum_prover::{ - proof::options::ProofOptions, prover::IsStarkProver, verifier::IsStarkVerifier, + proof::options::ProofOptions, + prover::{IsStarkProver, Prover}, + verifier::{IsStarkVerifier, Verifier}, }; use winter_air::TraceInfo; use winter_prover::{Trace, TraceTable}; use crate::{ - adapter::{ - air::AirAdapter, public_inputs::AirAdapterPublicInputs, Prover, Transcript, Verifier, - }, + adapter::{air::AirAdapter, public_inputs::AirAdapterPublicInputs, Transcript}, examples::fibonacci_2_terms::{self, FibAir2Terms}, }; @@ -115,7 +115,7 @@ mod tests { metadata: (), }; - let proof = Prover::prove::, Felt, _>>( + let proof = Prover::, Felt, _>>::prove( &trace, &pub_inputs, &lambda_proof_options, @@ -123,13 +123,13 @@ mod tests { ) .unwrap(); - assert!(Verifier::verify::< - AirAdapter, Felt, _>, - >( - &proof, - &pub_inputs, - &lambda_proof_options, - Transcript::new(&[]), - )); + assert!( + Verifier::, Felt, _>>::verify( + &proof, + &pub_inputs, + &lambda_proof_options, + Transcript::new(&[]), + ) + ); } } diff --git a/winterfell_adapter/src/examples/fibonacci_rap.rs b/winterfell_adapter/src/examples/fibonacci_rap.rs index f46e4e94ea..a878fd708f 100644 --- a/winterfell_adapter/src/examples/fibonacci_rap.rs +++ b/winterfell_adapter/src/examples/fibonacci_rap.rs @@ -242,15 +242,15 @@ pub fn build_trace(sequence_length: usize) -> TraceTable { mod tests { use miden_core::Felt; use stark_platinum_prover::{ - proof::options::ProofOptions, prover::IsStarkProver, verifier::IsStarkVerifier, + proof::options::ProofOptions, + prover::{IsStarkProver, Prover}, + verifier::{IsStarkVerifier, Verifier}, }; use winter_air::{TraceInfo, TraceLayout}; use winter_prover::Trace; use crate::{ - adapter::{ - air::AirAdapter, public_inputs::AirAdapterPublicInputs, Prover, Transcript, Verifier, - }, + adapter::{air::AirAdapter, public_inputs::AirAdapterPublicInputs, Transcript}, examples::fibonacci_rap::{self, FibonacciRAP, RapTraceTable}, }; @@ -273,16 +273,16 @@ mod tests { metadata: (), }; - let proof = Prover::prove::, Felt, _>>( + let proof = Prover::, Felt, _>>::prove( &trace, &pub_inputs, &lambda_proof_options, Transcript::new(&[]), ) .unwrap(); - assert!(Verifier::verify::< + assert!(Verifier::< AirAdapter, Felt, _>, - >( + >::verify( &proof, &pub_inputs, &lambda_proof_options, diff --git a/winterfell_adapter/src/examples/miden_vm.rs b/winterfell_adapter/src/examples/miden_vm.rs index 0f6da58f68..42d1b9abf2 100644 --- a/winterfell_adapter/src/examples/miden_vm.rs +++ b/winterfell_adapter/src/examples/miden_vm.rs @@ -46,7 +46,7 @@ impl FromColumns for ExecutionTrace { mod tests { use crate::adapter::air::AirAdapter; use crate::adapter::public_inputs::AirAdapterPublicInputs; - use crate::adapter::{Prover, Transcript, Verifier}; + use crate::adapter::Transcript; use crate::examples::fibonacci_2_terms::FibAir2Terms; use miden_air::{ProcessorAir, ProvingOptions, PublicInputs}; use miden_assembly::Assembler; @@ -54,6 +54,8 @@ mod tests { use miden_processor::DefaultHost; use miden_processor::{self as processor}; use processor::ExecutionTrace; + use stark_platinum_prover::prover::Prover; + use stark_platinum_prover::verifier::Verifier; use stark_platinum_prover::{ proof::options::ProofOptions, prover::IsStarkProver, verifier::IsStarkVerifier, }; @@ -93,7 +95,7 @@ mod tests { winter_trace.main_segment().clone(), ); - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &lambda_proof_options, @@ -101,14 +103,14 @@ mod tests { ) .unwrap(); - assert!(Verifier::verify::< - AirAdapter, - >( - &proof, - &pub_inputs, - &lambda_proof_options, - Transcript::new(&[]), - )); + assert!( + Verifier::>::verify( + &proof, + &pub_inputs, + &lambda_proof_options, + Transcript::new(&[]), + ) + ); } fn compute_fibonacci(n: usize) -> Felt { @@ -171,7 +173,7 @@ mod tests { winter_trace.main_segment().clone(), ); - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &lambda_proof_options, @@ -179,13 +181,13 @@ mod tests { ) .unwrap(); - assert!(Verifier::verify::< - AirAdapter, - >( - &proof, - &pub_inputs, - &lambda_proof_options, - Transcript::new(&[]), - )); + assert!( + Verifier::>::verify( + &proof, + &pub_inputs, + &lambda_proof_options, + Transcript::new(&[]), + ) + ); } } From f857a3abe096c2956325dbd5a9487b44a4c43be2 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> Date: Wed, 20 Dec 2023 16:51:31 -0300 Subject: [PATCH 20/29] Update Winterfell AIR Adapter for field extensions (#720) * wip * cast to fieldextension * fmt * clippy * fmt * fix parallel * prover with generic air argument * verifier with generic argument * clippy, fmt * wip, prover refactor * wip * fix stone serialization * add safe scope for unwraps * minor refactor * clippy, fmt * remove commented code. Fix typo in docs * fix compilation error * fmt * fix parallel * remove unnecessary trait bound * fix wasm * fix wasm * change wasm proof bytes * fix number of bytes * fix wasm proof * fix number of bytes * wip * refactor airadapter to support field extensions * fmt * fmt --- math/src/field/fields/winterfell.rs | 137 +++++++++++++++++- math/src/polynomial.rs | 2 +- provers/stark/src/traits.rs | 2 +- winterfell_adapter/benches/proving.rs | 20 ++- winterfell_adapter/src/adapter/air.rs | 58 +++++--- winterfell_adapter/src/adapter/mod.rs | 44 +++++- winterfell_adapter/src/examples/cubic.rs | 17 ++- .../src/examples/fibonacci_2_terms.rs | 24 +-- .../src/examples/fibonacci_rap.rs | 13 +- winterfell_adapter/src/examples/miden_vm.rs | 59 ++++---- winterfell_adapter/src/utils.rs | 23 +-- 11 files changed, 281 insertions(+), 118 deletions(-) diff --git a/math/src/field/fields/winterfell.rs b/math/src/field/fields/winterfell.rs index 8d06f7347b..b9ea51cf51 100644 --- a/math/src/field/fields/winterfell.rs +++ b/math/src/field/fields/winterfell.rs @@ -1,16 +1,19 @@ +use core::ops::Add; + use crate::{ errors::ByteConversionError, field::{ element::FieldElement, errors::FieldError, - traits::{IsFFTField, IsField, IsPrimeField}, + traits::{IsFFTField, IsField, IsPrimeField, IsSubFieldOf}, }, traits::{ByteConversion, Serializable}, unsigned_integer::element::U256, }; pub use miden_core::Felt; +use miden_core::QuadExtension; pub use winter_math::fields::f128::BaseElement; -use winter_math::{FieldElement as IsWinterfellFieldElement, StarkField}; +use winter_math::{ExtensionOf, FieldElement as IsWinterfellFieldElement, StarkField}; impl IsFFTField for Felt { const TWO_ADICITY: u64 = ::TWO_ADICITY as u64; @@ -118,3 +121,133 @@ impl ByteConversion for Felt { } } } + +pub type QuadFelt = QuadExtension; + +impl ByteConversion for QuadFelt { + fn to_bytes_be(&self) -> Vec { + let [b0, b1] = self.to_base_elements(); + let mut bytes = b0.to_bytes_be(); + bytes.extend(&b1.to_bytes_be()); + bytes + } + + fn to_bytes_le(&self) -> Vec { + let [b0, b1] = self.to_base_elements(); + let mut bytes = b0.to_bytes_le(); + bytes.extend(&b1.to_bytes_be()); + bytes + } + + fn from_bytes_be(_bytes: &[u8]) -> Result + where + Self: Sized, + { + todo!() + } + + fn from_bytes_le(_bytes: &[u8]) -> Result + where + Self: Sized, + { + todo!() + } +} + +impl Serializable for FieldElement { + fn serialize(&self) -> Vec { + let [b0, b1] = self.value().to_base_elements(); + let mut bytes = b0.to_bytes_be(); + bytes.extend(&b1.to_bytes_be()); + bytes + } +} + +impl IsField for QuadFelt { + type BaseType = QuadFelt; + + fn add(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + *a + *b + } + + fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + *a * *b + } + + fn sub(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + *a - *b + } + + fn neg(a: &Self::BaseType) -> Self::BaseType { + -*a + } + + fn inv(a: &Self::BaseType) -> Result { + Ok((*a).inv()) + } + + fn div(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + *a / *b + } + + fn eq(a: &Self::BaseType, b: &Self::BaseType) -> bool { + *a == *b + } + + fn zero() -> Self::BaseType { + Self::BaseType::ZERO + } + + fn one() -> Self::BaseType { + Self::BaseType::ONE + } + + fn from_u64(x: u64) -> Self::BaseType { + Self::BaseType::from(x) + } + + fn from_base_type(x: Self::BaseType) -> Self::BaseType { + x + } +} + +impl IsSubFieldOf for Felt { + fn mul( + a: &Self::BaseType, + b: &::BaseType, + ) -> ::BaseType { + b.mul_base(*a) + } + + fn add( + a: &Self::BaseType, + b: &::BaseType, + ) -> ::BaseType { + let [b0, b1] = b.to_base_elements(); + QuadFelt::new(b0.add(*a), b1) + } + + fn div( + a: &Self::BaseType, + b: &::BaseType, + ) -> ::BaseType { + let b_inv = b.inv(); + >::mul(a, &b_inv) + } + + fn sub( + a: &Self::BaseType, + b: &::BaseType, + ) -> ::BaseType { + let [b0, b1] = b.to_base_elements(); + QuadFelt::new(a.add(-(b0)), -b1) + } + + fn embed(a: Self::BaseType) -> ::BaseType { + QuadFelt::new(a, Felt::ZERO) + } + + fn to_subfield_vec(b: ::BaseType) -> Vec { + b.to_base_elements().to_vec() + } +} diff --git a/math/src/polynomial.rs b/math/src/polynomial.rs index 4a4008abe6..cee58d339c 100644 --- a/math/src/polynomial.rs +++ b/math/src/polynomial.rs @@ -157,7 +157,7 @@ impl Polynomial> { coefficients.push(c.clone()); c = coeff + c * b; } - coefficients.reverse(); + coefficients = coefficients.into_iter().rev().collect(); Polynomial::new(&coefficients) } else { Polynomial::zero() diff --git a/provers/stark/src/traits.rs b/provers/stark/src/traits.rs index 94e028bfdb..7099dd9263 100644 --- a/provers/stark/src/traits.rs +++ b/provers/stark/src/traits.rs @@ -50,7 +50,7 @@ pub trait AIR { frame: &Frame, periodic_values: &[FieldElement], rap_challenges: &Self::RAPChallenges, - ) -> Vec>; + ) -> Vec>; fn boundary_constraints( &self, diff --git a/winterfell_adapter/benches/proving.rs b/winterfell_adapter/benches/proving.rs index 0a05aa356c..b82bb284c8 100644 --- a/winterfell_adapter/benches/proving.rs +++ b/winterfell_adapter/benches/proving.rs @@ -1,18 +1,17 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; use miden_air::{HashFunction, ProcessorAir, ProvingOptions, PublicInputs}; use miden_assembly::Assembler; -use miden_core::{Felt, Program, StackInputs}; +use miden_core::{Program, StackInputs}; use miden_processor::DefaultHost; use miden_processor::{self as processor}; use miden_prover::prove; -use processor::ExecutionTrace; use stark_platinum_prover::proof::options::ProofOptions; use stark_platinum_prover::prover::{IsStarkProver, Prover}; use winter_air::FieldExtension; use winter_prover::Trace; use winterfell_adapter::adapter::public_inputs::AirAdapterPublicInputs; -use winterfell_adapter::adapter::{air::AirAdapter, Transcript}; -use winterfell_adapter::examples::miden_vm::ExecutionTraceMetadata; +use winterfell_adapter::adapter::QuadFeltTranscript; +use winterfell_adapter::examples::miden_vm::{ExecutionTraceMetadata, MidenVMQuadFeltAir}; struct BenchInstance { program: Program, @@ -50,7 +49,7 @@ pub fn bench_prove_miden_fibonacci(c: &mut Criterion) { instance.lambda_proof_options.fri_number_of_queries, instance.lambda_proof_options.blowup_factor as usize, instance.lambda_proof_options.grinding_factor as u32, - FieldExtension::None, + FieldExtension::Quadratic, 2, 0, HashFunction::Blake3_192, @@ -97,17 +96,16 @@ pub fn bench_prove_miden_fibonacci(c: &mut Criterion) { winter_trace.clone().into(), ); - let trace = - AirAdapter::::convert_winterfell_trace_table( - winter_trace.main_segment().clone(), - ); + let trace = MidenVMQuadFeltAir::convert_winterfell_trace_table( + winter_trace.main_segment().clone(), + ); let _proof = black_box( - Prover::>::prove( + Prover::::prove( &trace, &pub_inputs, &instance.lambda_proof_options, - Transcript::new(&[]), + QuadFeltTranscript::new(&[]), ) .unwrap(), ); diff --git a/winterfell_adapter/src/adapter/air.rs b/winterfell_adapter/src/adapter/air.rs index d38e2c6b15..d539ed84df 100644 --- a/winterfell_adapter/src/adapter/air.rs +++ b/winterfell_adapter/src/adapter/air.rs @@ -2,7 +2,7 @@ use crate::utils::{ matrix_lambda2winter, matrix_winter2lambda, vec_lambda2winter, vec_winter2lambda, }; use lambdaworks_math::field::element::FieldElement; -use lambdaworks_math::field::traits::{IsFFTField, IsField}; +use lambdaworks_math::field::traits::{IsFFTField, IsField, IsSubFieldOf}; use lambdaworks_math::traits::ByteConversion; use miden_core::Felt; use stark_platinum_prover::{ @@ -27,9 +27,15 @@ impl FromColumns for TraceTable { } #[derive(Clone)] -pub struct AirAdapter +pub struct AirAdapter where - FE: IsWinterfellFieldElement + StarkField + ByteConversion + Unpin + IsFFTField, + FE: IsWinterfellFieldElement + + StarkField + + ByteConversion + + Unpin + + IsFFTField + + IsSubFieldOf, + E: IsField, A: Air, A::PublicInputs: Clone, T: Trace + Clone + FromColumns, @@ -38,17 +44,20 @@ where winterfell_air: A, public_inputs: AirAdapterPublicInputs, air_context: stark_platinum_prover::context::AirContext, - phantom: PhantomData, + trace: PhantomData, + extension: PhantomData, } -impl AirAdapter +impl AirAdapter where FE: IsWinterfellFieldElement + StarkField + ByteConversion + Unpin + IsFFTField - + IsField, + + IsField + + IsSubFieldOf, + E: IsField + IsWinterfellFieldElement, A: Air + Clone, A::PublicInputs: Clone, T: Trace + Clone + FromColumns, @@ -66,22 +75,24 @@ where } } -impl AIR for AirAdapter +impl AIR for AirAdapter where FE: IsWinterfellFieldElement + StarkField + ByteConversion + Unpin + IsFFTField - + IsField, + + IsField + + IsSubFieldOf, + E: IsField + IsWinterfellFieldElement, A: Air + Clone, A::PublicInputs: Clone, T: Trace + Clone + FromColumns, M: Clone, { type Field = FE; - type FieldExtension = FE; - type RAPChallenges = Vec; + type FieldExtension = E; + type RAPChallenges = Vec; type PublicInputs = AirAdapterPublicInputs; const STEP_SIZE: usize = 1; @@ -118,7 +129,8 @@ where winterfell_air, public_inputs: pub_inputs.clone(), air_context: lambda_context, - phantom: PhantomData, + trace: PhantomData, + extension: PhantomData, } } @@ -126,7 +138,7 @@ where &self, main_trace: &stark_platinum_prover::trace::TraceTable, rap_challenges: &Self::RAPChallenges, - ) -> stark_platinum_prover::trace::TraceTable { + ) -> stark_platinum_prover::trace::TraceTable { // We support at most a one-stage RAP. This covers most use cases. if let Some(winter_trace) = T::from_cols( matrix_lambda2winter(&main_trace.columns()), @@ -138,18 +150,18 @@ where for i in 0..winter_trace.num_cols() { columns.push(winter_trace.get_column(i).to_owned()); } - stark_platinum_prover::trace::TraceTable::from_columns( + stark_platinum_prover::trace::TraceTable::::from_columns( matrix_winter2lambda(&columns), 1, ) } else { - stark_platinum_prover::trace::TraceTable::::empty() + stark_platinum_prover::trace::TraceTable::::empty() } } fn build_rap_challenges( &self, - transcript: &mut impl stark_platinum_prover::transcript::IsStarkTranscript, + transcript: &mut impl stark_platinum_prover::transcript::IsStarkTranscript, ) -> Self::RAPChallenges { let trace_layout = self.winterfell_air.trace_layout(); let num_segments = trace_layout.num_aux_segments(); @@ -180,10 +192,10 @@ where fn compute_transition( &self, - frame: &stark_platinum_prover::frame::Frame, - periodic_values: &[FieldElement], + frame: &stark_platinum_prover::frame::Frame, + periodic_values: &[FieldElement], rap_challenges: &Self::RAPChallenges, - ) -> Vec> { + ) -> Vec> { let num_aux_columns = self.number_auxiliary_rap_columns(); let num_main_columns = self.context().trace_columns - num_aux_columns; @@ -205,7 +217,7 @@ where ]; let mut main_result_winter = vec_lambda2winter(&main_result); - self.winterfell_air.evaluate_transition::( + self.winterfell_air.evaluate_transition::( &main_frame, &periodic_values, &mut main_result_winter, @@ -248,17 +260,17 @@ where fn boundary_constraints( &self, rap_challenges: &Self::RAPChallenges, - ) -> stark_platinum_prover::constraints::boundary::BoundaryConstraints { + ) -> stark_platinum_prover::constraints::boundary::BoundaryConstraints { let num_aux_columns = self.number_auxiliary_rap_columns(); let num_main_columns = self.context().trace_columns - num_aux_columns; let mut result = Vec::new(); for assertion in self.winterfell_air.get_assertions() { assert!(assertion.is_single()); - result.push(BoundaryConstraint::new( + result.push(BoundaryConstraint::::new( assertion.column(), assertion.first_step(), - FieldElement::::const_from_raw(assertion.values()[0]), + FieldElement::::const_from_raw(assertion.values()[0]).to_extension(), )); } @@ -270,7 +282,7 @@ where result.push(BoundaryConstraint::new( assertion.column() + num_main_columns, assertion.first_step(), - FieldElement::::const_from_raw(assertion.values()[0]), + FieldElement::::const_from_raw(assertion.values()[0]), )); } diff --git a/winterfell_adapter/src/adapter/mod.rs b/winterfell_adapter/src/adapter/mod.rs index c0ef319c7f..365f0850df 100644 --- a/winterfell_adapter/src/adapter/mod.rs +++ b/winterfell_adapter/src/adapter/mod.rs @@ -1,4 +1,4 @@ -use lambdaworks_math::traits::ByteConversion; +use lambdaworks_math::{field::fields::winterfell::QuadFelt, traits::ByteConversion}; use miden_core::Felt; use sha3::{Digest, Keccak256}; use stark_platinum_prover::{fri::FieldElement, transcript::IsStarkTranscript}; @@ -7,11 +7,11 @@ use winter_math::StarkField; pub mod air; pub mod public_inputs; -pub struct Transcript { +pub struct FeltTranscript { hasher: Keccak256, } -impl Transcript { +impl FeltTranscript { pub fn new(data: &[u8]) -> Self { let mut res = Self { hasher: Keccak256::new(), @@ -21,7 +21,7 @@ impl Transcript { } } -impl IsStarkTranscript for Transcript { +impl IsStarkTranscript for FeltTranscript { fn append_field_element(&mut self, element: &FieldElement) { self.append_bytes(&element.value().to_bytes_be()); } @@ -49,3 +49,39 @@ impl IsStarkTranscript for Transcript { u64::from_be_bytes(self.state()[..8].try_into().unwrap()) % upper_bound } } + +pub struct QuadFeltTranscript { + felt_transcript: FeltTranscript, +} + +impl QuadFeltTranscript { + pub fn new(data: &[u8]) -> Self { + Self { + felt_transcript: FeltTranscript::new(data), + } + } +} + +impl IsStarkTranscript for QuadFeltTranscript { + fn append_field_element(&mut self, element: &FieldElement) { + self.append_bytes(&element.value().to_bytes_be()); + } + + fn append_bytes(&mut self, new_bytes: &[u8]) { + self.felt_transcript.append_bytes(new_bytes); + } + + fn state(&self) -> [u8; 32] { + self.felt_transcript.state() + } + + fn sample_field_element(&mut self) -> FieldElement { + let x = self.felt_transcript.sample_field_element(); + let y = self.felt_transcript.sample_field_element(); + FieldElement::const_from_raw(QuadFelt::new(*x.value(), *y.value())) + } + + fn sample_u64(&mut self, upper_bound: u64) -> u64 { + u64::from_be_bytes(self.state()[..8].try_into().unwrap()) % upper_bound + } +} diff --git a/winterfell_adapter/src/examples/cubic.rs b/winterfell_adapter/src/examples/cubic.rs index c9ac74a0d8..b250abea1d 100644 --- a/winterfell_adapter/src/examples/cubic.rs +++ b/winterfell_adapter/src/examples/cubic.rs @@ -82,7 +82,7 @@ mod tests { use winter_prover::{Trace, TraceTable}; use crate::{ - adapter::{air::AirAdapter, public_inputs::AirAdapterPublicInputs, Transcript}, + adapter::{air::AirAdapter, public_inputs::AirAdapterPublicInputs, FeltTranscript}, examples::cubic::{self, Cubic}, }; @@ -90,9 +90,10 @@ mod tests { fn prove_and_verify_a_winterfell_cubic_air() { let lambda_proof_options = ProofOptions::default_test_options(); let winter_trace = cubic::build_trace(16); - let trace = AirAdapter::, Felt, ()>::convert_winterfell_trace_table( - winter_trace.main_segment().clone(), - ); + let trace = + AirAdapter::, Felt, Felt, ()>::convert_winterfell_trace_table( + winter_trace.main_segment().clone(), + ); let pub_inputs = AirAdapterPublicInputs { winterfell_public_inputs: *trace.columns()[0][15].value(), transition_exemptions: vec![1], @@ -101,19 +102,19 @@ mod tests { metadata: (), }; - let proof = Prover::, Felt, _>>::prove( + let proof = Prover::, Felt, Felt, _>>::prove( &trace, &pub_inputs, &lambda_proof_options, - Transcript::new(&[]), + FeltTranscript::new(&[]), ) .unwrap(); assert!( - Verifier::, Felt, _>>::verify( + Verifier::, Felt, Felt, _>>::verify( &proof, &pub_inputs, &lambda_proof_options, - Transcript::new(&[]), + FeltTranscript::new(&[]), ) ); } diff --git a/winterfell_adapter/src/examples/fibonacci_2_terms.rs b/winterfell_adapter/src/examples/fibonacci_2_terms.rs index 49e8d9fd41..227f8ab2ae 100644 --- a/winterfell_adapter/src/examples/fibonacci_2_terms.rs +++ b/winterfell_adapter/src/examples/fibonacci_2_terms.rs @@ -95,7 +95,7 @@ mod tests { use winter_prover::{Trace, TraceTable}; use crate::{ - adapter::{air::AirAdapter, public_inputs::AirAdapterPublicInputs, Transcript}, + adapter::{air::AirAdapter, public_inputs::AirAdapterPublicInputs, FeltTranscript}, examples::fibonacci_2_terms::{self, FibAir2Terms}, }; @@ -104,7 +104,7 @@ mod tests { let lambda_proof_options = ProofOptions::default_test_options(); let winter_trace = fibonacci_2_terms::build_trace(16); let trace = - AirAdapter::, Felt, ()>::convert_winterfell_trace_table( + AirAdapter::, Felt, Felt, ()>::convert_winterfell_trace_table( winter_trace.main_segment().clone(), ); let pub_inputs = AirAdapterPublicInputs { @@ -115,21 +115,21 @@ mod tests { metadata: (), }; - let proof = Prover::, Felt, _>>::prove( + let proof = Prover::, Felt, Felt, _>>::prove( &trace, &pub_inputs, &lambda_proof_options, - Transcript::new(&[]), + FeltTranscript::new(&[]), ) .unwrap(); - assert!( - Verifier::, Felt, _>>::verify( - &proof, - &pub_inputs, - &lambda_proof_options, - Transcript::new(&[]), - ) - ); + assert!(Verifier::< + AirAdapter, Felt, Felt, _>, + >::verify( + &proof, + &pub_inputs, + &lambda_proof_options, + FeltTranscript::new(&[]), + )); } } diff --git a/winterfell_adapter/src/examples/fibonacci_rap.rs b/winterfell_adapter/src/examples/fibonacci_rap.rs index a878fd708f..7315c3d099 100644 --- a/winterfell_adapter/src/examples/fibonacci_rap.rs +++ b/winterfell_adapter/src/examples/fibonacci_rap.rs @@ -240,6 +240,7 @@ pub fn build_trace(sequence_length: usize) -> TraceTable { #[cfg(test)] mod tests { + use lambdaworks_math::field::fields::winterfell::QuadFelt; use miden_core::Felt; use stark_platinum_prover::{ proof::options::ProofOptions, @@ -250,7 +251,7 @@ mod tests { use winter_prover::Trace; use crate::{ - adapter::{air::AirAdapter, public_inputs::AirAdapterPublicInputs, Transcript}, + adapter::{air::AirAdapter, public_inputs::AirAdapterPublicInputs, QuadFeltTranscript}, examples::fibonacci_rap::{self, FibonacciRAP, RapTraceTable}, }; @@ -259,7 +260,7 @@ mod tests { let lambda_proof_options = ProofOptions::default_test_options(); let winter_trace = fibonacci_rap::build_trace(16); let trace = - AirAdapter::, Felt, ()>::convert_winterfell_trace_table( + AirAdapter::, Felt, QuadFelt, ()>::convert_winterfell_trace_table( winter_trace.main_segment().clone(), ); let trace_layout = TraceLayout::new(3, [1], [1]); @@ -273,20 +274,20 @@ mod tests { metadata: (), }; - let proof = Prover::, Felt, _>>::prove( + let proof = Prover::, Felt, QuadFelt, _>>::prove( &trace, &pub_inputs, &lambda_proof_options, - Transcript::new(&[]), + QuadFeltTranscript::new(&[]), ) .unwrap(); assert!(Verifier::< - AirAdapter, Felt, _>, + AirAdapter, Felt, QuadFelt, _>, >::verify( &proof, &pub_inputs, &lambda_proof_options, - Transcript::new(&[]), + QuadFeltTranscript::new(&[]), )); } } diff --git a/winterfell_adapter/src/examples/miden_vm.rs b/winterfell_adapter/src/examples/miden_vm.rs index 42d1b9abf2..89208f7876 100644 --- a/winterfell_adapter/src/examples/miden_vm.rs +++ b/winterfell_adapter/src/examples/miden_vm.rs @@ -1,9 +1,14 @@ +use lambdaworks_math::field::fields::winterfell::QuadFelt; +use miden_air::ProcessorAir; use miden_core::{Felt, ProgramInfo, StackOutputs}; use miden_processor::{AuxTraceHints, ExecutionTrace, TraceLenSummary}; use winter_air::TraceLayout; use winter_prover::ColMatrix; -use crate::adapter::air::FromColumns; +use crate::adapter::air::{AirAdapter, FromColumns}; + +pub type MidenVMQuadFeltAir = + AirAdapter; #[derive(Clone)] pub struct ExecutionTraceMetadata { @@ -44,16 +49,14 @@ impl FromColumns for ExecutionTrace { #[cfg(test)] mod tests { - use crate::adapter::air::AirAdapter; use crate::adapter::public_inputs::AirAdapterPublicInputs; - use crate::adapter::Transcript; - use crate::examples::fibonacci_2_terms::FibAir2Terms; - use miden_air::{ProcessorAir, ProvingOptions, PublicInputs}; + use crate::adapter::QuadFeltTranscript; + use crate::examples::miden_vm::MidenVMQuadFeltAir; + use miden_air::{ProvingOptions, PublicInputs}; use miden_assembly::Assembler; use miden_core::{Felt, StackInputs}; use miden_processor::DefaultHost; use miden_processor::{self as processor}; - use processor::ExecutionTrace; use stark_platinum_prover::prover::Prover; use stark_platinum_prover::verifier::Verifier; use stark_platinum_prover::{ @@ -91,26 +94,22 @@ mod tests { }; let trace = - AirAdapter::::convert_winterfell_trace_table( - winter_trace.main_segment().clone(), - ); + MidenVMQuadFeltAir::convert_winterfell_trace_table(winter_trace.main_segment().clone()); - let proof = Prover::>::prove( + let proof = Prover::::prove( &trace, &pub_inputs, &lambda_proof_options, - Transcript::new(&[]), + QuadFeltTranscript::new(&[]), ) .unwrap(); - assert!( - Verifier::>::verify( - &proof, - &pub_inputs, - &lambda_proof_options, - Transcript::new(&[]), - ) - ); + assert!(Verifier::::verify( + &proof, + &pub_inputs, + &lambda_proof_options, + QuadFeltTranscript::new(&[]), + )); } fn compute_fibonacci(n: usize) -> Felt { @@ -169,25 +168,21 @@ mod tests { }; let trace = - AirAdapter::::convert_winterfell_trace_table( - winter_trace.main_segment().clone(), - ); + MidenVMQuadFeltAir::convert_winterfell_trace_table(winter_trace.main_segment().clone()); - let proof = Prover::>::prove( + let proof = Prover::::prove( &trace, &pub_inputs, &lambda_proof_options, - Transcript::new(&[]), + QuadFeltTranscript::new(&[]), ) .unwrap(); - assert!( - Verifier::>::verify( - &proof, - &pub_inputs, - &lambda_proof_options, - Transcript::new(&[]), - ) - ); + assert!(Verifier::::verify( + &proof, + &pub_inputs, + &lambda_proof_options, + QuadFeltTranscript::new(&[]), + )); } } diff --git a/winterfell_adapter/src/utils.rs b/winterfell_adapter/src/utils.rs index 8fa8422c38..12591cd7cb 100644 --- a/winterfell_adapter/src/utils.rs +++ b/winterfell_adapter/src/utils.rs @@ -1,37 +1,24 @@ -use lambdaworks_math::{field::traits::IsField, traits::ByteConversion}; +use lambdaworks_math::field::traits::IsField; use stark_platinum_prover::fri::FieldElement; -use winter_math::FieldElement as IsWinterfellFieldElement; -pub fn vec_lambda2winter< - FE: IsField + IsWinterfellFieldElement + ByteConversion + Unpin, ->( - input: &[FieldElement], -) -> Vec { +pub fn vec_lambda2winter + Copy>(input: &[FieldElement]) -> Vec { input.iter().map(|&e| *e.value()).collect() } -pub fn vec_winter2lambda< - FE: IsField + IsWinterfellFieldElement + ByteConversion + Unpin, ->( - input: &[FE], -) -> Vec> { +pub fn vec_winter2lambda + Copy>(input: &[FE]) -> Vec> { input .iter() .map(|&e| FieldElement::::const_from_raw(e)) .collect() } -pub fn matrix_lambda2winter< - FE: IsField + IsWinterfellFieldElement + ByteConversion + Unpin, ->( +pub fn matrix_lambda2winter + Copy>( input: &[Vec>], ) -> Vec> { input.iter().map(|v| vec_lambda2winter(v)).collect() } -pub fn matrix_winter2lambda< - FE: IsField + IsWinterfellFieldElement + ByteConversion + Unpin, ->( +pub fn matrix_winter2lambda + Copy>( input: &[Vec], ) -> Vec>> { input.iter().map(|v| vec_winter2lambda(v)).collect() From e1a8716b1bed1647b560c2b98e42f65134499161 Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Wed, 20 Dec 2023 16:59:11 -0300 Subject: [PATCH 21/29] perf: implement new radix 4 NR NTT (#725) * perf: implement new radix 4 NR NTT Measured locally on M1: 4-5% throughput improvement over radix 2. Criterion benchmark added. * cargo fmt * clippy * Update comments and assert --- math/benches/criterion_fft.rs | 19 +++++- math/benches/utils/fft_functions.rs | 6 +- math/src/fft/cpu/fft.rs | 95 ++++++++++++++++++++++++++++- 3 files changed, 116 insertions(+), 4 deletions(-) diff --git a/math/benches/criterion_fft.rs b/math/benches/criterion_fft.rs index 8d1c761351..5ee486caa2 100644 --- a/math/benches/criterion_fft.rs +++ b/math/benches/criterion_fft.rs @@ -6,7 +6,7 @@ use utils::stark252_utils; mod utils; -const SIZE_ORDERS: [u64; 4] = [21, 22, 23, 24]; +const SIZE_ORDERS: [u64; 5] = [20, 21, 22, 23, 24]; pub fn fft_benchmarks(c: &mut Criterion) { let mut group = c.benchmark_group("Ordered FFT"); @@ -22,7 +22,7 @@ pub fn fft_benchmarks(c: &mut Criterion) { group.bench_with_input( "Sequential from NR radix2", - &(input_nat, twiddles_bitrev), + &(input_nat.clone(), twiddles_bitrev.clone()), |bench, (input, twiddles)| { bench.iter_batched( || input.clone(), @@ -46,6 +46,21 @@ pub fn fft_benchmarks(c: &mut Criterion) { ); }, ); + if order % 2 == 0 { + group.bench_with_input( + "Sequential from NR radix4", + &(input_nat, twiddles_bitrev), + |bench, (input, twiddles)| { + bench.iter_batched( + || input.clone(), + |mut input| { + fft_functions::ordered_fft_nr4(&mut input, twiddles); + }, + BatchSize::LargeInput, + ); + }, + ); + } } group.finish(); diff --git a/math/benches/utils/fft_functions.rs b/math/benches/utils/fft_functions.rs index 2b2f50fb65..2bb587599e 100644 --- a/math/benches/utils/fft_functions.rs +++ b/math/benches/utils/fft_functions.rs @@ -3,7 +3,7 @@ use criterion::black_box; use lambdaworks_math::fft::cpu::{ bit_reversing::in_place_bit_reverse_permute, - fft::{in_place_nr_2radix_fft, in_place_rn_2radix_fft}, + fft::{in_place_nr_2radix_fft, in_place_nr_4radix_fft, in_place_rn_2radix_fft}, roots_of_unity::get_twiddles, }; use lambdaworks_math::{field::traits::RootsConfig, polynomial::Polynomial}; @@ -18,6 +18,10 @@ pub fn ordered_fft_rn(input: &mut [FE], twiddles: &[FE]) { in_place_rn_2radix_fft(input, twiddles); } +pub fn ordered_fft_nr4(input: &mut [FE], twiddles: &[FE]) { + in_place_nr_4radix_fft(input, twiddles); +} + pub fn twiddles_generation(order: u64, config: RootsConfig) { get_twiddles::(order, config).unwrap(); } diff --git a/math/src/fft/cpu/fft.rs b/math/src/fft/cpu/fft.rs index 724ca9ca04..e2910f8cd6 100644 --- a/math/src/fft/cpu/fft.rs +++ b/math/src/fft/cpu/fft.rs @@ -104,6 +104,79 @@ where } } +/// In-Place Radix-4 NR DIT FFT algorithm over a slice of two-adic field elements. +/// It's required that the twiddle factors are in bit-reverse order. Else this function will not +/// return fourier transformed values. +/// Also the input size needs to be a power of two. +/// It's recommended to use the current safe abstractions instead of this function. +/// +/// Performs a fast fourier transform with the next attributes: +/// - In-Place: an auxiliary vector of data isn't needed for the algorithm. +/// - Radix-4: the algorithm halves the problem size log(n) times. +/// - NR: natural to reverse order, meaning that the input is naturally ordered and the output will +/// be bit-reversed ordered. +/// - DIT: decimation in time +pub fn in_place_nr_4radix_fft(input: &mut [FieldElement], twiddles: &[FieldElement]) +where + F: IsFFTField + IsSubFieldOf, + E: IsField, +{ + debug_assert!(input.len().is_power_of_two()); + debug_assert!(input.len().ilog2() % 2 == 0); // Even power of 2 => x is power of 4 + + // divide input in groups, starting with 1, duplicating the number of groups in each stage. + let mut group_count = 1; + let mut group_size = input.len(); + + // for each group, there'll be group_size / 4 butterflies. + // a butterfly is the atomic operation of a FFT, e.g: + // x' = x + yw2 + zw1 + tw1w2 + // y' = x - yw2 + zw1 - tw1w2 + // z' = x + yw3 - zw1 - tw1w3 + // t' = x - yw3 - zw1 + tw1w3 + // The 0.25 factor is what gives FFT its performance, it recursively divides the problem size + // by 4 (group size). + + while group_count < input.len() { + #[allow(clippy::needless_range_loop)] // the suggestion would obfuscate a bit the algorithm + for group in 0..group_count { + let first_in_group = group * group_size; + let first_in_next_group = first_in_group + group_size / 4; + + let (w1, w2, w3) = ( + &twiddles[group], + &twiddles[2 * group], + &twiddles[2 * group + 1], + ); + + for i in first_in_group..first_in_next_group { + let (j, k, l) = ( + i + group_size / 4, + i + group_size / 2, + i + 3 * group_size / 4, + ); + + let zw1 = w1 * &input[k]; + let tw1 = w1 * &input[l]; + let a = w2 * (&input[j] + &tw1); + let b = w3 * (&input[j] - &tw1); + + let x = &input[i] + &zw1 + &a; + let y = &input[i] + &zw1 - &a; + let z = &input[i] - &zw1 + &b; + let t = &input[i] - &zw1 - &b; + + input[i] = x; + input[j] = y; + input[k] = z; + input[l] = t; + } + } + group_count *= 4; + group_size /= 4; + } +} + #[cfg(test)] mod tests { use crate::fft::cpu::bit_reversing::in_place_bit_reverse_permute; @@ -128,7 +201,12 @@ mod tests { } } prop_compose! { - fn field_vec(max_exp: u8)(vec in collection::vec(field_element(), 2..1< Vec { + fn field_vec(max_exp: u8)(vec in (1..max_exp).prop_flat_map(|i| collection::vec(field_element(), 1 << i))) -> Vec { + vec + } + } + prop_compose! { + fn field_vec_r4(max_exp: u8)(vec in (1..max_exp).prop_flat_map(|i| collection::vec(field_element(), 1 << (2 * i)))) -> Vec { vec } } @@ -163,5 +241,20 @@ mod tests { prop_assert_eq!(result, expected); } + + // Property-based test that ensures NR Radix-2 FFT gives the same result as a naive DFT. + #[test] + fn test_nr_4radix_fft_matches_naive_eval(coeffs in field_vec_r4(5)) { + let expected = naive_matrix_dft_test(&coeffs); + + let order = coeffs.len().trailing_zeros(); + let twiddles = get_twiddles(order.into(), RootsConfig::BitReverse).unwrap(); + + let mut result = coeffs; + in_place_nr_4radix_fft::(&mut result, &twiddles); + in_place_bit_reverse_permute(&mut result); + + prop_assert_eq!(expected, result); + } } } From c0e190fef1bb7046c1a583ebf1ec1b4d61b533ff Mon Sep 17 00:00:00 2001 From: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> Date: Wed, 27 Dec 2023 16:27:50 -0300 Subject: [PATCH 22/29] Stark: Prover and Verifier over field extensions v3 (#724) * wip * cast to fieldextension * fmt * clippy * fmt * fix parallel * prover with generic air argument * verifier with generic argument * clippy, fmt * wip, prover refactor * wip * fix stone serialization * add safe scope for unwraps * minor refactor * clippy, fmt * remove commented code. Fix typo in docs * fix compilation error * fmt * fix parallel * remove unnecessary trait bound * fix wasm * fix wasm * change wasm proof bytes * fix number of bytes * fix wasm proof * fix number of bytes * wip * wip, compiles * wip * wip. valid proofs * fix trace ood element send order * fix adapter * clippy * clippy * clippy * fmt * remove unnecessary methods * rename * minor refactor. Avoid clone * minor refactor * add docstrings * fix parallel * add docstrings * clippy * fix test * fix debug assert --- provers/cairo/src/air.rs | 309 ++++++++------ provers/cairo/src/tests/integration_tests.rs | 1 + provers/cairo/tests/wasm.rs | 402 +++++++++--------- provers/stark/src/constraints/boundary.rs | 38 +- provers/stark/src/constraints/evaluator.rs | 46 +- provers/stark/src/debug.rs | 48 ++- provers/stark/src/examples/dummy_air.rs | 27 +- .../src/examples/fibonacci_2_cols_shifted.rs | 25 +- .../stark/src/examples/fibonacci_2_columns.rs | 25 +- provers/stark/src/examples/fibonacci_rap.rs | 33 +- provers/stark/src/examples/quadratic_air.rs | 19 +- .../stark/src/examples/simple_fibonacci.rs | 23 +- .../src/examples/simple_periodic_cols.rs | 27 +- provers/stark/src/frame.rs | 33 +- provers/stark/src/proof/stark.rs | 11 +- provers/stark/src/prover.rs | 104 ++--- provers/stark/src/table.rs | 151 ++++++- provers/stark/src/trace.rs | 89 ++-- provers/stark/src/traits.rs | 29 +- provers/stark/src/verifier.rs | 37 +- winterfell_adapter/src/adapter/air.rs | 105 ++++- 21 files changed, 965 insertions(+), 617 deletions(-) diff --git a/provers/cairo/src/air.rs b/provers/cairo/src/air.rs index 4ce45d1305..f5e22c85b6 100644 --- a/provers/cairo/src/air.rs +++ b/provers/cairo/src/air.rs @@ -124,34 +124,34 @@ pub const EXTRA_VAL: usize = 34; pub const RC_HOLES: usize = 35; // Auxiliary range check columns -pub const RANGE_CHECK_COL_1: usize = 36; -pub const RANGE_CHECK_COL_2: usize = 37; -pub const RANGE_CHECK_COL_3: usize = 38; -pub const RANGE_CHECK_COL_4: usize = 39; +pub const RANGE_CHECK_COL_1: usize = 0; +pub const RANGE_CHECK_COL_2: usize = 1; +pub const RANGE_CHECK_COL_3: usize = 2; +pub const RANGE_CHECK_COL_4: usize = 3; // Auxiliary memory columns -pub const MEMORY_ADDR_SORTED_0: usize = 40; -pub const MEMORY_ADDR_SORTED_1: usize = 41; -pub const MEMORY_ADDR_SORTED_2: usize = 42; -pub const MEMORY_ADDR_SORTED_3: usize = 43; -pub const MEMORY_ADDR_SORTED_4: usize = 44; - -pub const MEMORY_VALUES_SORTED_0: usize = 45; -pub const MEMORY_VALUES_SORTED_1: usize = 46; -pub const MEMORY_VALUES_SORTED_2: usize = 47; -pub const MEMORY_VALUES_SORTED_3: usize = 48; -pub const MEMORY_VALUES_SORTED_4: usize = 49; - -pub const PERMUTATION_ARGUMENT_COL_0: usize = 50; -pub const PERMUTATION_ARGUMENT_COL_1: usize = 51; -pub const PERMUTATION_ARGUMENT_COL_2: usize = 52; -pub const PERMUTATION_ARGUMENT_COL_3: usize = 53; -pub const PERMUTATION_ARGUMENT_COL_4: usize = 54; - -pub const PERMUTATION_ARGUMENT_RANGE_CHECK_COL_1: usize = 55; -pub const PERMUTATION_ARGUMENT_RANGE_CHECK_COL_2: usize = 56; -pub const PERMUTATION_ARGUMENT_RANGE_CHECK_COL_3: usize = 57; -pub const PERMUTATION_ARGUMENT_RANGE_CHECK_COL_4: usize = 58; +pub const MEMORY_ADDR_SORTED_0: usize = 4; +pub const MEMORY_ADDR_SORTED_1: usize = 5; +pub const MEMORY_ADDR_SORTED_2: usize = 6; +pub const MEMORY_ADDR_SORTED_3: usize = 7; +pub const MEMORY_ADDR_SORTED_4: usize = 8; + +pub const MEMORY_VALUES_SORTED_0: usize = 9; +pub const MEMORY_VALUES_SORTED_1: usize = 10; +pub const MEMORY_VALUES_SORTED_2: usize = 11; +pub const MEMORY_VALUES_SORTED_3: usize = 12; +pub const MEMORY_VALUES_SORTED_4: usize = 13; + +pub const PERMUTATION_ARGUMENT_COL_0: usize = 14; +pub const PERMUTATION_ARGUMENT_COL_1: usize = 15; +pub const PERMUTATION_ARGUMENT_COL_2: usize = 16; +pub const PERMUTATION_ARGUMENT_COL_3: usize = 17; +pub const PERMUTATION_ARGUMENT_COL_4: usize = 18; + +pub const PERMUTATION_ARGUMENT_RANGE_CHECK_COL_1: usize = 19; +pub const PERMUTATION_ARGUMENT_RANGE_CHECK_COL_2: usize = 20; +pub const PERMUTATION_ARGUMENT_RANGE_CHECK_COL_3: usize = 21; +pub const PERMUTATION_ARGUMENT_RANGE_CHECK_COL_4: usize = 22; // Trace layout pub const MEM_P_TRACE_OFFSET: usize = 17; @@ -787,9 +787,9 @@ impl AIR for CairoAIR { 23 } - fn compute_transition( + fn compute_transition_prover( &self, - frame: &Frame, + frame: &Frame, _periodic_values: &[FieldElement], rap_challenges: &Self::RAPChallenges, ) -> Vec> { @@ -819,15 +819,17 @@ impl AIR for CairoAIR { &self, rap_challenges: &Self::RAPChallenges, ) -> BoundaryConstraints { - let initial_pc = BoundaryConstraint::new(MEM_A_TRACE_OFFSET, 0, self.pub_inputs.pc_init); - let initial_ap = BoundaryConstraint::new(MEM_P_TRACE_OFFSET, 0, self.pub_inputs.ap_init); + let initial_pc = + BoundaryConstraint::new_main(MEM_A_TRACE_OFFSET, 0, self.pub_inputs.pc_init); + let initial_ap = + BoundaryConstraint::new_main(MEM_P_TRACE_OFFSET, 0, self.pub_inputs.ap_init); - let final_pc = BoundaryConstraint::new( + let final_pc = BoundaryConstraint::new_main( MEM_A_TRACE_OFFSET, self.pub_inputs.num_steps - 1, self.pub_inputs.pc_final, ); - let final_ap = BoundaryConstraint::new( + let final_ap = BoundaryConstraint::new_main( MEM_P_TRACE_OFFSET, self.pub_inputs.num_steps - 1, self.pub_inputs.ap_final, @@ -853,19 +855,19 @@ impl AIR for CairoAIR { * cumulative_product; let permutation_final_constraint = - BoundaryConstraint::new(PERMUTATION_ARGUMENT_COL_4, final_index, permutation_final); + BoundaryConstraint::new_aux(PERMUTATION_ARGUMENT_COL_4, final_index, permutation_final); let one: FieldElement = FieldElement::one(); let range_check_final_constraint = - BoundaryConstraint::new(PERMUTATION_ARGUMENT_RANGE_CHECK_COL_4, final_index, one); + BoundaryConstraint::new_aux(PERMUTATION_ARGUMENT_RANGE_CHECK_COL_4, final_index, one); - let range_check_min = BoundaryConstraint::new( + let range_check_min = BoundaryConstraint::new_aux( RANGE_CHECK_COL_1, 0, FieldElement::from(self.pub_inputs.range_check_min.unwrap() as u64), ); - let range_check_max = BoundaryConstraint::new( + let range_check_max = BoundaryConstraint::new_aux( RANGE_CHECK_COL_4, final_index, FieldElement::from(self.pub_inputs.range_check_max.unwrap() as u64), @@ -900,17 +902,29 @@ impl AIR for CairoAIR { fn pub_inputs(&self) -> &Self::PublicInputs { &self.pub_inputs } + + fn compute_transition_verifier( + &self, + frame: &Frame, + periodic_values: &[FieldElement], + rap_challenges: &Self::RAPChallenges, + ) -> Vec> { + self.compute_transition_prover(frame, periodic_values, rap_challenges) + } } /// From the Cairo whitepaper, section 9.10 -fn compute_instr_constraints(constraints: &mut [Felt252], frame: &Frame) { +fn compute_instr_constraints( + constraints: &mut [Felt252], + frame: &Frame, +) { // These constraints are only applied over elements of the same row. let curr = frame.get_evaluation_step(0); // Bit-prefixes constraints. // See section 9.4 of Cairo whitepaper https://eprint.iacr.org/2021/1063.pdf. let flags: Vec<&Felt252> = (0..16) - .map(|col_idx| curr.get_evaluation_element(0, col_idx)) + .map(|col_idx| curr.get_main_evaluation_element(0, col_idx)) .collect(); let one = Felt252::one(); @@ -965,10 +979,10 @@ fn compute_instr_constraints(constraints: &mut [Felt252], frame: &Frame) { +fn compute_operand_constraints( + constraints: &mut [Felt252], + frame: &Frame, +) { // These constraints are only applied over elements of the same row. let curr = frame.get_evaluation_step(0); - let ap = curr.get_evaluation_element(0, FRAME_AP); - let fp = curr.get_evaluation_element(0, FRAME_FP); - let pc = curr.get_evaluation_element(0, FRAME_PC); + let ap = curr.get_main_evaluation_element(0, FRAME_AP); + let fp = curr.get_main_evaluation_element(0, FRAME_FP); + let pc = curr.get_main_evaluation_element(0, FRAME_PC); let dst_fp = into_bit_flag(curr, F_DST_FP); - let off_dst = curr.get_evaluation_element(0, OFF_DST); - let dst_addr = curr.get_evaluation_element(0, FRAME_DST_ADDR); + let off_dst = curr.get_main_evaluation_element(0, OFF_DST); + let dst_addr = curr.get_main_evaluation_element(0, FRAME_DST_ADDR); let op0_fp = into_bit_flag(curr, F_OP_0_FP); - let off_op0 = curr.get_evaluation_element(0, OFF_OP0); - let op0_addr = curr.get_evaluation_element(0, FRAME_OP0_ADDR); + let off_op0 = curr.get_main_evaluation_element(0, OFF_OP0); + let op0_addr = curr.get_main_evaluation_element(0, FRAME_OP0_ADDR); let op1_val = into_bit_flag(curr, F_OP_1_VAL); let op1_ap = into_bit_flag(curr, F_OP_1_AP); let op1_fp = into_bit_flag(curr, F_OP_1_FP); - let op0 = curr.get_evaluation_element(0, FRAME_OP0); - let off_op1 = curr.get_evaluation_element(0, OFF_OP1); - let op1_addr = curr.get_evaluation_element(0, FRAME_OP1_ADDR); + let op0 = curr.get_main_evaluation_element(0, FRAME_OP0); + let off_op1 = curr.get_main_evaluation_element(0, OFF_OP1); + let op1_addr = curr.get_main_evaluation_element(0, FRAME_OP1_ADDR); let one = Felt252::one(); let b15 = Felt252::from(2).pow(15u32); @@ -1030,37 +1047,43 @@ fn compute_operand_constraints(constraints: &mut [Felt252], frame: &Frame, element_idx: usize) -> Felt252 { - step.get_evaluation_element(0, element_idx) - - Felt252::from(2) * step.get_evaluation_element(0, element_idx + 1) +fn into_bit_flag( + step: &StepView, + element_idx: usize, +) -> Felt252 { + step.get_main_evaluation_element(0, element_idx) + - Felt252::from(2) * step.get_main_evaluation_element(0, element_idx + 1) } -fn compute_register_constraints(constraints: &mut [Felt252], frame: &Frame) { +fn compute_register_constraints( + constraints: &mut [Felt252], + frame: &Frame, +) { let curr = frame.get_evaluation_step(0); let next = frame.get_evaluation_step(1); let one = Felt252::one(); let two = Felt252::from(2); - let ap = curr.get_evaluation_element(0, FRAME_AP); - let next_ap = next.get_evaluation_element(0, FRAME_AP); + let ap = curr.get_main_evaluation_element(0, FRAME_AP); + let next_ap = next.get_main_evaluation_element(0, FRAME_AP); let ap_add = into_bit_flag(curr, F_AP_ADD); - let res = curr.get_evaluation_element(0, FRAME_RES); + let res = curr.get_main_evaluation_element(0, FRAME_RES); let ap_one = into_bit_flag(curr, F_AP_ONE); let opc_ret = into_bit_flag(curr, F_OPC_RET); let opc_call = into_bit_flag(curr, F_OPC_CALL); - let dst = curr.get_evaluation_element(0, FRAME_DST); - let fp = curr.get_evaluation_element(0, FRAME_FP); - let next_fp = next.get_evaluation_element(0, FRAME_FP); + let dst = curr.get_main_evaluation_element(0, FRAME_DST); + let fp = curr.get_main_evaluation_element(0, FRAME_FP); + let next_fp = next.get_main_evaluation_element(0, FRAME_FP); - let t1 = curr.get_evaluation_element(0, FRAME_T1); + let t1 = curr.get_main_evaluation_element(0, FRAME_T1); let pc_jnz = into_bit_flag(curr, F_PC_JNZ); - let pc = curr.get_evaluation_element(0, FRAME_PC); - let next_pc = next.get_evaluation_element(0, FRAME_PC); + let pc = curr.get_main_evaluation_element(0, FRAME_PC); + let next_pc = next.get_main_evaluation_element(0, FRAME_PC); - let t0 = curr.get_evaluation_element(0, FRAME_T0); - let op1 = curr.get_evaluation_element(0, FRAME_OP1); + let t0 = curr.get_main_evaluation_element(0, FRAME_T0); + let op1 = curr.get_main_evaluation_element(0, FRAME_OP1); let pc_abs = into_bit_flag(curr, F_PC_ABS); let pc_rel = into_bit_flag(curr, F_PC_REL); @@ -1082,23 +1105,26 @@ fn compute_register_constraints(constraints: &mut [Felt252], frame: &Frame) { +fn compute_opcode_constraints( + constraints: &mut [Felt252], + frame: &Frame, +) { let curr = frame.get_evaluation_step(0); let one = Felt252::one(); - let mul = curr.get_evaluation_element(0, FRAME_MUL); - let op0 = curr.get_evaluation_element(0, FRAME_OP0); - let op1 = curr.get_evaluation_element(0, FRAME_OP1); + let mul = curr.get_main_evaluation_element(0, FRAME_MUL); + let op0 = curr.get_main_evaluation_element(0, FRAME_OP0); + let op1 = curr.get_main_evaluation_element(0, FRAME_OP1); let res_add = into_bit_flag(curr, F_RES_ADD); let res_mul = into_bit_flag(curr, F_RES_MUL); let pc_jnz = into_bit_flag(curr, F_PC_JNZ); - let res = curr.get_evaluation_element(0, FRAME_RES); + let res = curr.get_main_evaluation_element(0, FRAME_RES); let opc_call = into_bit_flag(curr, F_OPC_CALL); - let dst = curr.get_evaluation_element(0, FRAME_DST); - let fp = curr.get_evaluation_element(0, FRAME_FP); - let pc = curr.get_evaluation_element(0, FRAME_PC); + let dst = curr.get_main_evaluation_element(0, FRAME_DST); + let fp = curr.get_main_evaluation_element(0, FRAME_FP); + let pc = curr.get_main_evaluation_element(0, FRAME_PC); let opc_aeq = into_bit_flag(curr, F_OPC_AEQ); @@ -1115,24 +1141,27 @@ fn compute_opcode_constraints(constraints: &mut [Felt252], frame: &Frame) { +fn memory_is_increasing( + constraints: &mut [Felt252], + frame: &Frame, +) { let curr = frame.get_evaluation_step(0); let next = frame.get_evaluation_step(1); let one = FieldElement::one(); - let mem_addr_sorted_0 = curr.get_evaluation_element(0, MEMORY_ADDR_SORTED_0); - let mem_addr_sorted_1 = curr.get_evaluation_element(0, MEMORY_ADDR_SORTED_1); - let mem_addr_sorted_2 = curr.get_evaluation_element(0, MEMORY_ADDR_SORTED_2); - let mem_addr_sorted_3 = curr.get_evaluation_element(0, MEMORY_ADDR_SORTED_3); - let mem_addr_sorted_4 = curr.get_evaluation_element(0, MEMORY_ADDR_SORTED_4); - let next_mem_addr_sorted_0 = next.get_evaluation_element(0, MEMORY_ADDR_SORTED_0); + let mem_addr_sorted_0 = curr.get_aux_evaluation_element(0, MEMORY_ADDR_SORTED_0); + let mem_addr_sorted_1 = curr.get_aux_evaluation_element(0, MEMORY_ADDR_SORTED_1); + let mem_addr_sorted_2 = curr.get_aux_evaluation_element(0, MEMORY_ADDR_SORTED_2); + let mem_addr_sorted_3 = curr.get_aux_evaluation_element(0, MEMORY_ADDR_SORTED_3); + let mem_addr_sorted_4 = curr.get_aux_evaluation_element(0, MEMORY_ADDR_SORTED_4); + let next_mem_addr_sorted_0 = next.get_aux_evaluation_element(0, MEMORY_ADDR_SORTED_0); - let mem_val_sorted_0 = curr.get_evaluation_element(0, MEMORY_VALUES_SORTED_0); - let mem_val_sorted_1 = curr.get_evaluation_element(0, MEMORY_VALUES_SORTED_1); - let mem_val_sorted_2 = curr.get_evaluation_element(0, MEMORY_VALUES_SORTED_2); - let mem_val_sorted_3 = curr.get_evaluation_element(0, MEMORY_VALUES_SORTED_3); - let mem_val_sorted_4 = curr.get_evaluation_element(0, MEMORY_VALUES_SORTED_4); - let next_mem_val_sorted_0 = next.get_evaluation_element(0, MEMORY_VALUES_SORTED_0); + let mem_val_sorted_0 = curr.get_aux_evaluation_element(0, MEMORY_VALUES_SORTED_0); + let mem_val_sorted_1 = curr.get_aux_evaluation_element(0, MEMORY_VALUES_SORTED_1); + let mem_val_sorted_2 = curr.get_aux_evaluation_element(0, MEMORY_VALUES_SORTED_2); + let mem_val_sorted_3 = curr.get_aux_evaluation_element(0, MEMORY_VALUES_SORTED_3); + let mem_val_sorted_4 = curr.get_aux_evaluation_element(0, MEMORY_VALUES_SORTED_4); + let next_mem_val_sorted_0 = next.get_aux_evaluation_element(0, MEMORY_VALUES_SORTED_0); constraints[MEMORY_INCREASING_0] = (mem_addr_sorted_0 - mem_addr_sorted_1) * (mem_addr_sorted_1 - mem_addr_sorted_0 - one); @@ -1167,7 +1196,7 @@ fn memory_is_increasing(constraints: &mut [Felt252], frame: &Frame, + frame: &Frame, rap_challenges: &CairoRAPChallenges, ) { let curr = frame.get_evaluation_step(0); @@ -1176,36 +1205,36 @@ fn permutation_argument( let z = &rap_challenges.z_memory; let alpha = &rap_challenges.alpha_memory; - let p0 = curr.get_evaluation_element(0, PERMUTATION_ARGUMENT_COL_0); - let next_p0 = next.get_evaluation_element(0, PERMUTATION_ARGUMENT_COL_0); - let p1 = curr.get_evaluation_element(0, PERMUTATION_ARGUMENT_COL_1); - let p2 = curr.get_evaluation_element(0, PERMUTATION_ARGUMENT_COL_2); - let p3 = curr.get_evaluation_element(0, PERMUTATION_ARGUMENT_COL_3); - let p4 = curr.get_evaluation_element(0, PERMUTATION_ARGUMENT_COL_4); - - let next_ap0 = next.get_evaluation_element(0, MEMORY_ADDR_SORTED_0); - let ap1 = curr.get_evaluation_element(0, MEMORY_ADDR_SORTED_1); - let ap2 = curr.get_evaluation_element(0, MEMORY_ADDR_SORTED_2); - let ap3 = curr.get_evaluation_element(0, MEMORY_ADDR_SORTED_3); - let ap4 = curr.get_evaluation_element(0, MEMORY_ADDR_SORTED_4); - - let next_vp0 = next.get_evaluation_element(0, MEMORY_VALUES_SORTED_0); - let vp1 = curr.get_evaluation_element(0, MEMORY_VALUES_SORTED_1); - let vp2 = curr.get_evaluation_element(0, MEMORY_VALUES_SORTED_2); - let vp3 = curr.get_evaluation_element(0, MEMORY_VALUES_SORTED_3); - let vp4 = curr.get_evaluation_element(0, MEMORY_VALUES_SORTED_4); - - let next_a0 = next.get_evaluation_element(0, FRAME_PC); - let a1 = curr.get_evaluation_element(0, FRAME_DST_ADDR); - let a2 = curr.get_evaluation_element(0, FRAME_OP0_ADDR); - let a3 = curr.get_evaluation_element(0, FRAME_OP1_ADDR); - let a4 = curr.get_evaluation_element(0, EXTRA_ADDR); - - let next_v0 = next.get_evaluation_element(0, FRAME_INST); - let v1 = curr.get_evaluation_element(0, FRAME_DST); - let v2 = curr.get_evaluation_element(0, FRAME_OP0); - let v3 = curr.get_evaluation_element(0, FRAME_OP1); - let v4 = curr.get_evaluation_element(0, EXTRA_VAL); + let p0 = curr.get_aux_evaluation_element(0, PERMUTATION_ARGUMENT_COL_0); + let next_p0 = next.get_aux_evaluation_element(0, PERMUTATION_ARGUMENT_COL_0); + let p1 = curr.get_aux_evaluation_element(0, PERMUTATION_ARGUMENT_COL_1); + let p2 = curr.get_aux_evaluation_element(0, PERMUTATION_ARGUMENT_COL_2); + let p3 = curr.get_aux_evaluation_element(0, PERMUTATION_ARGUMENT_COL_3); + let p4 = curr.get_aux_evaluation_element(0, PERMUTATION_ARGUMENT_COL_4); + + let next_ap0 = next.get_aux_evaluation_element(0, MEMORY_ADDR_SORTED_0); + let ap1 = curr.get_aux_evaluation_element(0, MEMORY_ADDR_SORTED_1); + let ap2 = curr.get_aux_evaluation_element(0, MEMORY_ADDR_SORTED_2); + let ap3 = curr.get_aux_evaluation_element(0, MEMORY_ADDR_SORTED_3); + let ap4 = curr.get_aux_evaluation_element(0, MEMORY_ADDR_SORTED_4); + + let next_vp0 = next.get_aux_evaluation_element(0, MEMORY_VALUES_SORTED_0); + let vp1 = curr.get_aux_evaluation_element(0, MEMORY_VALUES_SORTED_1); + let vp2 = curr.get_aux_evaluation_element(0, MEMORY_VALUES_SORTED_2); + let vp3 = curr.get_aux_evaluation_element(0, MEMORY_VALUES_SORTED_3); + let vp4 = curr.get_aux_evaluation_element(0, MEMORY_VALUES_SORTED_4); + + let next_a0 = next.get_main_evaluation_element(0, FRAME_PC); + let a1 = curr.get_main_evaluation_element(0, FRAME_DST_ADDR); + let a2 = curr.get_main_evaluation_element(0, FRAME_OP0_ADDR); + let a3 = curr.get_main_evaluation_element(0, FRAME_OP1_ADDR); + let a4 = curr.get_main_evaluation_element(0, EXTRA_ADDR); + + let next_v0 = next.get_main_evaluation_element(0, FRAME_INST); + let v1 = curr.get_main_evaluation_element(0, FRAME_DST); + let v2 = curr.get_main_evaluation_element(0, FRAME_OP0); + let v3 = curr.get_main_evaluation_element(0, FRAME_OP1); + let v4 = curr.get_main_evaluation_element(0, EXTRA_VAL); constraints[PERMUTATION_ARGUMENT_0] = (z - (ap1 + alpha * vp1)) * p1 - (z - (a1 + alpha * v1)) * p0; @@ -1221,7 +1250,7 @@ fn permutation_argument( fn permutation_argument_range_check( constraints: &mut [Felt252], - frame: &Frame, + frame: &Frame, rap_challenges: &CairoRAPChallenges, ) { let curr = frame.get_evaluation_step(0); @@ -1229,11 +1258,11 @@ fn permutation_argument_range_check( let one = FieldElement::one(); let z = &rap_challenges.z_range_check; - let rc_col_1 = curr.get_evaluation_element(0, RANGE_CHECK_COL_1); - let rc_col_2 = curr.get_evaluation_element(0, RANGE_CHECK_COL_2); - let rc_col_3 = curr.get_evaluation_element(0, RANGE_CHECK_COL_3); - let rc_col_4 = curr.get_evaluation_element(0, RANGE_CHECK_COL_4); - let next_rc_col_1 = next.get_evaluation_element(0, RANGE_CHECK_COL_1); + let rc_col_1 = curr.get_aux_evaluation_element(0, RANGE_CHECK_COL_1); + let rc_col_2 = curr.get_aux_evaluation_element(0, RANGE_CHECK_COL_2); + let rc_col_3 = curr.get_aux_evaluation_element(0, RANGE_CHECK_COL_3); + let rc_col_4 = curr.get_aux_evaluation_element(0, RANGE_CHECK_COL_4); + let next_rc_col_1 = next.get_aux_evaluation_element(0, RANGE_CHECK_COL_1); constraints[RANGE_CHECK_INCREASING_0] = (rc_col_1 - rc_col_2) * (rc_col_2 - rc_col_1 - one); constraints[RANGE_CHECK_INCREASING_1] = (rc_col_2 - rc_col_3) * (rc_col_3 - rc_col_2 - one); @@ -1241,21 +1270,21 @@ fn permutation_argument_range_check( constraints[RANGE_CHECK_INCREASING_3] = (rc_col_4 - next_rc_col_1) * (next_rc_col_1 - rc_col_4 - one); - let p0 = curr.get_evaluation_element(0, PERMUTATION_ARGUMENT_RANGE_CHECK_COL_1); - let next_p0 = next.get_evaluation_element(0, PERMUTATION_ARGUMENT_RANGE_CHECK_COL_1); - let p1 = curr.get_evaluation_element(0, PERMUTATION_ARGUMENT_RANGE_CHECK_COL_2); - let p2 = curr.get_evaluation_element(0, PERMUTATION_ARGUMENT_RANGE_CHECK_COL_3); - let p3 = curr.get_evaluation_element(0, PERMUTATION_ARGUMENT_RANGE_CHECK_COL_4); + let p0 = curr.get_aux_evaluation_element(0, PERMUTATION_ARGUMENT_RANGE_CHECK_COL_1); + let next_p0 = next.get_aux_evaluation_element(0, PERMUTATION_ARGUMENT_RANGE_CHECK_COL_1); + let p1 = curr.get_aux_evaluation_element(0, PERMUTATION_ARGUMENT_RANGE_CHECK_COL_2); + let p2 = curr.get_aux_evaluation_element(0, PERMUTATION_ARGUMENT_RANGE_CHECK_COL_3); + let p3 = curr.get_aux_evaluation_element(0, PERMUTATION_ARGUMENT_RANGE_CHECK_COL_4); - let next_ap0 = next.get_evaluation_element(0, RANGE_CHECK_COL_1); - let ap1 = curr.get_evaluation_element(0, RANGE_CHECK_COL_2); - let ap2 = curr.get_evaluation_element(0, RANGE_CHECK_COL_3); - let ap3 = curr.get_evaluation_element(0, RANGE_CHECK_COL_4); + let next_ap0 = next.get_aux_evaluation_element(0, RANGE_CHECK_COL_1); + let ap1 = curr.get_aux_evaluation_element(0, RANGE_CHECK_COL_2); + let ap2 = curr.get_aux_evaluation_element(0, RANGE_CHECK_COL_3); + let ap3 = curr.get_aux_evaluation_element(0, RANGE_CHECK_COL_4); - let a0_next = next.get_evaluation_element(0, OFF_DST); - let a1 = curr.get_evaluation_element(0, OFF_OP0); - let a2 = curr.get_evaluation_element(0, OFF_OP1); - let a3 = curr.get_evaluation_element(0, RC_HOLES); + let a0_next = next.get_main_evaluation_element(0, OFF_DST); + let a1 = curr.get_main_evaluation_element(0, OFF_OP0); + let a2 = curr.get_main_evaluation_element(0, OFF_OP1); + let a3 = curr.get_main_evaluation_element(0, RC_HOLES); constraints[RANGE_CHECK_0] = (z - ap1) * p1 - (z - a1) * p0; constraints[RANGE_CHECK_1] = (z - ap2) * p2 - (z - a2) * p1; @@ -1263,7 +1292,7 @@ fn permutation_argument_range_check( constraints[RANGE_CHECK_3] = (z - next_ap0) * next_p0 - (z - a0_next) * p3; } -fn frame_inst_size(step: &StepView) -> Felt252 { +fn frame_inst_size(step: &StepView) -> Felt252 { let op1_val = into_bit_flag(step, F_OP_1_VAL); op1_val + Felt252::one() } diff --git a/provers/cairo/src/tests/integration_tests.rs b/provers/cairo/src/tests/integration_tests.rs index 4c7a41046e..79f051830e 100644 --- a/provers/cairo/src/tests/integration_tests.rs +++ b/provers/cairo/src/tests/integration_tests.rs @@ -161,6 +161,7 @@ fn check_simple_cairo_trace_evaluates_to_zero() { assert!(validate_trace( &cairo_air, &trace_polys, + &aux_polys, &domain, &rap_challenges )); diff --git a/provers/cairo/tests/wasm.rs b/provers/cairo/tests/wasm.rs index 0eba688985..0fd44a5e9f 100644 --- a/provers/cairo/tests/wasm.rs +++ b/provers/cairo/tests/wasm.rs @@ -19,103 +19,66 @@ fn test_prove_cairo1_program_wasm() { // Test case is fibo5, with default test options #[cfg(feature = "wasm")] -static PROOF: [u8; 25527] = [ - 189, 90, 0, 0, 64, 34, 150, 252, 123, 41, 36, 58, 219, 76, 25, 24, 145, 52, 128, 169, 121, 212, +static PROOF: [u8; 25531] = [ + 193, 90, 0, 0, 64, 34, 150, 252, 123, 41, 36, 58, 219, 76, 25, 24, 145, 52, 128, 169, 121, 212, 127, 31, 128, 230, 237, 60, 212, 165, 171, 81, 190, 191, 157, 241, 200, 1, 77, 31, 232, 212, 215, 170, 34, 124, 137, 195, 171, 208, 228, 24, 74, 34, 237, 107, 135, 106, 205, 168, 33, 72, - 193, 202, 8, 15, 250, 12, 38, 185, 118, 32, 4, 171, 46, 46, 72, 149, 150, 106, 253, 54, 138, - 21, 229, 36, 56, 120, 99, 238, 141, 157, 240, 156, 48, 237, 33, 19, 160, 95, 129, 156, 230, - 103, 32, 5, 92, 56, 221, 221, 174, 163, 72, 227, 62, 104, 105, 67, 88, 2, 1, 167, 128, 234, - 197, 5, 19, 84, 237, 187, 255, 26, 202, 199, 198, 212, 123, 32, 6, 179, 252, 250, 97, 234, 175, - 28, 235, 179, 10, 3, 105, 223, 144, 217, 123, 180, 69, 201, 228, 37, 168, 76, 160, 177, 251, - 61, 4, 188, 206, 150, 32, 4, 35, 76, 126, 184, 197, 8, 44, 216, 227, 61, 246, 83, 23, 174, 189, - 38, 10, 238, 119, 48, 144, 139, 151, 205, 240, 162, 170, 38, 69, 32, 218, 32, 0, 78, 185, 188, - 8, 218, 190, 3, 120, 130, 120, 175, 57, 130, 249, 75, 60, 8, 193, 5, 209, 215, 185, 84, 20, - 152, 161, 136, 29, 105, 202, 146, 32, 1, 32, 251, 95, 208, 37, 117, 134, 77, 38, 169, 174, 238, - 162, 116, 104, 140, 208, 75, 38, 112, 222, 177, 176, 95, 20, 91, 133, 96, 134, 241, 165, 32, 5, - 58, 73, 111, 62, 133, 211, 177, 161, 47, 249, 216, 236, 145, 223, 222, 0, 208, 194, 218, 24, - 115, 204, 219, 46, 122, 185, 109, 97, 142, 6, 157, 32, 6, 157, 36, 183, 159, 66, 233, 225, 80, - 151, 252, 236, 118, 72, 239, 239, 0, 104, 97, 109, 12, 57, 230, 109, 151, 61, 92, 182, 176, - 199, 3, 79, 32, 1, 112, 61, 72, 192, 28, 241, 170, 208, 254, 139, 231, 7, 209, 54, 27, 192, - 145, 224, 147, 87, 46, 129, 150, 250, 105, 17, 146, 120, 238, 160, 242, 32, 6, 171, 57, 209, - 156, 186, 18, 81, 248, 81, 70, 231, 108, 187, 34, 194, 209, 111, 66, 41, 165, 196, 44, 203, - 185, 199, 88, 119, 146, 115, 47, 134, 32, 3, 253, 112, 171, 215, 178, 72, 192, 87, 190, 133, - 240, 187, 7, 161, 188, 230, 12, 255, 184, 120, 26, 25, 36, 123, 72, 169, 168, 139, 153, 157, - 59, 32, 4, 66, 223, 179, 251, 173, 74, 78, 2, 84, 31, 166, 43, 236, 208, 150, 145, 194, 243, - 10, 118, 191, 206, 20, 156, 165, 1, 69, 115, 4, 199, 206, 32, 7, 139, 31, 61, 86, 188, 154, - 191, 165, 215, 226, 86, 162, 70, 214, 134, 187, 31, 125, 208, 216, 59, 183, 54, 16, 82, 52, - 237, 240, 126, 50, 223, 32, 7, 191, 175, 19, 56, 74, 241, 8, 88, 216, 27, 92, 136, 239, 219, - 106, 181, 155, 238, 129, 10, 129, 221, 197, 69, 118, 172, 159, 87, 101, 181, 40, 32, 6, 1, 130, - 118, 140, 160, 245, 70, 213, 30, 155, 31, 17, 36, 171, 217, 155, 43, 167, 29, 86, 82, 125, 66, - 209, 133, 185, 134, 204, 61, 249, 223, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 5, 5, 77, 210, 22, 240, 224, 140, 52, 197, - 169, 11, 160, 21, 13, 55, 239, 229, 66, 219, 125, 182, 6, 36, 172, 145, 58, 153, 209, 202, 52, - 114, 32, 0, 148, 26, 6, 89, 195, 227, 119, 6, 167, 12, 169, 231, 161, 188, 192, 231, 34, 181, - 72, 158, 226, 167, 181, 10, 160, 173, 188, 242, 236, 219, 152, 32, 0, 228, 51, 150, 138, 23, - 240, 58, 239, 11, 11, 49, 205, 232, 207, 77, 226, 183, 230, 217, 57, 64, 109, 196, 12, 39, 85, - 255, 237, 18, 85, 193, 32, 6, 85, 4, 87, 121, 137, 197, 38, 112, 136, 27, 230, 225, 169, 48, - 102, 5, 84, 136, 250, 71, 223, 219, 186, 104, 60, 135, 3, 128, 62, 106, 45, 32, 7, 56, 179, - 166, 236, 231, 225, 176, 54, 191, 5, 110, 209, 16, 229, 236, 73, 110, 41, 50, 90, 219, 209, - 125, 93, 79, 197, 186, 92, 170, 49, 219, 32, 3, 10, 93, 160, 62, 131, 106, 88, 66, 230, 41, - 206, 250, 29, 161, 133, 102, 106, 249, 221, 8, 97, 108, 255, 63, 67, 47, 168, 171, 220, 106, - 41, 32, 0, 139, 66, 37, 71, 31, 54, 72, 41, 206, 95, 73, 251, 212, 195, 255, 11, 100, 97, 24, - 153, 43, 0, 48, 38, 167, 83, 93, 94, 34, 210, 250, 32, 5, 109, 143, 37, 8, 2, 69, 20, 0, 152, - 119, 96, 22, 198, 72, 166, 96, 100, 110, 176, 22, 243, 142, 119, 221, 147, 218, 103, 40, 35, - 46, 125, 32, 4, 188, 184, 9, 251, 140, 5, 113, 122, 209, 130, 111, 194, 127, 208, 115, 60, 123, - 105, 88, 5, 7, 234, 212, 115, 83, 139, 114, 75, 218, 7, 70, 32, 1, 227, 195, 188, 203, 231, - 139, 5, 12, 254, 39, 124, 95, 116, 106, 12, 10, 179, 28, 165, 161, 182, 30, 11, 214, 138, 148, - 228, 179, 58, 162, 36, 32, 5, 62, 149, 75, 155, 0, 72, 204, 169, 3, 6, 65, 123, 194, 43, 245, - 19, 54, 172, 21, 95, 53, 55, 148, 127, 95, 27, 64, 184, 193, 216, 120, 32, 4, 27, 7, 54, 57, - 150, 199, 162, 106, 136, 171, 108, 35, 200, 0, 240, 225, 124, 114, 127, 192, 131, 131, 14, 216, - 142, 47, 58, 30, 65, 127, 55, 32, 1, 148, 91, 227, 154, 152, 80, 25, 52, 55, 158, 182, 187, 14, - 180, 170, 20, 138, 197, 24, 175, 230, 9, 219, 122, 144, 144, 61, 108, 215, 189, 250, 32, 7, - 114, 120, 91, 223, 13, 219, 241, 233, 217, 192, 111, 190, 224, 172, 246, 176, 182, 66, 142, - 136, 226, 43, 54, 172, 53, 15, 59, 142, 97, 211, 188, 32, 2, 132, 57, 150, 70, 49, 1, 8, 234, - 246, 45, 249, 78, 230, 149, 128, 150, 96, 213, 213, 11, 99, 63, 254, 93, 43, 187, 22, 231, 60, - 154, 36, 32, 3, 63, 223, 107, 43, 58, 72, 137, 1, 117, 86, 227, 189, 56, 81, 54, 68, 42, 58, - 135, 219, 191, 103, 9, 82, 207, 206, 34, 151, 38, 200, 153, 32, 1, 238, 242, 213, 113, 252, 6, - 128, 90, 154, 103, 235, 16, 227, 37, 91, 202, 212, 170, 172, 81, 1, 10, 69, 89, 136, 49, 193, - 130, 176, 239, 60, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, 255, 254, 239, 253, 240, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 255, - 225, 32, 2, 91, 68, 30, 167, 149, 38, 118, 145, 95, 148, 62, 85, 145, 230, 53, 55, 130, 176, - 244, 48, 67, 173, 42, 214, 141, 102, 245, 187, 90, 230, 19, 32, 5, 34, 69, 70, 141, 13, 255, - 133, 225, 104, 55, 188, 22, 185, 42, 245, 181, 99, 180, 84, 29, 89, 106, 130, 235, 89, 220, 87, - 144, 176, 77, 60, 32, 5, 34, 69, 70, 141, 13, 255, 133, 225, 104, 55, 188, 22, 185, 42, 245, - 181, 99, 180, 84, 29, 89, 106, 130, 235, 89, 220, 87, 144, 176, 77, 60, 32, 5, 34, 69, 70, 141, - 13, 255, 133, 225, 104, 55, 188, 22, 185, 42, 245, 181, 99, 180, 84, 29, 89, 106, 130, 235, 89, - 220, 87, 144, 176, 77, 60, 32, 4, 172, 50, 234, 18, 80, 78, 10, 45, 122, 188, 84, 165, 217, - 106, 152, 186, 208, 180, 33, 157, 178, 199, 196, 247, 86, 102, 162, 254, 117, 30, 72, 32, 0, - 181, 233, 160, 243, 50, 209, 135, 128, 205, 250, 37, 164, 203, 220, 42, 145, 194, 139, 44, 213, - 121, 80, 254, 184, 153, 32, 79, 69, 57, 83, 216, 32, 6, 19, 23, 214, 231, 29, 95, 179, 150, - 132, 169, 32, 196, 217, 91, 169, 163, 124, 152, 184, 237, 143, 180, 231, 117, 152, 167, 123, - 83, 209, 158, 147, 32, 6, 53, 28, 103, 222, 115, 121, 45, 17, 1, 207, 189, 245, 123, 206, 196, - 83, 65, 169, 108, 164, 89, 160, 91, 82, 95, 127, 247, 39, 181, 34, 110, 32, 3, 31, 236, 182, - 13, 15, 107, 36, 19, 38, 250, 71, 179, 208, 136, 106, 154, 173, 227, 31, 17, 152, 79, 12, 144, - 63, 104, 61, 227, 164, 31, 228, 32, 1, 159, 112, 153, 196, 243, 158, 187, 254, 28, 60, 241, 46, - 105, 131, 157, 200, 26, 27, 122, 61, 228, 124, 189, 29, 242, 143, 79, 146, 139, 250, 153, 32, - 6, 193, 59, 12, 1, 213, 95, 240, 123, 204, 211, 89, 91, 2, 87, 245, 137, 143, 166, 33, 52, 104, - 219, 164, 137, 65, 209, 54, 190, 218, 216, 212, 32, 3, 229, 249, 197, 153, 170, 217, 112, 20, - 238, 233, 2, 174, 64, 10, 149, 156, 93, 238, 183, 26, 131, 97, 252, 235, 129, 71, 6, 23, 175, - 79, 173, 32, 6, 253, 237, 190, 189, 104, 8, 118, 156, 19, 217, 149, 238, 165, 120, 5, 142, 216, - 5, 93, 213, 251, 77, 5, 153, 131, 157, 90, 222, 105, 122, 120, 32, 2, 129, 96, 22, 155, 203, - 11, 182, 232, 185, 144, 137, 252, 51, 123, 36, 247, 87, 64, 32, 96, 251, 255, 208, 25, 98, 57, - 66, 175, 117, 176, 193, 32, 3, 193, 122, 195, 109, 117, 12, 99, 231, 45, 239, 98, 138, 155, 65, - 11, 130, 87, 253, 152, 152, 204, 121, 224, 34, 239, 131, 187, 169, 165, 80, 147, 32, 4, 246, - 117, 1, 253, 181, 210, 144, 241, 105, 62, 229, 159, 173, 99, 208, 146, 160, 204, 225, 177, 13, - 249, 172, 46, 189, 182, 61, 154, 161, 37, 214, 32, 7, 204, 121, 1, 160, 97, 61, 117, 31, 237, - 167, 240, 191, 56, 46, 150, 73, 122, 206, 192, 102, 9, 94, 103, 110, 233, 248, 26, 236, 255, - 132, 154, 32, 4, 137, 131, 56, 170, 8, 125, 216, 70, 207, 87, 130, 132, 70, 1, 125, 242, 43, - 121, 151, 176, 95, 112, 189, 214, 242, 170, 214, 129, 55, 154, 128, 32, 4, 201, 114, 110, 175, - 33, 250, 89, 22, 203, 194, 137, 227, 107, 31, 82, 20, 62, 160, 204, 121, 71, 65, 18, 220, 240, - 245, 105, 137, 65, 104, 186, 32, 5, 73, 163, 150, 210, 209, 44, 45, 88, 147, 177, 140, 73, 245, - 231, 52, 114, 8, 113, 216, 127, 88, 253, 134, 215, 221, 35, 183, 71, 99, 55, 163, 32, 4, 183, - 149, 133, 222, 16, 181, 158, 187, 26, 112, 160, 13, 165, 89, 191, 175, 117, 135, 204, 186, 117, - 164, 118, 218, 181, 24, 248, 8, 182, 165, 181, 32, 5, 180, 94, 217, 181, 61, 240, 200, 208, 79, - 68, 123, 238, 236, 167, 131, 241, 128, 31, 247, 36, 252, 212, 75, 196, 240, 89, 232, 91, 20, - 91, 209, 32, 4, 44, 50, 77, 67, 251, 45, 217, 70, 42, 101, 206, 195, 184, 161, 10, 85, 151, 59, - 51, 28, 208, 81, 78, 247, 241, 88, 37, 195, 149, 151, 228, 32, 5, 164, 73, 93, 67, 178, 117, + 193, 202, 8, 15, 250, 12, 38, 185, 72, 32, 4, 171, 46, 46, 72, 149, 150, 106, 253, 54, 138, 21, + 229, 36, 56, 120, 99, 238, 141, 157, 240, 156, 48, 237, 33, 19, 160, 95, 129, 156, 230, 103, + 32, 5, 92, 56, 221, 221, 174, 163, 72, 227, 62, 104, 105, 67, 88, 2, 1, 167, 128, 234, 197, 5, + 19, 84, 237, 187, 255, 26, 202, 199, 198, 212, 123, 32, 6, 179, 252, 250, 97, 234, 175, 28, + 235, 179, 10, 3, 105, 223, 144, 217, 123, 180, 69, 201, 228, 37, 168, 76, 160, 177, 251, 61, 4, + 188, 206, 150, 32, 4, 35, 76, 126, 184, 197, 8, 44, 216, 227, 61, 246, 83, 23, 174, 189, 38, + 10, 238, 119, 48, 144, 139, 151, 205, 240, 162, 170, 38, 69, 32, 218, 32, 0, 78, 185, 188, 8, + 218, 190, 3, 120, 130, 120, 175, 57, 130, 249, 75, 60, 8, 193, 5, 209, 215, 185, 84, 20, 152, + 161, 136, 29, 105, 202, 146, 32, 1, 32, 251, 95, 208, 37, 117, 134, 77, 38, 169, 174, 238, 162, + 116, 104, 140, 208, 75, 38, 112, 222, 177, 176, 95, 20, 91, 133, 96, 134, 241, 165, 32, 5, 58, + 73, 111, 62, 133, 211, 177, 161, 47, 249, 216, 236, 145, 223, 222, 0, 208, 194, 218, 24, 115, + 204, 219, 46, 122, 185, 109, 97, 142, 6, 157, 32, 6, 157, 36, 183, 159, 66, 233, 225, 80, 151, + 252, 236, 118, 72, 239, 239, 0, 104, 97, 109, 12, 57, 230, 109, 151, 61, 92, 182, 176, 199, 3, + 79, 32, 1, 112, 61, 72, 192, 28, 241, 170, 208, 254, 139, 231, 7, 209, 54, 27, 192, 145, 224, + 147, 87, 46, 129, 150, 250, 105, 17, 146, 120, 238, 160, 242, 32, 6, 171, 57, 209, 156, 186, + 18, 81, 248, 81, 70, 231, 108, 187, 34, 194, 209, 111, 66, 41, 165, 196, 44, 203, 185, 199, 88, + 119, 146, 115, 47, 134, 32, 3, 253, 112, 171, 215, 178, 72, 192, 87, 190, 133, 240, 187, 7, + 161, 188, 230, 12, 255, 184, 120, 26, 25, 36, 123, 72, 169, 168, 139, 153, 157, 59, 32, 4, 66, + 223, 179, 251, 173, 74, 78, 2, 84, 31, 166, 43, 236, 208, 150, 145, 194, 243, 10, 118, 191, + 206, 20, 156, 165, 1, 69, 115, 4, 199, 206, 32, 7, 139, 31, 61, 86, 188, 154, 191, 165, 215, + 226, 86, 162, 70, 214, 134, 187, 31, 125, 208, 216, 59, 183, 54, 16, 82, 52, 237, 240, 126, 50, + 223, 32, 7, 191, 175, 19, 56, 74, 241, 8, 88, 216, 27, 92, 136, 239, 219, 106, 181, 155, 238, + 129, 10, 129, 221, 197, 69, 118, 172, 159, 87, 101, 181, 40, 32, 6, 1, 130, 118, 140, 160, 245, + 70, 213, 30, 155, 31, 17, 36, 171, 217, 155, 43, 167, 29, 86, 82, 125, 66, 209, 133, 185, 134, + 204, 61, 249, 223, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 32, 5, 5, 77, 210, 22, 240, 224, 140, 52, 197, 169, 11, 160, 21, 13, + 55, 239, 229, 66, 219, 125, 182, 6, 36, 172, 145, 58, 153, 209, 202, 52, 114, 32, 0, 148, 26, + 6, 89, 195, 227, 119, 6, 167, 12, 169, 231, 161, 188, 192, 231, 34, 181, 72, 158, 226, 167, + 181, 10, 160, 173, 188, 242, 236, 219, 152, 32, 0, 228, 51, 150, 138, 23, 240, 58, 239, 11, 11, + 49, 205, 232, 207, 77, 226, 183, 230, 217, 57, 64, 109, 196, 12, 39, 85, 255, 237, 18, 85, 193, + 32, 6, 85, 4, 87, 121, 137, 197, 38, 112, 136, 27, 230, 225, 169, 48, 102, 5, 84, 136, 250, 71, + 223, 219, 186, 104, 60, 135, 3, 128, 62, 106, 45, 32, 7, 56, 179, 166, 236, 231, 225, 176, 54, + 191, 5, 110, 209, 16, 229, 236, 73, 110, 41, 50, 90, 219, 209, 125, 93, 79, 197, 186, 92, 170, + 49, 219, 32, 3, 10, 93, 160, 62, 131, 106, 88, 66, 230, 41, 206, 250, 29, 161, 133, 102, 106, + 249, 221, 8, 97, 108, 255, 63, 67, 47, 168, 171, 220, 106, 41, 32, 0, 139, 66, 37, 71, 31, 54, + 72, 41, 206, 95, 73, 251, 212, 195, 255, 11, 100, 97, 24, 153, 43, 0, 48, 38, 167, 83, 93, 94, + 34, 210, 250, 32, 5, 109, 143, 37, 8, 2, 69, 20, 0, 152, 119, 96, 22, 198, 72, 166, 96, 100, + 110, 176, 22, 243, 142, 119, 221, 147, 218, 103, 40, 35, 46, 125, 32, 4, 188, 184, 9, 251, 140, + 5, 113, 122, 209, 130, 111, 194, 127, 208, 115, 60, 123, 105, 88, 5, 7, 234, 212, 115, 83, 139, + 114, 75, 218, 7, 70, 32, 1, 227, 195, 188, 203, 231, 139, 5, 12, 254, 39, 124, 95, 116, 106, + 12, 10, 179, 28, 165, 161, 182, 30, 11, 214, 138, 148, 228, 179, 58, 162, 36, 32, 5, 62, 149, + 75, 155, 0, 72, 204, 169, 3, 6, 65, 123, 194, 43, 245, 19, 54, 172, 21, 95, 53, 55, 148, 127, + 95, 27, 64, 184, 193, 216, 120, 32, 4, 27, 7, 54, 57, 150, 199, 162, 106, 136, 171, 108, 35, + 200, 0, 240, 225, 124, 114, 127, 192, 131, 131, 14, 216, 142, 47, 58, 30, 65, 127, 55, 32, 1, + 148, 91, 227, 154, 152, 80, 25, 52, 55, 158, 182, 187, 14, 180, 170, 20, 138, 197, 24, 175, + 230, 9, 219, 122, 144, 144, 61, 108, 215, 189, 250, 32, 7, 114, 120, 91, 223, 13, 219, 241, + 233, 217, 192, 111, 190, 224, 172, 246, 176, 182, 66, 142, 136, 226, 43, 54, 172, 53, 15, 59, + 142, 97, 211, 188, 32, 2, 132, 57, 150, 70, 49, 1, 8, 234, 246, 45, 249, 78, 230, 149, 128, + 150, 96, 213, 213, 11, 99, 63, 254, 93, 43, 187, 22, 231, 60, 154, 36, 32, 3, 63, 223, 107, 43, + 58, 72, 137, 1, 117, 86, 227, 189, 56, 81, 54, 68, 42, 58, 135, 219, 191, 103, 9, 82, 207, 206, + 34, 151, 38, 200, 153, 32, 1, 238, 242, 213, 113, 252, 6, 128, 90, 154, 103, 235, 16, 227, 37, + 91, 202, 212, 170, 172, 81, 1, 10, 69, 89, 136, 49, 193, 130, 176, 239, 60, 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, + 255, 255, 255, 254, 239, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 255, 225, 32, 5, 164, 73, 93, 67, 178, 117, 132, 236, 197, 104, 163, 216, 184, 202, 92, 66, 199, 96, 214, 233, 218, 106, 44, 172, 235, 112, 136, 66, 249, 90, 126, 32, 3, 216, 209, 48, 53, 210, 164, 231, 47, 146, 117, 140, 11, 65, 169, 131, 250, 39, 109, 15, 216, 66, 25, 51, 242, 199, 136, 251, 208, 55, 7, 38, 32, 0, 28, 192, @@ -171,23 +134,60 @@ static PROOF: [u8; 25527] = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, 255, 254, 239, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 239, 255, 225, 32, 2, 97, 27, 155, 37, 146, 202, 43, 214, - 62, 62, 237, 12, 122, 139, 243, 242, 150, 242, 183, 49, 130, 122, 247, 48, 56, 106, 114, 143, - 235, 8, 79, 32, 2, 185, 124, 96, 183, 155, 14, 136, 208, 75, 114, 37, 245, 191, 178, 178, 63, - 111, 67, 60, 28, 27, 238, 221, 224, 240, 253, 47, 91, 239, 13, 179, 32, 2, 185, 124, 96, 183, - 155, 14, 136, 208, 75, 114, 37, 245, 191, 178, 178, 63, 111, 67, 60, 28, 27, 238, 221, 224, - 240, 253, 47, 91, 239, 13, 179, 32, 2, 185, 124, 96, 183, 155, 14, 136, 208, 75, 114, 37, 245, - 191, 178, 178, 63, 111, 67, 60, 28, 27, 238, 221, 224, 240, 253, 47, 91, 239, 13, 179, 32, 3, - 81, 15, 195, 103, 39, 66, 162, 136, 92, 92, 14, 153, 166, 21, 71, 131, 158, 181, 10, 33, 207, - 138, 191, 11, 61, 159, 8, 59, 196, 116, 206, 32, 6, 92, 174, 186, 3, 163, 232, 46, 142, 159, - 231, 236, 199, 52, 201, 253, 94, 196, 194, 27, 170, 65, 56, 3, 235, 54, 159, 71, 74, 224, 145, - 194, 32, 7, 37, 192, 191, 55, 232, 222, 163, 133, 119, 204, 154, 2, 252, 68, 204, 127, 35, 115, - 18, 255, 101, 92, 159, 113, 38, 148, 11, 88, 14, 188, 24, 32, 1, 185, 157, 208, 201, 232, 187, - 167, 69, 41, 191, 112, 20, 178, 90, 158, 106, 103, 187, 169, 152, 209, 138, 82, 222, 48, 151, - 43, 211, 122, 139, 62, 32, 0, 82, 151, 15, 254, 101, 72, 162, 180, 25, 122, 196, 91, 240, 198, - 230, 133, 210, 171, 61, 168, 31, 207, 166, 62, 126, 137, 58, 58, 215, 89, 196, 32, 7, 243, 45, - 235, 180, 10, 147, 108, 102, 148, 41, 190, 1, 4, 246, 173, 38, 149, 22, 241, 244, 63, 151, 200, - 36, 150, 99, 141, 148, 213, 170, 198, 32, 3, 127, 184, 40, 17, 194, 24, 186, 31, 167, 103, 162, + 255, 255, 255, 255, 255, 255, 255, 239, 255, 225, 36, 2, 46, 32, 2, 91, 68, 30, 167, 149, 38, + 118, 145, 95, 148, 62, 85, 145, 230, 53, 55, 130, 176, 244, 48, 67, 173, 42, 214, 141, 102, + 245, 187, 90, 230, 19, 32, 5, 34, 69, 70, 141, 13, 255, 133, 225, 104, 55, 188, 22, 185, 42, + 245, 181, 99, 180, 84, 29, 89, 106, 130, 235, 89, 220, 87, 144, 176, 77, 60, 32, 5, 34, 69, 70, + 141, 13, 255, 133, 225, 104, 55, 188, 22, 185, 42, 245, 181, 99, 180, 84, 29, 89, 106, 130, + 235, 89, 220, 87, 144, 176, 77, 60, 32, 5, 34, 69, 70, 141, 13, 255, 133, 225, 104, 55, 188, + 22, 185, 42, 245, 181, 99, 180, 84, 29, 89, 106, 130, 235, 89, 220, 87, 144, 176, 77, 60, 32, + 4, 172, 50, 234, 18, 80, 78, 10, 45, 122, 188, 84, 165, 217, 106, 152, 186, 208, 180, 33, 157, + 178, 199, 196, 247, 86, 102, 162, 254, 117, 30, 72, 32, 0, 181, 233, 160, 243, 50, 209, 135, + 128, 205, 250, 37, 164, 203, 220, 42, 145, 194, 139, 44, 213, 121, 80, 254, 184, 153, 32, 79, + 69, 57, 83, 216, 32, 6, 19, 23, 214, 231, 29, 95, 179, 150, 132, 169, 32, 196, 217, 91, 169, + 163, 124, 152, 184, 237, 143, 180, 231, 117, 152, 167, 123, 83, 209, 158, 147, 32, 6, 53, 28, + 103, 222, 115, 121, 45, 17, 1, 207, 189, 245, 123, 206, 196, 83, 65, 169, 108, 164, 89, 160, + 91, 82, 95, 127, 247, 39, 181, 34, 110, 32, 3, 31, 236, 182, 13, 15, 107, 36, 19, 38, 250, 71, + 179, 208, 136, 106, 154, 173, 227, 31, 17, 152, 79, 12, 144, 63, 104, 61, 227, 164, 31, 228, + 32, 1, 159, 112, 153, 196, 243, 158, 187, 254, 28, 60, 241, 46, 105, 131, 157, 200, 26, 27, + 122, 61, 228, 124, 189, 29, 242, 143, 79, 146, 139, 250, 153, 32, 6, 193, 59, 12, 1, 213, 95, + 240, 123, 204, 211, 89, 91, 2, 87, 245, 137, 143, 166, 33, 52, 104, 219, 164, 137, 65, 209, 54, + 190, 218, 216, 212, 32, 3, 229, 249, 197, 153, 170, 217, 112, 20, 238, 233, 2, 174, 64, 10, + 149, 156, 93, 238, 183, 26, 131, 97, 252, 235, 129, 71, 6, 23, 175, 79, 173, 32, 6, 253, 237, + 190, 189, 104, 8, 118, 156, 19, 217, 149, 238, 165, 120, 5, 142, 216, 5, 93, 213, 251, 77, 5, + 153, 131, 157, 90, 222, 105, 122, 120, 32, 2, 129, 96, 22, 155, 203, 11, 182, 232, 185, 144, + 137, 252, 51, 123, 36, 247, 87, 64, 32, 96, 251, 255, 208, 25, 98, 57, 66, 175, 117, 176, 193, + 32, 3, 193, 122, 195, 109, 117, 12, 99, 231, 45, 239, 98, 138, 155, 65, 11, 130, 87, 253, 152, + 152, 204, 121, 224, 34, 239, 131, 187, 169, 165, 80, 147, 32, 4, 246, 117, 1, 253, 181, 210, + 144, 241, 105, 62, 229, 159, 173, 99, 208, 146, 160, 204, 225, 177, 13, 249, 172, 46, 189, 182, + 61, 154, 161, 37, 214, 32, 7, 204, 121, 1, 160, 97, 61, 117, 31, 237, 167, 240, 191, 56, 46, + 150, 73, 122, 206, 192, 102, 9, 94, 103, 110, 233, 248, 26, 236, 255, 132, 154, 32, 4, 137, + 131, 56, 170, 8, 125, 216, 70, 207, 87, 130, 132, 70, 1, 125, 242, 43, 121, 151, 176, 95, 112, + 189, 214, 242, 170, 214, 129, 55, 154, 128, 32, 4, 201, 114, 110, 175, 33, 250, 89, 22, 203, + 194, 137, 227, 107, 31, 82, 20, 62, 160, 204, 121, 71, 65, 18, 220, 240, 245, 105, 137, 65, + 104, 186, 32, 5, 73, 163, 150, 210, 209, 44, 45, 88, 147, 177, 140, 73, 245, 231, 52, 114, 8, + 113, 216, 127, 88, 253, 134, 215, 221, 35, 183, 71, 99, 55, 163, 32, 4, 183, 149, 133, 222, 16, + 181, 158, 187, 26, 112, 160, 13, 165, 89, 191, 175, 117, 135, 204, 186, 117, 164, 118, 218, + 181, 24, 248, 8, 182, 165, 181, 32, 5, 180, 94, 217, 181, 61, 240, 200, 208, 79, 68, 123, 238, + 236, 167, 131, 241, 128, 31, 247, 36, 252, 212, 75, 196, 240, 89, 232, 91, 20, 91, 209, 32, 4, + 44, 50, 77, 67, 251, 45, 217, 70, 42, 101, 206, 195, 184, 161, 10, 85, 151, 59, 51, 28, 208, + 81, 78, 247, 241, 88, 37, 195, 149, 151, 228, 32, 2, 97, 27, 155, 37, 146, 202, 43, 214, 62, + 62, 237, 12, 122, 139, 243, 242, 150, 242, 183, 49, 130, 122, 247, 48, 56, 106, 114, 143, 235, + 8, 79, 32, 2, 185, 124, 96, 183, 155, 14, 136, 208, 75, 114, 37, 245, 191, 178, 178, 63, 111, + 67, 60, 28, 27, 238, 221, 224, 240, 253, 47, 91, 239, 13, 179, 32, 2, 185, 124, 96, 183, 155, + 14, 136, 208, 75, 114, 37, 245, 191, 178, 178, 63, 111, 67, 60, 28, 27, 238, 221, 224, 240, + 253, 47, 91, 239, 13, 179, 32, 2, 185, 124, 96, 183, 155, 14, 136, 208, 75, 114, 37, 245, 191, + 178, 178, 63, 111, 67, 60, 28, 27, 238, 221, 224, 240, 253, 47, 91, 239, 13, 179, 32, 3, 81, + 15, 195, 103, 39, 66, 162, 136, 92, 92, 14, 153, 166, 21, 71, 131, 158, 181, 10, 33, 207, 138, + 191, 11, 61, 159, 8, 59, 196, 116, 206, 32, 6, 92, 174, 186, 3, 163, 232, 46, 142, 159, 231, + 236, 199, 52, 201, 253, 94, 196, 194, 27, 170, 65, 56, 3, 235, 54, 159, 71, 74, 224, 145, 194, + 32, 7, 37, 192, 191, 55, 232, 222, 163, 133, 119, 204, 154, 2, 252, 68, 204, 127, 35, 115, 18, + 255, 101, 92, 159, 113, 38, 148, 11, 88, 14, 188, 24, 32, 1, 185, 157, 208, 201, 232, 187, 167, + 69, 41, 191, 112, 20, 178, 90, 158, 106, 103, 187, 169, 152, 209, 138, 82, 222, 48, 151, 43, + 211, 122, 139, 62, 32, 0, 82, 151, 15, 254, 101, 72, 162, 180, 25, 122, 196, 91, 240, 198, 230, + 133, 210, 171, 61, 168, 31, 207, 166, 62, 126, 137, 58, 58, 215, 89, 196, 32, 7, 243, 45, 235, + 180, 10, 147, 108, 102, 148, 41, 190, 1, 4, 246, 173, 38, 149, 22, 241, 244, 63, 151, 200, 36, + 150, 99, 141, 148, 213, 170, 198, 32, 3, 127, 184, 40, 17, 194, 24, 186, 31, 167, 103, 162, 171, 234, 52, 191, 197, 201, 175, 217, 230, 31, 216, 93, 227, 79, 185, 7, 14, 166, 193, 138, 32, 5, 198, 163, 129, 217, 40, 49, 109, 212, 20, 113, 205, 18, 187, 6, 2, 41, 105, 178, 32, 112, 127, 81, 84, 120, 159, 217, 95, 160, 16, 1, 38, 32, 0, 214, 196, 180, 99, 92, 89, 104, @@ -207,56 +207,95 @@ static PROOF: [u8; 25527] = [ 161, 129, 74, 133, 100, 113, 62, 104, 54, 249, 65, 156, 152, 203, 181, 174, 32, 7, 10, 198, 8, 71, 203, 123, 22, 215, 252, 95, 209, 228, 106, 1, 250, 148, 203, 149, 7, 35, 101, 250, 210, 186, 174, 64, 246, 179, 223, 66, 30, 32, 5, 242, 65, 116, 15, 209, 144, 160, 137, 244, 39, 200, - 223, 20, 127, 84, 108, 194, 233, 203, 81, 191, 36, 35, 67, 165, 197, 81, 185, 130, 37, 193, 59, - 2, 109, 191, 15, 222, 88, 251, 175, 15, 246, 130, 107, 105, 199, 118, 215, 241, 134, 76, 221, - 63, 47, 49, 81, 45, 0, 176, 60, 72, 117, 236, 249, 84, 2, 32, 3, 48, 127, 250, 48, 148, 100, - 159, 213, 218, 177, 58, 105, 139, 239, 218, 169, 185, 129, 164, 21, 88, 237, 88, 82, 191, 111, - 215, 137, 76, 223, 150, 32, 6, 16, 251, 113, 251, 35, 236, 142, 170, 88, 82, 203, 6, 0, 233, - 114, 105, 131, 238, 92, 207, 172, 58, 109, 219, 24, 246, 118, 55, 208, 230, 78, 5, 82, 112, - 121, 238, 185, 141, 1, 80, 168, 186, 34, 154, 225, 245, 139, 2, 123, 218, 190, 111, 15, 255, - 121, 41, 116, 227, 152, 42, 33, 168, 14, 1, 91, 35, 62, 84, 79, 132, 71, 170, 171, 23, 112, - 214, 190, 237, 191, 69, 250, 105, 10, 169, 108, 130, 87, 63, 201, 38, 178, 114, 139, 43, 91, - 113, 197, 141, 75, 213, 227, 174, 88, 250, 176, 106, 37, 76, 216, 107, 19, 138, 88, 156, 239, - 72, 27, 21, 218, 19, 60, 112, 156, 230, 158, 109, 190, 37, 168, 189, 205, 25, 14, 92, 102, 145, - 190, 239, 54, 179, 10, 15, 6, 103, 175, 66, 228, 134, 136, 118, 9, 64, 139, 92, 75, 55, 96, - 218, 229, 124, 227, 68, 31, 40, 194, 252, 219, 96, 27, 0, 152, 179, 217, 227, 210, 255, 77, 23, - 81, 166, 105, 218, 253, 13, 104, 220, 10, 192, 184, 18, 0, 193, 32, 1, 104, 12, 185, 205, 138, - 208, 244, 253, 33, 222, 248, 187, 128, 233, 27, 56, 12, 110, 239, 76, 137, 124, 240, 234, 232, - 103, 31, 240, 100, 98, 53, 3, 5, 6, 115, 121, 6, 85, 176, 150, 169, 85, 8, 57, 5, 144, 154, 23, - 153, 192, 202, 71, 43, 105, 225, 171, 19, 208, 87, 5, 6, 31, 239, 175, 229, 64, 33, 47, 206, 7, - 162, 188, 218, 42, 204, 87, 160, 251, 213, 62, 180, 7, 121, 99, 0, 211, 63, 30, 254, 106, 31, - 176, 41, 25, 175, 80, 100, 190, 111, 88, 75, 217, 188, 235, 191, 225, 117, 197, 67, 219, 63, - 231, 94, 69, 23, 155, 115, 39, 126, 66, 230, 232, 97, 65, 196, 47, 174, 63, 3, 102, 12, 170, - 97, 193, 165, 130, 39, 228, 52, 78, 243, 4, 108, 238, 148, 139, 4, 138, 220, 22, 151, 90, 245, - 199, 228, 126, 112, 63, 238, 140, 3, 100, 141, 244, 146, 128, 230, 70, 232, 217, 250, 154, 27, - 231, 23, 163, 61, 230, 124, 50, 221, 102, 196, 17, 93, 22, 222, 37, 204, 3, 54, 252, 116, 208, - 45, 93, 164, 82, 210, 92, 91, 63, 69, 76, 186, 185, 17, 78, 26, 238, 81, 20, 223, 109, 188, - 216, 28, 7, 139, 13, 241, 111, 119, 115, 3, 223, 5, 138, 86, 3, 251, 12, 37, 26, 26, 37, 229, - 31, 77, 213, 196, 29, 94, 132, 241, 23, 42, 110, 8, 144, 8, 241, 120, 250, 91, 227, 24, 59, - 118, 27, 166, 110, 151, 252, 42, 38, 13, 148, 178, 163, 7, 86, 243, 206, 99, 51, 45, 174, 176, - 2, 176, 222, 79, 185, 46, 239, 213, 231, 110, 203, 243, 231, 144, 68, 128, 118, 48, 62, 253, - 17, 89, 151, 143, 22, 34, 87, 213, 31, 37, 220, 227, 16, 247, 190, 214, 150, 187, 105, 125, - 102, 15, 145, 3, 159, 192, 79, 117, 81, 131, 127, 69, 145, 20, 160, 108, 209, 65, 86, 163, 214, - 220, 48, 231, 113, 225, 26, 192, 222, 215, 102, 154, 77, 26, 184, 181, 230, 178, 31, 28, 242, - 118, 216, 199, 237, 31, 119, 2, 113, 240, 112, 172, 197, 210, 98, 201, 91, 235, 196, 125, 20, - 119, 2, 25, 24, 146, 211, 241, 4, 74, 100, 162, 210, 220, 234, 124, 46, 120, 142, 236, 102, - 152, 212, 37, 114, 126, 89, 239, 119, 17, 248, 231, 171, 61, 108, 232, 175, 196, 62, 194, 69, - 13, 46, 84, 234, 17, 3, 21, 46, 232, 96, 9, 70, 128, 36, 230, 16, 165, 191, 254, 148, 41, 202, - 229, 89, 212, 233, 141, 168, 174, 229, 140, 224, 81, 97, 29, 1, 23, 2, 22, 151, 247, 233, 81, - 79, 170, 158, 249, 229, 199, 81, 89, 200, 216, 145, 38, 153, 216, 65, 34, 219, 99, 193, 248, - 36, 23, 36, 129, 51, 155, 81, 170, 123, 1, 59, 160, 203, 139, 28, 197, 58, 211, 230, 37, 31, - 85, 193, 140, 119, 108, 130, 78, 25, 191, 43, 91, 161, 3, 0, 250, 166, 193, 194, 161, 166, 73, - 214, 204, 218, 13, 80, 76, 118, 250, 79, 222, 128, 150, 252, 245, 253, 158, 102, 27, 172, 50, - 192, 131, 192, 147, 28, 23, 189, 214, 38, 116, 4, 248, 192, 87, 143, 63, 134, 25, 6, 201, 48, - 121, 23, 233, 211, 162, 185, 219, 176, 27, 21, 61, 121, 58, 228, 60, 47, 126, 131, 56, 51, 84, - 132, 13, 81, 124, 22, 98, 17, 50, 110, 109, 59, 181, 109, 154, 84, 88, 60, 188, 0, 191, 64, 46, - 229, 182, 32, 20, 2, 74, 42, 18, 235, 9, 249, 64, 116, 65, 54, 67, 1, 118, 34, 236, 213, 63, - 88, 202, 131, 114, 138, 154, 105, 29, 58, 106, 66, 201, 200, 74, 150, 27, 149, 159, 90, 213, - 12, 112, 252, 64, 244, 104, 220, 50, 128, 113, 201, 104, 20, 107, 144, 30, 58, 28, 189, 44, 89, - 107, 176, 167, 113, 52, 250, 5, 32, 2, 78, 97, 121, 127, 238, 47, 119, 199, 15, 200, 125, 16, - 150, 254, 168, 168, 59, 109, 247, 150, 38, 227, 91, 124, 212, 64, 194, 25, 127, 188, 125, 32, - 0, 113, 249, 249, 255, 228, 13, 136, 153, 122, 192, 187, 56, 42, 197, 101, 118, 59, 124, 61, - 95, 37, 92, 208, 123, 153, 43, 38, 94, 210, 169, 139, 32, 6, 167, 37, 185, 208, 123, 36, 223, + 223, 20, 127, 84, 108, 194, 233, 203, 81, 191, 36, 35, 67, 165, 197, 81, 185, 130, 37, 193, 23, + 2, 1, 109, 191, 15, 222, 88, 251, 175, 15, 246, 130, 107, 105, 199, 118, 215, 241, 134, 76, + 221, 63, 47, 49, 81, 45, 0, 176, 60, 72, 117, 236, 249, 84, 2, 32, 3, 48, 127, 250, 48, 148, + 100, 159, 213, 218, 177, 58, 105, 139, 239, 218, 169, 185, 129, 164, 21, 88, 237, 88, 82, 191, + 111, 215, 137, 76, 223, 150, 32, 6, 16, 251, 113, 251, 35, 236, 142, 170, 88, 82, 203, 6, 0, + 233, 114, 105, 131, 238, 92, 207, 172, 58, 109, 219, 24, 246, 118, 55, 208, 230, 78, 5, 82, + 112, 121, 238, 185, 141, 1, 80, 168, 186, 34, 154, 225, 245, 139, 2, 123, 218, 190, 111, 15, + 255, 121, 41, 116, 227, 152, 42, 33, 168, 14, 1, 91, 35, 62, 84, 79, 132, 71, 170, 171, 23, + 112, 214, 190, 237, 191, 69, 250, 105, 10, 169, 108, 130, 87, 63, 201, 38, 178, 114, 139, 43, + 91, 113, 197, 141, 75, 213, 227, 174, 88, 250, 176, 106, 37, 76, 216, 107, 19, 138, 88, 156, + 239, 72, 27, 21, 218, 19, 60, 112, 156, 230, 158, 109, 190, 37, 168, 189, 205, 25, 14, 92, 102, + 145, 190, 239, 54, 179, 10, 15, 6, 103, 175, 66, 228, 134, 136, 118, 9, 64, 139, 92, 75, 55, + 96, 218, 229, 124, 227, 68, 31, 40, 194, 252, 219, 96, 27, 0, 152, 179, 217, 227, 210, 255, 77, + 23, 81, 166, 105, 218, 253, 13, 104, 220, 10, 192, 184, 18, 0, 193, 32, 1, 104, 12, 185, 205, + 138, 208, 244, 253, 33, 222, 248, 187, 128, 233, 27, 56, 12, 110, 239, 76, 137, 124, 240, 234, + 232, 103, 31, 240, 100, 98, 53, 3, 5, 6, 115, 121, 6, 85, 176, 150, 169, 85, 8, 57, 5, 144, + 154, 23, 153, 192, 202, 71, 43, 105, 225, 171, 19, 208, 87, 5, 6, 31, 239, 175, 229, 64, 33, + 47, 206, 7, 162, 188, 218, 42, 204, 87, 160, 251, 213, 62, 180, 7, 121, 99, 0, 211, 63, 30, + 254, 106, 31, 176, 41, 25, 175, 80, 100, 190, 111, 88, 75, 217, 188, 235, 191, 225, 117, 197, + 67, 219, 63, 231, 94, 69, 23, 155, 115, 39, 126, 66, 230, 232, 97, 65, 196, 47, 174, 63, 3, + 102, 12, 170, 97, 193, 165, 130, 39, 228, 52, 78, 243, 4, 108, 238, 148, 139, 4, 138, 220, 22, + 151, 90, 245, 199, 228, 126, 112, 63, 238, 140, 3, 100, 141, 244, 146, 128, 230, 70, 232, 217, + 250, 154, 27, 231, 23, 163, 61, 230, 124, 50, 221, 102, 196, 17, 93, 22, 222, 37, 204, 3, 54, + 252, 116, 208, 45, 93, 164, 82, 210, 92, 91, 63, 69, 76, 186, 185, 17, 78, 26, 238, 81, 20, + 223, 109, 188, 216, 28, 7, 139, 13, 241, 111, 119, 115, 3, 223, 5, 138, 86, 3, 251, 12, 37, 26, + 26, 37, 229, 31, 77, 213, 196, 29, 94, 132, 241, 23, 42, 110, 8, 144, 8, 241, 120, 250, 91, + 227, 24, 59, 118, 27, 166, 110, 151, 252, 42, 38, 13, 148, 178, 163, 7, 86, 243, 206, 99, 51, + 45, 174, 176, 2, 176, 222, 79, 185, 46, 239, 213, 231, 110, 203, 243, 231, 144, 68, 128, 118, + 48, 62, 253, 17, 89, 151, 143, 22, 34, 87, 213, 31, 37, 220, 227, 16, 247, 190, 214, 150, 187, + 105, 125, 102, 15, 145, 3, 159, 192, 79, 117, 81, 131, 127, 69, 145, 20, 160, 108, 209, 65, 86, + 163, 214, 220, 48, 231, 113, 225, 26, 192, 222, 215, 102, 154, 77, 26, 184, 181, 230, 178, 31, + 28, 242, 118, 216, 199, 237, 31, 119, 2, 113, 240, 112, 172, 197, 210, 98, 201, 91, 235, 196, + 125, 20, 119, 2, 25, 24, 146, 211, 241, 4, 74, 100, 162, 210, 220, 234, 124, 46, 120, 142, 236, + 102, 152, 212, 37, 114, 126, 89, 239, 119, 17, 248, 231, 171, 61, 108, 232, 175, 196, 62, 194, + 69, 13, 46, 84, 234, 17, 3, 21, 46, 232, 96, 9, 70, 128, 36, 230, 16, 165, 191, 254, 148, 41, + 202, 229, 89, 212, 233, 141, 168, 174, 229, 140, 224, 81, 97, 29, 1, 23, 2, 22, 151, 247, 233, + 81, 79, 170, 158, 249, 229, 199, 81, 89, 200, 216, 145, 38, 153, 216, 65, 34, 219, 99, 193, + 248, 36, 23, 36, 129, 51, 155, 81, 170, 123, 1, 59, 160, 203, 139, 28, 197, 58, 211, 230, 37, + 31, 85, 193, 140, 119, 108, 130, 78, 25, 191, 43, 91, 161, 3, 0, 250, 166, 193, 194, 161, 166, + 73, 214, 204, 218, 13, 80, 76, 118, 250, 79, 222, 128, 150, 252, 245, 253, 158, 102, 27, 172, + 50, 192, 131, 192, 147, 28, 23, 189, 214, 38, 116, 4, 248, 192, 87, 143, 63, 134, 25, 6, 201, + 48, 121, 23, 233, 211, 162, 185, 219, 176, 27, 21, 61, 121, 58, 228, 60, 47, 126, 131, 56, 51, + 84, 132, 13, 81, 124, 22, 98, 17, 50, 110, 109, 59, 181, 109, 154, 84, 88, 60, 188, 0, 191, 64, + 46, 229, 182, 32, 20, 2, 74, 42, 18, 235, 9, 249, 64, 116, 65, 54, 67, 1, 118, 34, 236, 213, + 63, 88, 202, 131, 114, 138, 154, 105, 29, 58, 106, 66, 201, 200, 74, 150, 27, 149, 159, 90, + 213, 12, 112, 252, 64, 244, 104, 220, 50, 128, 113, 201, 104, 20, 107, 144, 30, 58, 28, 189, + 44, 89, 107, 176, 167, 113, 52, 250, 5, 32, 2, 78, 97, 121, 127, 238, 47, 119, 199, 15, 200, + 125, 16, 150, 254, 168, 168, 59, 109, 247, 150, 38, 227, 91, 124, 212, 64, 194, 25, 127, 188, + 125, 32, 0, 113, 249, 249, 255, 228, 13, 136, 153, 122, 192, 187, 56, 42, 197, 101, 118, 59, + 124, 61, 95, 37, 92, 208, 123, 153, 43, 38, 94, 210, 169, 139, 32, 6, 167, 37, 185, 208, 123, + 36, 223, 152, 196, 150, 147, 157, 29, 252, 246, 66, 97, 248, 67, 241, 28, 215, 27, 63, 0, 229, + 114, 99, 207, 95, 21, 32, 2, 112, 59, 63, 171, 54, 166, 178, 221, 202, 101, 39, 130, 200, 103, + 184, 44, 166, 220, 246, 108, 106, 76, 54, 33, 134, 47, 117, 48, 240, 255, 173, 32, 5, 196, 143, + 35, 233, 0, 32, 54, 82, 255, 118, 12, 30, 80, 201, 187, 151, 120, 176, 215, 146, 164, 12, 194, + 190, 166, 214, 43, 224, 224, 149, 217, 5, 6, 168, 38, 220, 25, 149, 71, 84, 129, 64, 73, 73, + 59, 140, 69, 111, 67, 195, 15, 178, 147, 177, 105, 84, 186, 79, 63, 194, 33, 85, 112, 1, 213, + 33, 47, 206, 7, 162, 188, 218, 42, 204, 87, 160, 251, 213, 62, 180, 7, 121, 99, 0, 211, 63, 30, + 254, 106, 31, 176, 41, 25, 175, 80, 100, 190, 111, 88, 75, 217, 188, 235, 191, 225, 117, 197, + 67, 219, 63, 231, 94, 69, 23, 155, 115, 39, 126, 66, 230, 232, 97, 65, 196, 47, 174, 63, 3, + 102, 12, 170, 97, 193, 165, 130, 39, 228, 52, 78, 243, 4, 108, 238, 148, 139, 4, 138, 220, 22, + 151, 90, 245, 199, 228, 126, 112, 63, 238, 140, 3, 100, 141, 244, 146, 128, 230, 70, 232, 217, + 250, 154, 27, 231, 23, 163, 61, 230, 124, 50, 221, 102, 196, 17, 93, 22, 222, 37, 204, 3, 54, + 252, 116, 208, 45, 93, 164, 82, 210, 92, 91, 63, 69, 76, 186, 185, 17, 78, 26, 238, 81, 20, + 223, 109, 188, 216, 28, 7, 139, 13, 241, 111, 119, 115, 3, 223, 5, 138, 86, 3, 251, 12, 37, 26, + 26, 37, 229, 31, 77, 213, 196, 29, 94, 132, 241, 23, 42, 110, 8, 144, 8, 241, 120, 250, 91, + 227, 24, 59, 118, 27, 166, 110, 151, 252, 42, 38, 13, 148, 178, 163, 7, 86, 243, 206, 99, 51, + 45, 174, 176, 2, 176, 222, 79, 185, 46, 239, 213, 231, 110, 203, 243, 231, 144, 68, 128, 118, + 48, 62, 253, 17, 89, 151, 143, 22, 34, 87, 213, 31, 37, 220, 227, 16, 247, 190, 214, 150, 187, + 105, 125, 102, 15, 145, 3, 159, 192, 79, 117, 81, 131, 127, 69, 145, 20, 160, 108, 209, 65, 86, + 163, 214, 220, 48, 231, 113, 225, 26, 192, 222, 215, 102, 154, 77, 26, 184, 181, 230, 178, 31, + 28, 242, 118, 216, 199, 237, 31, 119, 2, 113, 240, 112, 172, 197, 210, 98, 201, 91, 235, 196, + 125, 20, 119, 2, 25, 24, 146, 211, 241, 4, 74, 100, 162, 210, 220, 234, 124, 46, 120, 142, 236, + 102, 152, 212, 37, 114, 126, 89, 239, 119, 17, 248, 231, 171, 61, 108, 232, 175, 196, 62, 194, + 69, 13, 46, 84, 234, 17, 3, 21, 46, 232, 96, 9, 70, 128, 36, 230, 16, 165, 191, 254, 148, 41, + 202, 229, 89, 212, 233, 141, 168, 174, 229, 140, 224, 81, 97, 29, 1, 23, 2, 22, 151, 247, 233, + 81, 79, 170, 158, 249, 229, 199, 81, 89, 200, 216, 145, 38, 153, 216, 65, 34, 219, 99, 193, + 248, 36, 23, 36, 129, 51, 155, 81, 170, 123, 1, 59, 160, 203, 139, 28, 197, 58, 211, 230, 37, + 31, 85, 193, 140, 119, 108, 130, 78, 25, 191, 43, 91, 161, 3, 0, 250, 166, 193, 194, 161, 166, + 73, 214, 204, 218, 13, 80, 76, 118, 250, 79, 222, 128, 150, 252, 245, 253, 158, 102, 27, 172, + 50, 192, 131, 192, 147, 28, 23, 189, 214, 38, 116, 4, 248, 192, 87, 143, 63, 134, 25, 6, 201, + 48, 121, 23, 233, 211, 162, 185, 219, 176, 27, 21, 61, 121, 58, 228, 60, 47, 126, 131, 56, 51, + 84, 132, 13, 81, 124, 22, 98, 17, 50, 110, 109, 59, 181, 109, 154, 84, 88, 60, 188, 0, 191, 64, + 46, 229, 182, 32, 20, 2, 74, 42, 18, 235, 9, 249, 64, 116, 65, 54, 67, 1, 118, 34, 236, 213, + 63, 88, 202, 131, 114, 138, 154, 105, 29, 58, 106, 66, 201, 200, 74, 150, 27, 149, 159, 90, + 213, 12, 112, 252, 64, 244, 104, 220, 50, 128, 113, 201, 104, 20, 107, 144, 30, 58, 28, 189, + 44, 89, 107, 176, 167, 113, 52, 250, 5, 32, 4, 87, 105, 19, 100, 49, 206, 121, 89, 252, 243, + 252, 225, 44, 180, 118, 113, 8, 186, 165, 2, 200, 56, 154, 176, 142, 73, 83, 118, 20, 113, 72, + 32, 6, 136, 174, 25, 103, 129, 228, 236, 152, 40, 59, 146, 249, 150, 237, 20, 81, 215, 10, 200, + 139, 59, 35, 133, 200, 43, 47, 215, 22, 184, 229, 2, 32, 6, 167, 37, 185, 208, 123, 36, 223, 152, 196, 150, 147, 157, 29, 252, 246, 66, 97, 248, 67, 241, 28, 215, 27, 63, 0, 229, 114, 99, 207, 95, 21, 32, 2, 112, 59, 63, 171, 54, 166, 178, 221, 202, 101, 39, 130, 200, 103, 184, 44, 166, 220, 246, 108, 106, 76, 54, 33, 134, 47, 117, 48, 240, 255, 173, 32, 5, 196, 143, 35, 233, @@ -300,45 +339,6 @@ static PROOF: [u8; 25527] = [ 95, 21, 32, 2, 112, 59, 63, 171, 54, 166, 178, 221, 202, 101, 39, 130, 200, 103, 184, 44, 166, 220, 246, 108, 106, 76, 54, 33, 134, 47, 117, 48, 240, 255, 173, 32, 5, 196, 143, 35, 233, 0, 32, 54, 82, 255, 118, 12, 30, 80, 201, 187, 151, 120, 176, 215, 146, 164, 12, 194, 190, 166, - 214, 43, 224, 224, 149, 217, 5, 6, 168, 38, 220, 25, 149, 71, 84, 129, 64, 73, 73, 59, 140, 69, - 111, 67, 195, 15, 178, 147, 177, 105, 84, 186, 79, 63, 194, 33, 85, 112, 1, 213, 33, 47, 206, - 7, 162, 188, 218, 42, 204, 87, 160, 251, 213, 62, 180, 7, 121, 99, 0, 211, 63, 30, 254, 106, - 31, 176, 41, 25, 175, 80, 100, 190, 111, 88, 75, 217, 188, 235, 191, 225, 117, 197, 67, 219, - 63, 231, 94, 69, 23, 155, 115, 39, 126, 66, 230, 232, 97, 65, 196, 47, 174, 63, 3, 102, 12, - 170, 97, 193, 165, 130, 39, 228, 52, 78, 243, 4, 108, 238, 148, 139, 4, 138, 220, 22, 151, 90, - 245, 199, 228, 126, 112, 63, 238, 140, 3, 100, 141, 244, 146, 128, 230, 70, 232, 217, 250, 154, - 27, 231, 23, 163, 61, 230, 124, 50, 221, 102, 196, 17, 93, 22, 222, 37, 204, 3, 54, 252, 116, - 208, 45, 93, 164, 82, 210, 92, 91, 63, 69, 76, 186, 185, 17, 78, 26, 238, 81, 20, 223, 109, - 188, 216, 28, 7, 139, 13, 241, 111, 119, 115, 3, 223, 5, 138, 86, 3, 251, 12, 37, 26, 26, 37, - 229, 31, 77, 213, 196, 29, 94, 132, 241, 23, 42, 110, 8, 144, 8, 241, 120, 250, 91, 227, 24, - 59, 118, 27, 166, 110, 151, 252, 42, 38, 13, 148, 178, 163, 7, 86, 243, 206, 99, 51, 45, 174, - 176, 2, 176, 222, 79, 185, 46, 239, 213, 231, 110, 203, 243, 231, 144, 68, 128, 118, 48, 62, - 253, 17, 89, 151, 143, 22, 34, 87, 213, 31, 37, 220, 227, 16, 247, 190, 214, 150, 187, 105, - 125, 102, 15, 145, 3, 159, 192, 79, 117, 81, 131, 127, 69, 145, 20, 160, 108, 209, 65, 86, 163, - 214, 220, 48, 231, 113, 225, 26, 192, 222, 215, 102, 154, 77, 26, 184, 181, 230, 178, 31, 28, - 242, 118, 216, 199, 237, 31, 119, 2, 113, 240, 112, 172, 197, 210, 98, 201, 91, 235, 196, 125, - 20, 119, 2, 25, 24, 146, 211, 241, 4, 74, 100, 162, 210, 220, 234, 124, 46, 120, 142, 236, 102, - 152, 212, 37, 114, 126, 89, 239, 119, 17, 248, 231, 171, 61, 108, 232, 175, 196, 62, 194, 69, - 13, 46, 84, 234, 17, 3, 21, 46, 232, 96, 9, 70, 128, 36, 230, 16, 165, 191, 254, 148, 41, 202, - 229, 89, 212, 233, 141, 168, 174, 229, 140, 224, 81, 97, 29, 1, 23, 2, 22, 151, 247, 233, 81, - 79, 170, 158, 249, 229, 199, 81, 89, 200, 216, 145, 38, 153, 216, 65, 34, 219, 99, 193, 248, - 36, 23, 36, 129, 51, 155, 81, 170, 123, 1, 59, 160, 203, 139, 28, 197, 58, 211, 230, 37, 31, - 85, 193, 140, 119, 108, 130, 78, 25, 191, 43, 91, 161, 3, 0, 250, 166, 193, 194, 161, 166, 73, - 214, 204, 218, 13, 80, 76, 118, 250, 79, 222, 128, 150, 252, 245, 253, 158, 102, 27, 172, 50, - 192, 131, 192, 147, 28, 23, 189, 214, 38, 116, 4, 248, 192, 87, 143, 63, 134, 25, 6, 201, 48, - 121, 23, 233, 211, 162, 185, 219, 176, 27, 21, 61, 121, 58, 228, 60, 47, 126, 131, 56, 51, 84, - 132, 13, 81, 124, 22, 98, 17, 50, 110, 109, 59, 181, 109, 154, 84, 88, 60, 188, 0, 191, 64, 46, - 229, 182, 32, 20, 2, 74, 42, 18, 235, 9, 249, 64, 116, 65, 54, 67, 1, 118, 34, 236, 213, 63, - 88, 202, 131, 114, 138, 154, 105, 29, 58, 106, 66, 201, 200, 74, 150, 27, 149, 159, 90, 213, - 12, 112, 252, 64, 244, 104, 220, 50, 128, 113, 201, 104, 20, 107, 144, 30, 58, 28, 189, 44, 89, - 107, 176, 167, 113, 52, 250, 5, 32, 4, 87, 105, 19, 100, 49, 206, 121, 89, 252, 243, 252, 225, - 44, 180, 118, 113, 8, 186, 165, 2, 200, 56, 154, 176, 142, 73, 83, 118, 20, 113, 72, 32, 6, - 136, 174, 25, 103, 129, 228, 236, 152, 40, 59, 146, 249, 150, 237, 20, 81, 215, 10, 200, 139, - 59, 35, 133, 200, 43, 47, 215, 22, 184, 229, 2, 32, 6, 167, 37, 185, 208, 123, 36, 223, 152, - 196, 150, 147, 157, 29, 252, 246, 66, 97, 248, 67, 241, 28, 215, 27, 63, 0, 229, 114, 99, 207, - 95, 21, 32, 2, 112, 59, 63, 171, 54, 166, 178, 221, 202, 101, 39, 130, 200, 103, 184, 44, 166, - 220, 246, 108, 106, 76, 54, 33, 134, 47, 117, 48, 240, 255, 173, 32, 5, 196, 143, 35, 233, 0, - 32, 54, 82, 255, 118, 12, 30, 80, 201, 187, 151, 120, 176, 215, 146, 164, 12, 194, 190, 166, 214, 43, 224, 224, 149, 217, 3, 7, 126, 40, 243, 172, 167, 175, 234, 240, 174, 123, 250, 6, 48, 63, 188, 203, 225, 130, 112, 128, 54, 46, 62, 66, 37, 235, 178, 113, 16, 113, 73, 110, 112, 159, 196, 68, 95, 225, 214, 36, 29, 252, 221, 60, 143, 203, 240, 62, 161, 247, 182, 212, 232, diff --git a/provers/stark/src/constraints/boundary.rs b/provers/stark/src/constraints/boundary.rs index 6fc6e97c53..50a4bbb988 100644 --- a/provers/stark/src/constraints/boundary.rs +++ b/provers/stark/src/constraints/boundary.rs @@ -14,19 +14,45 @@ pub struct BoundaryConstraint { pub col: usize, pub step: usize, pub value: FieldElement, + pub is_aux: bool, } impl BoundaryConstraint { - pub fn new(col: usize, step: usize, value: FieldElement) -> Self { - Self { col, step, value } + pub fn new_main(col: usize, step: usize, value: FieldElement) -> Self { + Self { + col, + step, + value, + is_aux: false, + } + } + + pub fn new_aux(col: usize, step: usize, value: FieldElement) -> Self { + Self { + col, + step, + value, + is_aux: true, + } + } + + /// Used for creating boundary constraints for a trace with only one column + pub fn new_simple_main(step: usize, value: FieldElement) -> Self { + Self { + col: 0, + step, + value, + is_aux: false, + } } /// Used for creating boundary constraints for a trace with only one column - pub fn new_simple(step: usize, value: FieldElement) -> Self { + pub fn new_simple_aux(step: usize, value: FieldElement) -> Self { Self { col: 0, step, value, + is_aux: true, } } } @@ -150,9 +176,9 @@ mod test { // * a0 = 1 // * a1 = 1 // * a7 = 32 - let a0 = BoundaryConstraint::new_simple(0, one); - let a1 = BoundaryConstraint::new_simple(1, one); - let result = BoundaryConstraint::new_simple(7, FieldElement::::from(32)); + let a0 = BoundaryConstraint::new_simple_main(0, one); + let a1 = BoundaryConstraint::new_simple_main(1, one); + let result = BoundaryConstraint::new_simple_main(7, FieldElement::::from(32)); let constraints = BoundaryConstraints::from_constraints(vec![a0, a1, result]); diff --git a/provers/stark/src/constraints/evaluator.rs b/provers/stark/src/constraints/evaluator.rs index 6850f53145..1cc889b73b 100644 --- a/provers/stark/src/constraints/evaluator.rs +++ b/provers/stark/src/constraints/evaluator.rs @@ -17,9 +17,8 @@ use rayon::prelude::{ use super::boundary::BoundaryConstraints; #[cfg(all(debug_assertions, not(feature = "parallel")))] use crate::debug::check_boundary_polys_divisibility; -use crate::domain::Domain; -use crate::trace::TraceTable; use crate::traits::AIR; +use crate::{domain::Domain, table::EvaluationTable}; use crate::{frame::Frame, prover::evaluate_polynomial_on_lde_domain}; pub struct ConstraintEvaluator { @@ -34,10 +33,10 @@ impl ConstraintEvaluator { } } - pub fn evaluate( + pub(crate) fn evaluate( &self, air: &A, - lde_trace: &TraceTable, + lde_table: &EvaluationTable, domain: &Domain, transition_coefficients: &[FieldElement], boundary_coefficients: &[FieldElement], @@ -83,27 +82,30 @@ impl ConstraintEvaluator { &domain.coset_offset, ) }) - .collect::>>, FFTError>>() + .collect::>>, FFTError>>() .unwrap(); - let n_col = lde_trace.n_cols(); - let n_elem = domain.lde_roots_of_unity_coset.len(); let boundary_polys_evaluations = boundary_constraints .constraints .iter() .map(|constraint| { - let col = constraint.col; - lde_trace - .table - .data - .iter() - .skip(col) - .step_by(n_col) - .take(n_elem) - .map(|v| -&constraint.value + v) - .collect::>>() + if constraint.is_aux { + (0..lde_table.n_rows()) + .map(|row| { + let v = lde_table.get_aux(row, constraint.col); + v - &constraint.value + }) + .collect() + } else { + (0..lde_table.n_rows()) + .map(|row| { + let v = lde_table.get_main(row, constraint.col); + v - &constraint.value + }) + .collect() + } }) - .collect::>>>(); + .collect::>>>(); #[cfg(feature = "parallel")] let boundary_eval_iter = (0..domain.lde_roots_of_unity_coset.len()).into_par_iter(); @@ -182,8 +184,8 @@ impl ConstraintEvaluator { .zip(&boundary_evaluation) .zip(zerofier_iter) .map(|((i, boundary), zerofier)| { - let frame = Frame::read_from_trace( - lde_trace, + let frame = Frame::::read_from_lde_table( + lde_table, i, blowup_factor, &air.context().transition_offsets, @@ -191,13 +193,13 @@ impl ConstraintEvaluator { let periodic_values: Vec<_> = lde_periodic_columns .iter() - .map(|col| col[i].clone().to_extension()) + .map(|col| col[i].clone()) .collect(); // Compute all the transition constraints at this // point of the LDE domain. let evaluations_transition = - air.compute_transition(&frame, &periodic_values, rap_challenges); + air.compute_transition_prover(&frame, &periodic_values, rap_challenges); #[cfg(all(debug_assertions, not(feature = "parallel")))] transition_evaluations.push(evaluations_transition.clone()); diff --git a/provers/stark/src/debug.rs b/provers/stark/src/debug.rs index f9d264298e..85fc3a1af5 100644 --- a/provers/stark/src/debug.rs +++ b/provers/stark/src/debug.rs @@ -1,5 +1,4 @@ -use crate::frame::Frame; -use crate::trace::TraceTable; +use crate::{frame::Frame, table::EvaluationTable}; use super::domain::Domain; use super::traits::AIR; @@ -15,14 +14,26 @@ use log::{error, info}; /// Validates that the trace is valid with respect to the supplied AIR constraints pub fn validate_trace( air: &A, - trace_polys: &[Polynomial>], + main_trace_polys: &[Polynomial>], + aux_trace_polys: &[Polynomial>], domain: &Domain, rap_challenges: &A::RAPChallenges, ) -> bool { info!("Starting constraints validation over trace..."); let mut ret = true; - let trace_columns: Vec<_> = trace_polys + let main_trace_columns: Vec<_> = main_trace_polys + .iter() + .map(|poly| { + Polynomial::>::evaluate_fft::( + poly, + 1, + Some(domain.interpolation_domain_size), + ) + .unwrap() + }) + .collect(); + let aux_trace_columns: Vec<_> = aux_trace_polys .iter() .map(|poly| { Polynomial::evaluate_fft::(poly, 1, Some(domain.interpolation_domain_size)) @@ -30,14 +41,19 @@ pub fn validate_trace( }) .collect(); - let trace = TraceTable::from_columns(trace_columns, A::STEP_SIZE); + let lde_table = + EvaluationTable::from_columns(main_trace_columns, aux_trace_columns, A::STEP_SIZE); let periodic_columns: Vec<_> = air .get_periodic_column_polynomials() .iter() .map(|poly| { - Polynomial::evaluate_fft::(poly, 1, Some(domain.interpolation_domain_size)) - .unwrap() + Polynomial::>::evaluate_fft::( + poly, + 1, + Some(domain.interpolation_domain_size), + ) + .unwrap() }) .collect(); @@ -49,9 +65,14 @@ pub fn validate_trace( let col = constraint.col; let step = constraint.step; let boundary_value = constraint.value.clone(); - let trace_value = trace.get(step, col); - if &boundary_value.clone().to_extension() != trace_value { + let trace_value = if !constraint.is_aux { + lde_table.get_main(step, col).clone().to_extension() + } else { + lde_table.get_aux(step, col).clone() + }; + + if boundary_value.clone().to_extension() != trace_value { ret = false; error!("Boundary constraint inconsistency - Expected value {:?} in step {} and column {}, found: {:?}", boundary_value, step, col, trace_value); } @@ -61,21 +82,22 @@ pub fn validate_trace( let n_transition_constraints = air.context().num_transition_constraints(); let transition_exemptions = &air.context().transition_exemptions; - let exemption_steps: Vec = vec![trace.n_rows(); n_transition_constraints] + let exemption_steps: Vec = vec![lde_table.n_rows(); n_transition_constraints] .iter() .zip(transition_exemptions) .map(|(trace_steps, exemptions)| trace_steps - exemptions) .collect(); // Iterate over trace and compute transitions - for step in 0..trace.num_steps() { - let frame = Frame::read_from_trace(&trace, step, 1, &air.context().transition_offsets); + for step in 0..lde_table.num_steps() { + let frame = + Frame::read_from_lde_table(&lde_table, step, 1, &air.context().transition_offsets); let periodic_values: Vec<_> = periodic_columns .iter() .map(|col| col[step].clone()) .collect(); - let evaluations = air.compute_transition(&frame, &periodic_values, rap_challenges); + let evaluations = air.compute_transition_prover(&frame, &periodic_values, rap_challenges); // Iterate over each transition evaluation. When the evaluated step is not from // the exemption steps corresponding to the transition, it should have zero as a diff --git a/provers/stark/src/examples/dummy_air.rs b/provers/stark/src/examples/dummy_air.rs index d273533314..b85c49e9c0 100644 --- a/provers/stark/src/examples/dummy_air.rs +++ b/provers/stark/src/examples/dummy_air.rs @@ -59,20 +59,20 @@ impl AIR for DummyAIR { _transcript: &mut impl IsStarkTranscript, ) -> Self::RAPChallenges { } - fn compute_transition( + fn compute_transition_prover( &self, - frame: &Frame, + frame: &Frame, _periodic_values: &[FieldElement], _rap_challenges: &Self::RAPChallenges, - ) -> Vec> { + ) -> Vec> { let first_step = frame.get_evaluation_step(0); let second_step = frame.get_evaluation_step(1); let third_step = frame.get_evaluation_step(2); - let flag = first_step.get_evaluation_element(0, 0); - let a0 = first_step.get_evaluation_element(0, 1); - let a1 = second_step.get_evaluation_element(0, 1); - let a2 = third_step.get_evaluation_element(0, 1); + let flag = first_step.get_main_evaluation_element(0, 0); + let a0 = first_step.get_main_evaluation_element(0, 1); + let a1 = second_step.get_main_evaluation_element(0, 1); + let a2 = third_step.get_main_evaluation_element(0, 1); let f_constraint = flag * (flag - FieldElement::one()); @@ -85,8 +85,8 @@ impl AIR for DummyAIR { &self, _rap_challenges: &Self::RAPChallenges, ) -> BoundaryConstraints { - let a0 = BoundaryConstraint::new(1, 0, FieldElement::::one()); - let a1 = BoundaryConstraint::new(1, 1, FieldElement::::one()); + let a0 = BoundaryConstraint::new_main(1, 0, FieldElement::::one()); + let a1 = BoundaryConstraint::new_main(1, 1, FieldElement::::one()); BoundaryConstraints::from_constraints(vec![a0, a1]) } @@ -110,6 +110,15 @@ impl AIR for DummyAIR { fn pub_inputs(&self) -> &Self::PublicInputs { &() } + + fn compute_transition_verifier( + &self, + frame: &Frame, + periodic_values: &[FieldElement], + rap_challenges: &Self::RAPChallenges, + ) -> Vec> { + self.compute_transition_prover(frame, periodic_values, rap_challenges) + } } pub fn dummy_trace(trace_length: usize) -> TraceTable { diff --git a/provers/stark/src/examples/fibonacci_2_cols_shifted.rs b/provers/stark/src/examples/fibonacci_2_cols_shifted.rs index c50e6b937b..d5c220d01a 100644 --- a/provers/stark/src/examples/fibonacci_2_cols_shifted.rs +++ b/provers/stark/src/examples/fibonacci_2_cols_shifted.rs @@ -93,20 +93,20 @@ where ) -> Self::RAPChallenges { } - fn compute_transition( + fn compute_transition_prover( &self, - frame: &Frame, + frame: &Frame, _periodic_values: &[FieldElement], _rap_challenges: &Self::RAPChallenges, ) -> Vec> { let first_row = frame.get_evaluation_step(0); let second_row = frame.get_evaluation_step(1); - let a0_0 = first_row.get_evaluation_element(0, 0); - let a0_1 = first_row.get_evaluation_element(0, 1); + let a0_0 = first_row.get_main_evaluation_element(0, 0); + let a0_1 = first_row.get_main_evaluation_element(0, 1); - let a1_0 = second_row.get_evaluation_element(0, 0); - let a1_1 = second_row.get_evaluation_element(0, 1); + let a1_0 = second_row.get_main_evaluation_element(0, 0); + let a1_1 = second_row.get_main_evaluation_element(0, 1); let first_transition = a1_0 - a0_1; let second_transition = a1_1 - a0_0 - a0_1; @@ -122,8 +122,8 @@ where &self, _rap_challenges: &Self::RAPChallenges, ) -> BoundaryConstraints { - let initial_condition = BoundaryConstraint::new(0, 0, FieldElement::one()); - let claimed_value_constraint = BoundaryConstraint::new( + let initial_condition = BoundaryConstraint::new_main(0, 0, FieldElement::one()); + let claimed_value_constraint = BoundaryConstraint::new_main( 0, self.pub_inputs.claimed_index, self.pub_inputs.claimed_value.clone(), @@ -147,6 +147,15 @@ where fn pub_inputs(&self) -> &Self::PublicInputs { &self.pub_inputs } + + fn compute_transition_verifier( + &self, + frame: &Frame, + periodic_values: &[FieldElement], + rap_challenges: &Self::RAPChallenges, + ) -> Vec> { + self.compute_transition_prover(frame, periodic_values, rap_challenges) + } } pub fn compute_trace( diff --git a/provers/stark/src/examples/fibonacci_2_columns.rs b/provers/stark/src/examples/fibonacci_2_columns.rs index 0534fededd..98c93b72b3 100644 --- a/provers/stark/src/examples/fibonacci_2_columns.rs +++ b/provers/stark/src/examples/fibonacci_2_columns.rs @@ -69,9 +69,9 @@ where ) -> Self::RAPChallenges { } - fn compute_transition( + fn compute_transition_prover( &self, - frame: &Frame, + frame: &Frame, _periodic_values: &[FieldElement], _rap_challenges: &Self::RAPChallenges, ) -> Vec> { @@ -81,10 +81,10 @@ where // constraints of Fibonacci sequence (2 terms per step): // s_{0, i+1} = s_{0, i} + s_{1, i} // s_{1, i+1} = s_{1, i} + s_{0, i+1} - let s0_0 = first_step.get_evaluation_element(0, 0); - let s0_1 = first_step.get_evaluation_element(0, 1); - let s1_0 = second_step.get_evaluation_element(0, 0); - let s1_1 = second_step.get_evaluation_element(0, 1); + let s0_0 = first_step.get_main_evaluation_element(0, 0); + let s0_1 = first_step.get_main_evaluation_element(0, 1); + let s1_0 = second_step.get_main_evaluation_element(0, 0); + let s1_1 = second_step.get_main_evaluation_element(0, 1); let first_transition = s1_0 - s0_0 - s0_1; let second_transition = s1_1 - s0_1 - s1_0; @@ -100,8 +100,8 @@ where &self, _rap_challenges: &Self::RAPChallenges, ) -> BoundaryConstraints { - let a0 = BoundaryConstraint::new(0, 0, self.pub_inputs.a0.clone()); - let a1 = BoundaryConstraint::new(1, 0, self.pub_inputs.a1.clone()); + let a0 = BoundaryConstraint::new_main(0, 0, self.pub_inputs.a0.clone()); + let a1 = BoundaryConstraint::new_main(1, 0, self.pub_inputs.a1.clone()); BoundaryConstraints::from_constraints(vec![a0, a1]) } @@ -121,6 +121,15 @@ where fn pub_inputs(&self) -> &Self::PublicInputs { &self.pub_inputs } + + fn compute_transition_verifier( + &self, + frame: &Frame, + periodic_values: &[FieldElement], + rap_challenges: &Self::RAPChallenges, + ) -> Vec> { + self.compute_transition_prover(frame, periodic_values, rap_challenges) + } } pub fn compute_trace( diff --git a/provers/stark/src/examples/fibonacci_rap.rs b/provers/stark/src/examples/fibonacci_rap.rs index c2dbb6738c..b960ae97c0 100644 --- a/provers/stark/src/examples/fibonacci_rap.rs +++ b/provers/stark/src/examples/fibonacci_rap.rs @@ -107,9 +107,9 @@ where 1 } - fn compute_transition( + fn compute_transition_prover( &self, - frame: &Frame, + frame: &Frame, _periodic_values: &[FieldElement], gamma: &Self::RAPChallenges, ) -> Vec> { @@ -118,18 +118,18 @@ where let second_step = frame.get_evaluation_step(1); let third_step = frame.get_evaluation_step(2); - let a0 = first_step.get_evaluation_element(0, 0); - let a1 = second_step.get_evaluation_element(0, 0); - let a2 = third_step.get_evaluation_element(0, 0); + let a0 = first_step.get_main_evaluation_element(0, 0); + let a1 = second_step.get_main_evaluation_element(0, 0); + let a2 = third_step.get_main_evaluation_element(0, 0); let mut constraints = vec![a2 - a1 - a0]; // Auxiliary constraints - let z_i = first_step.get_evaluation_element(0, 2); - let z_i_plus_one = second_step.get_evaluation_element(0, 2); + let z_i = first_step.get_aux_evaluation_element(0, 0); + let z_i_plus_one = second_step.get_aux_evaluation_element(0, 0); - let a_i = first_step.get_evaluation_element(0, 0); - let b_i = first_step.get_evaluation_element(0, 1); + let a_i = first_step.get_main_evaluation_element(0, 0); + let b_i = first_step.get_main_evaluation_element(0, 1); let eval = z_i_plus_one * (b_i + gamma) - z_i * (a_i + gamma); @@ -142,11 +142,11 @@ where _rap_challenges: &Self::RAPChallenges, ) -> BoundaryConstraints { // Main boundary constraints - let a0 = BoundaryConstraint::new_simple(0, FieldElement::::one()); - let a1 = BoundaryConstraint::new_simple(1, FieldElement::::one()); + let a0 = BoundaryConstraint::new_simple_main(0, FieldElement::::one()); + let a1 = BoundaryConstraint::new_simple_main(1, FieldElement::::one()); // Auxiliary boundary constraints - let a0_aux = BoundaryConstraint::new(2, 0, FieldElement::::one()); + let a0_aux = BoundaryConstraint::new_aux(0, 0, FieldElement::::one()); BoundaryConstraints::from_constraints(vec![a0, a1, a0_aux]) } @@ -166,6 +166,15 @@ where fn pub_inputs(&self) -> &Self::PublicInputs { &self.pub_inputs } + + fn compute_transition_verifier( + &self, + frame: &Frame, + periodic_values: &[FieldElement], + rap_challenges: &Self::RAPChallenges, + ) -> Vec> { + self.compute_transition_prover(frame, periodic_values, rap_challenges) + } } pub fn fibonacci_rap_trace( diff --git a/provers/stark/src/examples/quadratic_air.rs b/provers/stark/src/examples/quadratic_air.rs index 5dff7bd580..ef7c294099 100644 --- a/provers/stark/src/examples/quadratic_air.rs +++ b/provers/stark/src/examples/quadratic_air.rs @@ -73,17 +73,17 @@ where ) -> Self::RAPChallenges { } - fn compute_transition( + fn compute_transition_prover( &self, - frame: &Frame, + frame: &Frame, _periodic_values: &[FieldElement], _rap_challenges: &Self::RAPChallenges, ) -> Vec> { let first_step = frame.get_evaluation_step(0); let second_step = frame.get_evaluation_step(1); - let x = first_step.get_evaluation_element(0, 0); - let x_squared = second_step.get_evaluation_element(0, 0); + let x = first_step.get_main_evaluation_element(0, 0); + let x_squared = second_step.get_main_evaluation_element(0, 0); vec![x_squared - x * x] } @@ -96,7 +96,7 @@ where &self, _rap_challenges: &Self::RAPChallenges, ) -> BoundaryConstraints { - let a0 = BoundaryConstraint::new_simple(0, self.pub_inputs.a0.clone()); + let a0 = BoundaryConstraint::new_simple_main(0, self.pub_inputs.a0.clone()); BoundaryConstraints::from_constraints(vec![a0]) } @@ -116,6 +116,15 @@ where fn pub_inputs(&self) -> &Self::PublicInputs { &self.pub_inputs } + + fn compute_transition_verifier( + &self, + frame: &Frame, + periodic_values: &[FieldElement], + rap_challenges: &Self::RAPChallenges, + ) -> Vec> { + self.compute_transition_prover(frame, periodic_values, rap_challenges) + } } pub fn quadratic_trace( diff --git a/provers/stark/src/examples/simple_fibonacci.rs b/provers/stark/src/examples/simple_fibonacci.rs index eb6bcde6a8..b255ee3185 100644 --- a/provers/stark/src/examples/simple_fibonacci.rs +++ b/provers/stark/src/examples/simple_fibonacci.rs @@ -78,9 +78,9 @@ where ) -> Self::RAPChallenges { } - fn compute_transition( + fn compute_transition_prover( &self, - frame: &Frame, + frame: &Frame, _periodic_values: &[FieldElement], _rap_challenges: &Self::RAPChallenges, ) -> Vec> { @@ -88,9 +88,9 @@ where let second_step = frame.get_evaluation_step(1); let third_step = frame.get_evaluation_step(2); - let a0 = first_step.get_evaluation_element(0, 0); - let a1 = second_step.get_evaluation_element(0, 0); - let a2 = third_step.get_evaluation_element(0, 0); + let a0 = first_step.get_main_evaluation_element(0, 0); + let a1 = second_step.get_main_evaluation_element(0, 0); + let a2 = third_step.get_main_evaluation_element(0, 0); vec![a2 - a1 - a0] } @@ -99,8 +99,8 @@ where &self, _rap_challenges: &Self::RAPChallenges, ) -> BoundaryConstraints { - let a0 = BoundaryConstraint::new_simple(0, self.pub_inputs.a0.clone()); - let a1 = BoundaryConstraint::new_simple(1, self.pub_inputs.a1.clone()); + let a0 = BoundaryConstraint::new_simple_main(0, self.pub_inputs.a0.clone()); + let a1 = BoundaryConstraint::new_simple_main(1, self.pub_inputs.a1.clone()); BoundaryConstraints::from_constraints(vec![a0, a1]) } @@ -120,6 +120,15 @@ where fn pub_inputs(&self) -> &Self::PublicInputs { &self.pub_inputs } + + fn compute_transition_verifier( + &self, + frame: &Frame, + periodic_values: &[FieldElement], + rap_challenges: &Self::RAPChallenges, + ) -> Vec> { + self.compute_transition_prover(frame, periodic_values, rap_challenges) + } } pub fn fibonacci_trace( diff --git a/provers/stark/src/examples/simple_periodic_cols.rs b/provers/stark/src/examples/simple_periodic_cols.rs index 5db81a6f10..3ca7b68b2a 100644 --- a/provers/stark/src/examples/simple_periodic_cols.rs +++ b/provers/stark/src/examples/simple_periodic_cols.rs @@ -92,9 +92,9 @@ where ) -> Self::RAPChallenges { } - fn compute_transition( + fn compute_transition_prover( &self, - frame: &Frame, + frame: &Frame, periodic_values: &[FieldElement], _rap_challenges: &Self::RAPChallenges, ) -> Vec> { @@ -102,9 +102,9 @@ where let second_step = frame.get_evaluation_step(1); let third_step = frame.get_evaluation_step(2); - let a0 = first_step.get_evaluation_element(0, 0); - let a1 = second_step.get_evaluation_element(0, 0); - let a2 = third_step.get_evaluation_element(0, 0); + let a0 = first_step.get_main_evaluation_element(0, 0); + let a1 = second_step.get_main_evaluation_element(0, 0); + let a2 = third_step.get_main_evaluation_element(0, 0); let s = &periodic_values[0]; @@ -115,9 +115,11 @@ where &self, _rap_challenges: &Self::RAPChallenges, ) -> BoundaryConstraints { - let a0 = BoundaryConstraint::new_simple(0, self.pub_inputs.a0.clone()); - let a1 = - BoundaryConstraint::new_simple(self.trace_length() - 1, self.pub_inputs.a1.clone()); + let a0 = BoundaryConstraint::new_simple_main(0, self.pub_inputs.a0.clone()); + let a1 = BoundaryConstraint::new_simple_main( + self.trace_length() - 1, + self.pub_inputs.a1.clone(), + ); BoundaryConstraints::from_constraints(vec![a0, a1]) } @@ -141,6 +143,15 @@ where fn pub_inputs(&self) -> &Self::PublicInputs { &self.pub_inputs } + + fn compute_transition_verifier( + &self, + frame: &Frame, + periodic_values: &[FieldElement], + rap_challenges: &Self::RAPChallenges, + ) -> Vec> { + self.compute_transition_prover(frame, periodic_values, rap_challenges) + } } pub fn simple_periodic_trace(trace_length: usize) -> TraceTable { diff --git a/provers/stark/src/frame.rs b/provers/stark/src/frame.rs index cca79d0441..51d24e3950 100644 --- a/provers/stark/src/frame.rs +++ b/provers/stark/src/frame.rs @@ -1,41 +1,50 @@ -use super::trace::TraceTable; -use crate::trace::StepView; -use lambdaworks_math::field::traits::IsField; +use crate::{table::EvaluationTable, trace::StepView}; +use lambdaworks_math::field::traits::{IsField, IsSubFieldOf}; /// A frame represents a collection of trace steps. /// The collected steps are all the necessary steps for /// all transition costraints over a trace to be evaluated. #[derive(Clone, Debug, PartialEq)] -pub struct Frame<'t, F: IsField> { - steps: Vec>, +pub struct Frame<'t, F: IsSubFieldOf, E: IsField> { + steps: Vec>, } -impl<'t, F: IsField> Frame<'t, F> { - pub fn new(steps: Vec>) -> Self { +impl<'t, F: IsSubFieldOf, E: IsField> Frame<'t, F, E> { + pub fn new(steps: Vec>) -> Self { Self { steps } } - pub fn get_evaluation_step(&self, step: usize) -> &StepView { + pub fn get_evaluation_step(&self, step: usize) -> &StepView { &self.steps[step] } - pub fn read_from_trace( - trace: &'t TraceTable, + pub fn read_from_lde_table( + lde_table: &'t EvaluationTable, step: usize, blowup: u8, offsets: &[usize], ) -> Self { // Get trace length to apply module with it when getting elements of // the frame from the trace. - let trace_steps = trace.num_steps(); + let trace_steps = lde_table.num_steps(); let steps = offsets .iter() .map(|eval_offset| { - trace.step_view((step + (eval_offset * blowup as usize)) % trace_steps) + lde_table.step_view((step + (eval_offset * blowup as usize)) % trace_steps) }) .collect(); Self::new(steps) } } + +impl<'t, E: IsField> Frame<'t, E, E> { + pub fn read_from_ood_table(ood_table: &'t EvaluationTable, offsets: &[usize]) -> Self { + let steps: Vec<_> = offsets + .iter() + .map(|offset| ood_table.step_view(*offset)) + .collect(); + Self::new(steps) + } +} diff --git a/provers/stark/src/proof/stark.rs b/provers/stark/src/proof/stark.rs index cabb193232..893643537c 100644 --- a/provers/stark/src/proof/stark.rs +++ b/provers/stark/src/proof/stark.rs @@ -14,7 +14,7 @@ use crate::{ config::Commitment, domain::Domain, fri::fri_decommit::FriDecommitment, - table::Table, + table::EvaluationTable, traits::AIR, transcript::StoneProverTranscript, verifier::{IsStarkVerifier, Verifier}, @@ -50,7 +50,7 @@ pub struct StarkProof, E: IsField> { // [tβ±Ό] pub lde_trace_aux_merkle_root: Option, // tβ±Ό(zgᡏ) - pub trace_ood_evaluations: Table, + pub trace_ood_evaluations: EvaluationTable, // Commitments to Hα΅’ pub composition_poly_root: Commitment, // Hα΅’(z^N) @@ -131,9 +131,10 @@ impl StoneCompatibleSerializer { proof: &StarkProof, output: &mut Vec, ) { - for i in 0..proof.trace_ood_evaluations.width { - for j in 0..proof.trace_ood_evaluations.height { - output.extend_from_slice(&proof.trace_ood_evaluations.get_row(j)[i].serialize()); + for i in 0..proof.trace_ood_evaluations.n_cols() { + for j in 0..proof.trace_ood_evaluations.n_rows() { + output + .extend_from_slice(&proof.trace_ood_evaluations.get_row_main(j)[i].serialize()); } } diff --git a/provers/stark/src/prover.rs b/provers/stark/src/prover.rs index 7c68c01f75..77ee87d0f9 100644 --- a/provers/stark/src/prover.rs +++ b/provers/stark/src/prover.rs @@ -20,7 +20,7 @@ use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelI use crate::debug::validate_trace; use crate::fri; use crate::proof::stark::{DeepPolynomialOpenings, PolynomialOpenings}; -use crate::table::Table; +use crate::table::{EvaluationTable, Table}; use crate::transcript::IsStarkTranscript; use super::config::{BatchedMerkleTree, Commitment}; @@ -50,7 +50,6 @@ where FieldElement: Serializable + Send + Sync, { pub(crate) trace_polys: Vec>>, - pub(crate) lde_trace: TraceTable, pub(crate) lde_trace_merkle_tree: BatchedMerkleTree, pub(crate) lde_trace_merkle_root: Commitment, } @@ -61,6 +60,7 @@ where FieldElement: Serializable + Sync + Send, FieldElement: Serializable + Sync + Send, { + pub(crate) lde_table: EvaluationTable, pub(crate) main: Round1CommitmentData, pub(crate) aux: Option>, pub(crate) rap_challenges: A::RAPChallenges, @@ -86,23 +86,6 @@ where } trace_polys } - - fn all_evaluations(&self) -> TraceTable { - let mut evaluations: Vec>> = self - .main - .lde_trace - .columns() - .clone() - .into_iter() - .map(|col| col.into_iter().map(|x| x.to_extension()).collect()) - .collect(); - - if let Some(aux) = &self.aux { - evaluations.extend_from_slice(&aux.lde_trace.columns()) - } - - TraceTable::from_columns(evaluations, A::STEP_SIZE) - } } pub struct Round2 where @@ -116,7 +99,7 @@ where } pub struct Round3 { - trace_ood_evaluations: Vec>>, + trace_ood_evaluations: EvaluationTable, composition_poly_parts_ood_evaluation: Vec>, } @@ -246,7 +229,6 @@ pub trait IsStarkProver { let main = Round1CommitmentData:: { trace_polys, - lde_trace: TraceTable::from_columns(evaluations, A::STEP_SIZE), lde_trace_merkle_tree: main_merkle_tree, lde_trace_merkle_root: main_merkle_root, }; @@ -254,21 +236,23 @@ pub trait IsStarkProver { let rap_challenges = air.build_rap_challenges(transcript); let aux_trace = air.build_auxiliary_trace(main_trace, &rap_challenges); - let aux = if !aux_trace.is_empty() { - // Check that this is valid for interpolation + let (aux, aux_evaluations) = if !aux_trace.is_empty() { let (aux_trace_polys, aux_trace_polys_evaluations, aux_merkle_tree, aux_merkle_root) = Self::interpolate_and_commit(&aux_trace, domain, transcript); - Some(Round1CommitmentData:: { + let aux_evaluations = aux_trace_polys_evaluations; + let aux = Some(Round1CommitmentData:: { trace_polys: aux_trace_polys, - lde_trace: TraceTable::from_columns(aux_trace_polys_evaluations, A::STEP_SIZE), lde_trace_merkle_tree: aux_merkle_tree, lde_trace_merkle_root: aux_merkle_root, - }) + }); + (aux, aux_evaluations) } else { - None + (None, Vec::new()) }; + let lde_table = EvaluationTable::from_columns(evaluations, aux_evaluations, A::STEP_SIZE); Ok(Round1 { + lde_table, main, aux, rap_challenges, @@ -317,19 +301,18 @@ pub trait IsStarkProver { FieldElement: Serializable + Send + Sync, FieldElement: Serializable + Send + Sync, { - // Create evaluation table + // Compute the evaluations of the composition polynomial on the LDE domain. let evaluator = ConstraintEvaluator::new(air, &round_1_result.rap_challenges); - let constraint_evaluations = evaluator.evaluate( air, - &round_1_result.all_evaluations(), + &round_1_result.lde_table, domain, transition_coefficients, boundary_coefficients, &round_1_result.rap_challenges, ); - // Get the composition poly H + // Get coefficients of the composition poly H let composition_poly = Polynomial::interpolate_offset_fft(&constraint_evaluations, &domain.coset_offset) .unwrap(); @@ -391,10 +374,16 @@ pub trait IsStarkProver { // polynomial and `g` is the primitive root of unity used when interpolating `t`. let trace_ood_evaluations = crate::trace::get_trace_evaluations( - &round_1_result.all_trace_polys(), + &round_1_result.main.trace_polys, + round_1_result + .aux + .as_ref() + .map(|aux| &aux.trace_polys) + .unwrap_or(&vec![]), z, &air.context().transition_offsets, &domain.trace_primitive_root, + A::STEP_SIZE, ); Round3 { @@ -542,7 +531,7 @@ pub trait IsStarkProver { // βˆ‘ β±Όβ‚– [ 𝛾ₖ ( tβ±Ό βˆ’ tβ±Ό(z) ) / ( X βˆ’ zgᡏ )] // @@@ this could be const - let trace_frame_length = trace_frame_evaluations.len(); + let trace_frame_length = trace_frame_evaluations.n_rows(); #[cfg(feature = "parallel")] let trace_terms = trace_polys @@ -556,7 +545,7 @@ pub trait IsStarkProver { (i, t_j), trace_frame_length, trace_terms_gammas, - trace_frame_evaluations, + &trace_frame_evaluations.columns(), transition_offsets, (z, primitive_root), ) @@ -575,7 +564,7 @@ pub trait IsStarkProver { (i, t_j), trace_frame_length, trace_terms_gammas, - trace_frame_evaluations, + &trace_frame_evaluations.columns(), transition_offsets, (z, primitive_root), ) @@ -601,15 +590,13 @@ pub trait IsStarkProver { let iter_trace_gammas = trace_terms_gammas .iter() .skip(i_times_trace_frame_evaluation); - let trace_int = trace_frame_evaluations + let trace_int = trace_frame_evaluations[i] .iter() .zip(transition_offsets) .zip(iter_trace_gammas) .fold( Polynomial::zero(), - |trace_agg, ((eval, offset), trace_gamma)| { - // @@@ we can avoid this clone - let t_j_z = &eval[i]; + |trace_agg, ((t_j_z, offset), trace_gamma)| { // @@@ this can be pre-computed let z_shifted = primitive_root.pow(*offset) * z; let mut poly = t_j - t_j_z; @@ -663,7 +650,7 @@ pub trait IsStarkProver { fn open_trace_polys( domain: &Domain, tree: &BatchedMerkleTree, - lde_trace: &TraceTable, + lde_trace: &Table, challenge: usize, ) -> PolynomialOpenings where @@ -706,7 +693,7 @@ pub trait IsStarkProver { let main_trace_opening = Self::open_trace_polys::( domain, &round_1_result.main.lde_trace_merkle_tree, - &round_1_result.main.lde_trace, + &round_1_result.lde_table.main_table, *index, ); @@ -720,7 +707,7 @@ pub trait IsStarkProver { Self::open_trace_polys::( domain, &aux.lde_trace_merkle_tree, - &aux.lde_trace, + &round_1_result.lde_table.aux_table, *index, ) }); @@ -781,7 +768,12 @@ pub trait IsStarkProver { #[cfg(debug_assertions)] validate_trace( &air, - &round_1_result.all_trace_polys(), + &round_1_result.main.trace_polys, + round_1_result + .aux + .as_ref() + .map(|a| &a.trace_polys) + .unwrap_or(&vec![]), &domain, &round_1_result.rap_challenges, ); @@ -858,9 +850,10 @@ pub trait IsStarkProver { ); // >>>> Send values: tβ±Ό(zgᡏ) - for i in 0..round_3_result.trace_ood_evaluations[0].len() { - for j in 0..round_3_result.trace_ood_evaluations.len() { - transcript.append_field_element(&round_3_result.trace_ood_evaluations[j][i]); + let trace_ood_evaluations_columns = round_3_result.trace_ood_evaluations.columns(); + for col in trace_ood_evaluations_columns.iter() { + for elem in col.iter() { + transcript.append_field_element(elem); } } @@ -916,22 +909,13 @@ pub trait IsStarkProver { info!("End proof generation"); - let trace_ood_evaluations = Table::new( - round_3_result - .trace_ood_evaluations - .into_iter() - .flatten() - .collect(), - air.context().trace_columns, - ); - Ok(StarkProof:: { // [tβ±Ό] lde_trace_main_merkle_root: round_1_result.main.lde_trace_merkle_root, // [tβ±Ό] lde_trace_aux_merkle_root: round_1_result.aux.map(|x| x.lde_trace_merkle_root), // tβ±Ό(zgᡏ) - trace_ood_evaluations, + trace_ood_evaluations: round_3_result.trace_ood_evaluations, // [H₁] and [Hβ‚‚] composition_poly_root: round_2_result.composition_poly_root, // Hα΅’(z^N) @@ -1193,25 +1177,25 @@ mod tests { let proof = stone_compatibility_case_1_proof(); assert_eq!( - proof.trace_ood_evaluations.get_row(0)[0], + proof.trace_ood_evaluations.get_row_main(0)[0], FieldElement::from_hex_unchecked( "70d8181785336cc7e0a0a1078a79ee6541ca0803ed3ff716de5a13c41684037", ) ); assert_eq!( - proof.trace_ood_evaluations.get_row(1)[0], + proof.trace_ood_evaluations.get_row_main(1)[0], FieldElement::from_hex_unchecked( "29808fc8b7480a69295e4b61600480ae574ca55f8d118100940501b789c1630", ) ); assert_eq!( - proof.trace_ood_evaluations.get_row(0)[1], + proof.trace_ood_evaluations.get_row_main(0)[1], FieldElement::from_hex_unchecked( "7d8110f21d1543324cc5e472ab82037eaad785707f8cae3d64c5b9034f0abd2", ) ); assert_eq!( - proof.trace_ood_evaluations.get_row(1)[1], + proof.trace_ood_evaluations.get_row_main(1)[1], FieldElement::from_hex_unchecked( "1b58470130218c122f71399bf1e04cf75a6e8556c4751629d5ce8c02cc4e62d", ) diff --git a/provers/stark/src/table.rs b/provers/stark/src/table.rs index ab9a102bcc..87d3febc0f 100644 --- a/provers/stark/src/table.rs +++ b/provers/stark/src/table.rs @@ -1,6 +1,9 @@ -use lambdaworks_math::field::{element::FieldElement, traits::IsField}; +use lambdaworks_math::field::{ + element::FieldElement, + traits::{IsField, IsSubFieldOf}, +}; -use crate::{frame::Frame, trace::StepView}; +use crate::trace::StepView; /// A two-dimensional Table holding field elements, arranged in a row-major order. /// This is the basic underlying data structure used for any two-dimensional component in the @@ -14,6 +17,25 @@ pub struct Table { pub height: usize, } +/// A view of a contiguos subset of rows of a table. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TableView<'t, F: IsField> { + pub data: &'t [FieldElement], + pub table_row_idx: usize, + pub width: usize, + pub height: usize, +} + +/// A pair of tables corresponding to evaluations of the main and auxiliary traces. +/// It supports main and auxiliary tables taking values in different fields. +/// Both tables must have the same number of rows. +#[derive(Clone, Default, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct EvaluationTable, E: IsField> { + pub(crate) main_table: Table, + pub(crate) aux_table: Table, + pub(crate) step_size: usize, +} + impl<'t, F: IsField> Table { /// Crates a new Table instance from a one-dimensional array in row major order /// and the intended width of the table. @@ -122,30 +144,117 @@ impl<'t, F: IsField> Table { let idx = row * self.width + col; &self.data[idx] } +} - /// Given a step size, converts the given table into a `Frame`. - pub fn into_frame(&'t self, step_size: usize) -> Frame<'t, F> { - debug_assert!(self.height % step_size == 0); - let steps = (0..self.height) - .step_by(step_size) - .enumerate() - .map(|(step_idx, row_idx)| { - let table_view = self.table_view(row_idx, step_size); - StepView::new(table_view, step_idx) - }) - .collect(); +impl EvaluationTable { + /// Returns a single vector of elements with the concatenation of the corresponding rows + /// in the main and auxiliary tables. + pub fn get_row(&self, row_idx: usize) -> Vec> { + let mut row: Vec<_> = self.get_row_main(row_idx).to_vec(); + row.extend_from_slice(self.get_row_aux(row_idx)); + row + } - Frame::new(steps) + /// Returns the values of the tables as a single list of columns containing both main and + /// auxiliary tables. The first `self.n_main_cols()` are the columns of the main trace and the + /// rest are the auxiliary table columns. + pub fn columns(&self) -> Vec>> { + let mut columns = self.main_table.columns(); + let aux_columns = self.aux_table.columns(); + columns.extend(aux_columns); + columns } } -/// A view of a contiguos subset of rows of a table. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct TableView<'t, F: IsField> { - pub data: &'t [FieldElement], - pub table_row_idx: usize, - pub width: usize, - pub height: usize, +impl, E: IsField> EvaluationTable { + /// Creates an EvaluationTable instance from a vector of columns. + pub fn from_columns( + main_columns: Vec>>, + aux_columns: Vec>>, + step_size: usize, + ) -> Self { + let main_table = Table::from_columns(main_columns); + let aux_table = Table::from_columns(aux_columns); + debug_assert!(aux_table.height == 0 || (main_table.height == aux_table.height)); + Self { + main_table, + aux_table, + step_size, + } + } + + /// Returns the number of columns of the main table. + pub fn n_main_cols(&self) -> usize { + self.main_table.width + } + + /// Returns the number of columns of the auxiliary table. + pub fn n_aux_cols(&self) -> usize { + self.aux_table.width + } + + /// Returns the total number of columns in both tables. + pub fn n_cols(&self) -> usize { + self.n_main_cols() + self.n_aux_cols() + } + + /// Returns the total number of rows. + pub fn n_rows(&self) -> usize { + debug_assert!( + self.aux_table.height == 0 || (self.main_table.height == self.aux_table.height) + ); + self.main_table.height + } + + /// Returns the total number of steps. + pub fn num_steps(&self) -> usize { + debug_assert!((self.main_table.height % self.step_size) == 0); + debug_assert!( + self.aux_table.height == 0 || (self.main_table.height == self.aux_table.height) + ); + self.main_table.height / self.step_size + } + + /// Given a particular step of the computation represented on the trace, + /// returns the row of the underlying table. + pub fn step_to_row(&self, step: usize) -> usize { + self.step_size * step + } + + /// Given a step index, return the step view of the trace for that index + pub fn step_view(&self, step_idx: usize) -> StepView<'_, F, E> { + let row_idx = self.step_to_row(step_idx); + let main_table_view = self.main_table.table_view(row_idx, self.step_size); + let aux_table_view = self.aux_table.table_view(row_idx, self.step_size); + + StepView { + main_table_view, + aux_table_view, + step_idx, + } + } + + /// Given a row and a column index, gives stored value in that position + pub fn get_main(&self, row: usize, col: usize) -> &FieldElement { + self.main_table.get(row, col) + } + + /// Given a row and a column index, gives stored value in that position + pub fn get_aux(&self, row: usize, col: usize) -> &FieldElement { + self.aux_table.get(row, col) + } + + /// Given a row index, returns a reference to that row in the main table as a slice of field elements. + pub fn get_row_main(&self, row_idx: usize) -> &[FieldElement] { + let row_offset = row_idx * self.main_table.width; + &self.main_table.data[row_offset..row_offset + self.main_table.width] + } + + /// Given a row index, returns a reference to that row in the aux table as a slice of field elements. + pub fn get_row_aux(&self, row_idx: usize) -> &[FieldElement] { + let row_offset = row_idx * self.aux_table.width; + &self.aux_table.data[row_offset..row_offset + self.aux_table.width] + } } impl<'t, F: IsField> TableView<'t, F> { diff --git a/provers/stark/src/trace.rs b/provers/stark/src/trace.rs index a3cabafd00..e7ef297de4 100644 --- a/provers/stark/src/trace.rs +++ b/provers/stark/src/trace.rs @@ -1,4 +1,4 @@ -use crate::table::{Table, TableView}; +use crate::table::{EvaluationTable, Table, TableView}; use lambdaworks_math::fft::errors::FFTError; use lambdaworks_math::field::traits::{IsField, IsSubFieldOf}; use lambdaworks_math::{ @@ -21,7 +21,7 @@ pub struct TraceTable { pub step_size: usize, } -impl<'t, F: IsField> TraceTable { +impl TraceTable { pub fn new(data: Vec>, n_columns: usize, step_size: usize) -> Self { let table = Table::new(data, n_columns); Self { table, step_size } @@ -55,17 +55,6 @@ impl<'t, F: IsField> TraceTable { self.step_size * step } - /// Given a step index, return the step view of the trace for that index - pub fn step_view(&'t self, step_idx: usize) -> StepView<'t, F> { - let row_idx = self.step_to_row(step_idx); - let table_view = self.table.table_view(row_idx, self.step_size); - - StepView { - table_view, - step_idx, - } - } - pub fn n_cols(&self) -> usize { self.table.width } @@ -106,11 +95,6 @@ impl<'t, F: IsField> TraceTable { data } - /// Given a row and a column index, gives stored value in that position - pub fn get(&self, row: usize, col: usize) -> &FieldElement { - self.table.get(row, col) - } - pub fn compute_trace_polys>( &self, ) -> Vec>> @@ -170,26 +154,41 @@ impl<'t, F: IsField> TraceTable { /// access the steps in a trace, in order to grab elements to calculate /// constraint evaluations. #[derive(Debug, Clone, PartialEq)] -pub struct StepView<'t, F: IsField> { - pub table_view: TableView<'t, F>, +pub struct StepView<'t, F: IsSubFieldOf, E: IsField> { + pub main_table_view: TableView<'t, F>, + pub aux_table_view: TableView<'t, E>, pub step_idx: usize, } -impl<'t, F: IsField> StepView<'t, F> { - pub fn new(table_view: TableView<'t, F>, step_idx: usize) -> Self { +impl<'t, F: IsSubFieldOf, E: IsField> StepView<'t, F, E> { + pub fn new( + main_table_view: TableView<'t, F>, + aux_table_view: TableView<'t, E>, + step_idx: usize, + ) -> Self { StepView { - table_view, + main_table_view, + aux_table_view, step_idx, } } - /// Gets the evaluation element specified by `row_idx` and `col_idx` of this step - pub fn get_evaluation_element(&self, row_idx: usize, col_idx: usize) -> &FieldElement { - self.table_view.get(row_idx, col_idx) + /// Gets the evaluation element of the main table specified by `row_idx` and `col_idx` of this step + pub fn get_main_evaluation_element(&self, row_idx: usize, col_idx: usize) -> &FieldElement { + self.main_table_view.get(row_idx, col_idx) } - pub fn get_row(&self, row_idx: usize) -> &[FieldElement] { - self.table_view.get_row(row_idx) + /// Gets the evaluation element of the aux table specified by `row_idx` and `col_idx` of this step + pub fn get_aux_evaluation_element(&self, row_idx: usize, col_idx: usize) -> &FieldElement { + self.aux_table_view.get(row_idx, col_idx) + } + + pub fn get_row_main(&self, row_idx: usize) -> &[FieldElement] { + self.main_table_view.get_row(row_idx) + } + + pub fn get_row_aux(&self, row_idx: usize) -> &[FieldElement] { + self.aux_table_view.get_row(row_idx) } } @@ -199,22 +198,38 @@ impl<'t, F: IsField> StepView<'t, F> { /// compute a transition. /// Example: For a simple Fibonacci computation, if t(x) is the trace polynomial of /// the computation, this will output evaluations t(x), t(g * x), t(g^2 * z). -pub fn get_trace_evaluations>( - trace_polys: &[Polynomial>], +pub(crate) fn get_trace_evaluations, E: IsField>( + main_trace_polys: &[Polynomial>], + aux_trace_polys: &[Polynomial>], x: &FieldElement, frame_offsets: &[usize], primitive_root: &FieldElement, -) -> Vec>> { - frame_offsets + step_size: usize, +) -> EvaluationTable { + let evaluation_points: Vec<_> = frame_offsets .iter() .map(|offset| primitive_root.pow(*offset) * x) - .map(|eval_point| { - trace_polys + .collect(); + let main_evaluations = main_trace_polys + .iter() + .map(|poly| { + evaluation_points .iter() - .map(|poly| poly.evaluate(&eval_point)) - .collect::>>() + .map(|eval_point| poly.evaluate(eval_point)) + .collect() }) - .collect() + .collect(); + let aux_evaluations = aux_trace_polys + .iter() + .map(|poly| { + evaluation_points + .iter() + .map(|eval_point| poly.evaluate(eval_point)) + .collect() + }) + .collect(); + + EvaluationTable::from_columns(main_evaluations, aux_evaluations, step_size) } #[cfg(test)] diff --git a/provers/stark/src/traits.rs b/provers/stark/src/traits.rs index 7099dd9263..82efa64099 100644 --- a/provers/stark/src/traits.rs +++ b/provers/stark/src/traits.rs @@ -45,9 +45,25 @@ pub trait AIR { fn composition_poly_degree_bound(&self) -> usize; - fn compute_transition( + /// The method called by the prover to evaluate the transitions corresponding to an evaluation frame. + /// In the case of the prover, the main evaluation table of the frame takes values in + /// `Self::Field`, since they are the evaluations of the main trace at the LDE domain. + fn compute_transition_prover( &self, - frame: &Frame, + frame: &Frame, + periodic_values: &[FieldElement], + rap_challenges: &Self::RAPChallenges, + ) -> Vec>; + + /// The method called by the verifier to evaluate the transitions at the out of domain frame. + /// In the case of the verifier, both main and auxiliary tables of the evaluation frame take + /// values in `Self::FieldExtension`, since they are the evaluations of the trace polynomials + /// at the out of domain challenge. + /// In case `Self::Field` coincides with `Self::FieldExtension`, this method and + /// `compute_transition_prover` should return the same values. + fn compute_transition_verifier( + &self, + frame: &Frame, periodic_values: &[FieldElement], rap_challenges: &Self::RAPChallenges, ) -> Vec>; @@ -133,9 +149,7 @@ pub trait AIR { vec![] } - fn get_periodic_column_polynomials( - &self, - ) -> Vec>> { + fn get_periodic_column_polynomials(&self) -> Vec>> { let mut result = Vec::new(); for periodic_column in self.get_periodic_column_values() { let values: Vec<_> = periodic_column @@ -143,9 +157,10 @@ pub trait AIR { .cycle() .take(self.trace_length()) .cloned() - .map(|x| x.to_extension()) .collect(); - let poly = Polynomial::interpolate_fft::(&values).unwrap(); + let poly = + Polynomial::>::interpolate_fft::(&values) + .unwrap(); result.push(poly); } result diff --git a/provers/stark/src/verifier.rs b/provers/stark/src/verifier.rs index 7be6148ad1..6755664528 100644 --- a/provers/stark/src/verifier.rs +++ b/provers/stark/src/verifier.rs @@ -8,7 +8,8 @@ use lambdaworks_crypto::merkle_tree::proof::Proof; use log::error; use crate::{ - config::Commitment, proof::stark::DeepPolynomialOpening, transcript::IsStarkTranscript, + config::Commitment, frame::Frame, proof::stark::DeepPolynomialOpening, + transcript::IsStarkTranscript, }; use lambdaworks_math::{ fft::cpu::bit_reversing::reverse_index, @@ -118,9 +119,10 @@ pub trait IsStarkVerifier { ); // <<<< Receive values: tβ±Ό(zgᡏ) - for i in 0..proof.trace_ood_evaluations.width { - for j in 0..proof.trace_ood_evaluations.height { - transcript.append_field_element(&proof.trace_ood_evaluations.get_row(j)[i]); + let trace_ood_evaluations_columns = proof.trace_ood_evaluations.columns(); + for col in trace_ood_evaluations_columns.iter() { + for elem in col.iter() { + transcript.append_field_element(elem); } } // <<<< Receive value: Hα΅’(z^N) @@ -217,9 +219,14 @@ pub trait IsStarkVerifier { ) = (0..number_of_b_constraints) .map(|index| { let step = boundary_constraints.constraints[index].step; + let is_aux = boundary_constraints.constraints[index].is_aux; let point = &domain.trace_primitive_root.pow(step as u64); let trace_idx = boundary_constraints.constraints[index].col; - let trace_evaluation = &proof.trace_ood_evaluations.get_row(0)[trace_idx]; + let trace_evaluation = if is_aux { + &proof.trace_ood_evaluations.get_row_aux(0)[trace_idx] + } else { + &proof.trace_ood_evaluations.get_row_main(0)[trace_idx] + }; let boundary_zerofier_challenges_z_den = -point + &challenges.z; let boundary_quotient_ood_evaluation_num = @@ -250,8 +257,11 @@ pub trait IsStarkVerifier { .map(|poly| poly.evaluate(&challenges.z)) .collect::>>(); - let transition_ood_frame_evaluations = air.compute_transition( - &(proof.trace_ood_evaluations).into_frame(A::STEP_SIZE), + let transition_ood_frame_evaluations = air.compute_transition_verifier( + &Frame::read_from_ood_table( + &proof.trace_ood_evaluations, + &air.context().transition_offsets, + ), &periodic_values, &challenges.rap_challenges, ); @@ -642,23 +652,22 @@ pub trait IsStarkVerifier { lde_trace_evaluations: &[FieldElement], lde_composition_poly_parts_evaluation: &[FieldElement], ) -> FieldElement { - let mut denoms_trace = (0..proof.trace_ood_evaluations.height) + let mut denoms_trace = (0..proof.trace_ood_evaluations.n_rows()) .map(|row_idx| evaluation_point - primitive_root.pow(row_idx as u64) * &challenges.z) .collect::>>(); FieldElement::inplace_batch_inverse(&mut denoms_trace).unwrap(); - let trace_term = (0..proof.trace_ood_evaluations.width) + let trace_term = (0..proof.trace_ood_evaluations.n_cols()) .zip(&challenges.trace_term_coeffs) .fold(FieldElement::zero(), |trace_terms, (col_idx, coeff_row)| { - let trace_i = (0..proof.trace_ood_evaluations.height).zip(coeff_row).fold( - FieldElement::zero(), - |trace_t, (row_idx, coeff)| { + let trace_i = (0..proof.trace_ood_evaluations.n_rows()) + .zip(coeff_row) + .fold(FieldElement::zero(), |trace_t, (row_idx, coeff)| { let poly_evaluation = (lde_trace_evaluations[col_idx].clone() - proof.trace_ood_evaluations.get_row(row_idx)[col_idx].clone()) * &denoms_trace[row_idx]; trace_t + &poly_evaluation * coeff - }, - ); + }); trace_terms + trace_i }); diff --git a/winterfell_adapter/src/adapter/air.rs b/winterfell_adapter/src/adapter/air.rs index d539ed84df..256deedf8f 100644 --- a/winterfell_adapter/src/adapter/air.rs +++ b/winterfell_adapter/src/adapter/air.rs @@ -190,26 +190,23 @@ where * self.trace_length() } - fn compute_transition( + fn compute_transition_prover( &self, - frame: &stark_platinum_prover::frame::Frame, - periodic_values: &[FieldElement], + frame: &stark_platinum_prover::frame::Frame, + periodic_values: &[FieldElement], rap_challenges: &Self::RAPChallenges, ) -> Vec> { - let num_aux_columns = self.number_auxiliary_rap_columns(); - let num_main_columns = self.context().trace_columns - num_aux_columns; - let first_step = frame.get_evaluation_step(0); let second_step = frame.get_evaluation_step(1); let main_frame = EvaluationFrame::from_rows( - vec_lambda2winter(&first_step.get_row(0)[..num_main_columns]), - vec_lambda2winter(&second_step.get_row(0)[..num_main_columns]), + vec_lambda2winter(first_step.get_row_main(0)), + vec_lambda2winter(second_step.get_row_main(0)), ); let periodic_values = vec_lambda2winter(periodic_values); - let mut main_result = vec![ + let main_result = vec![ FieldElement::zero(); self.winterfell_air .context() @@ -217,13 +214,16 @@ where ]; let mut main_result_winter = vec_lambda2winter(&main_result); - self.winterfell_air.evaluate_transition::( + self.winterfell_air.evaluate_transition::( &main_frame, &periodic_values, &mut main_result_winter, - ); // Periodic values not supported + ); - main_result = vec_winter2lambda(&main_result_winter); + let mut result: Vec<_> = vec_winter2lambda(&main_result_winter) + .into_iter() + .map(|element| element.to_extension()) + .collect(); if self.winterfell_air.trace_layout().num_aux_segments() == 1 { let mut rand_elements = AuxTraceRandElements::new(); @@ -233,8 +233,8 @@ where let second_step = frame.get_evaluation_step(1); let aux_frame = EvaluationFrame::from_rows( - vec_lambda2winter(&first_step.get_row(0)[num_main_columns..]), - vec_lambda2winter(&second_step.get_row(0)[num_main_columns..]), + vec_lambda2winter(first_step.get_row_aux(0)), + vec_lambda2winter(second_step.get_row_aux(0)), ); let mut aux_result = vec![ @@ -252,22 +252,19 @@ where &mut winter_aux_result, ); aux_result = vec_winter2lambda(&winter_aux_result); - main_result.extend_from_slice(&aux_result); + result.extend_from_slice(&aux_result); } - main_result + result } fn boundary_constraints( &self, rap_challenges: &Self::RAPChallenges, ) -> stark_platinum_prover::constraints::boundary::BoundaryConstraints { - let num_aux_columns = self.number_auxiliary_rap_columns(); - let num_main_columns = self.context().trace_columns - num_aux_columns; - let mut result = Vec::new(); for assertion in self.winterfell_air.get_assertions() { assert!(assertion.is_single()); - result.push(BoundaryConstraint::::new( + result.push(BoundaryConstraint::new_main( assertion.column(), assertion.first_step(), FieldElement::::const_from_raw(assertion.values()[0]).to_extension(), @@ -279,8 +276,8 @@ where for assertion in self.winterfell_air.get_aux_assertions(&rand_elements) { assert!(assertion.is_single()); - result.push(BoundaryConstraint::new( - assertion.column() + num_main_columns, + result.push(BoundaryConstraint::new_aux( + assertion.column(), assertion.first_step(), FieldElement::::const_from_raw(assertion.values()[0]), )); @@ -304,4 +301,68 @@ where fn get_periodic_column_values(&self) -> Vec>> { matrix_winter2lambda(&self.winterfell_air.get_periodic_column_values()) } + + fn compute_transition_verifier( + &self, + frame: &stark_platinum_prover::frame::Frame, + periodic_values: &[FieldElement], + rap_challenges: &Self::RAPChallenges, + ) -> Vec> { + let first_step = frame.get_evaluation_step(0); + let second_step = frame.get_evaluation_step(1); + + let main_frame = EvaluationFrame::from_rows( + vec_lambda2winter(first_step.get_row_main(0)), + vec_lambda2winter(second_step.get_row_main(0)), + ); + + let periodic_values = vec_lambda2winter(periodic_values); + + let main_result = vec![ + FieldElement::zero(); + self.winterfell_air + .context() + .num_main_transition_constraints() + ]; + + let mut main_result_winter = vec_lambda2winter(&main_result); + self.winterfell_air.evaluate_transition::( + &main_frame, + &periodic_values, + &mut main_result_winter, + ); + + let mut result: Vec> = vec_winter2lambda(&main_result_winter); + + if self.winterfell_air.trace_layout().num_aux_segments() == 1 { + let mut rand_elements = AuxTraceRandElements::new(); + rand_elements.add_segment_elements(rap_challenges.clone()); + + let first_step = frame.get_evaluation_step(0); + let second_step = frame.get_evaluation_step(1); + + let aux_frame = EvaluationFrame::from_rows( + vec_lambda2winter(first_step.get_row_aux(0)), + vec_lambda2winter(second_step.get_row_aux(0)), + ); + + let mut aux_result = vec![ + FieldElement::zero(); + self.winterfell_air + .context() + .num_aux_transition_constraints() + ]; + let mut winter_aux_result = vec_lambda2winter(&aux_result); + self.winterfell_air.evaluate_aux_transition( + &main_frame, + &aux_frame, + &periodic_values, + &rand_elements, + &mut winter_aux_result, + ); + aux_result = vec_winter2lambda(&winter_aux_result); + result.extend_from_slice(&aux_result); + } + result + } } From f96448c4fdd42344406ca2e4c6c5f2a063f058ed Mon Sep 17 00:00:00 2001 From: Mauro Toscano <12560266+MauroToscano@users.noreply.github.com> Date: Wed, 27 Dec 2023 16:39:29 -0300 Subject: [PATCH 23/29] Update codeowners (#733) --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 15f949544c..d227113e2c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @lambdaclass/zk_research_and_development @schouhy @ajgara +* @lambdaclass/zk_research_and_development From 7b41a9998d79593286ccd32bca6a8b80fd8ec95d Mon Sep 17 00:00:00 2001 From: fmoletta <99273364+fmoletta@users.noreply.github.com> Date: Tue, 2 Jan 2024 22:54:06 +0200 Subject: [PATCH 24/29] Make fn `ShortWeierstrassProjectivePoint::double` public (#737) * Make `double` fn public * Apply clippy suggestion --------- Co-authored-by: Mariano Nicolini --- math/src/elliptic_curve/short_weierstrass/point.rs | 2 +- provers/stark/src/fri/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/math/src/elliptic_curve/short_weierstrass/point.rs b/math/src/elliptic_curve/short_weierstrass/point.rs index 3881909946..d4adf0f922 100644 --- a/math/src/elliptic_curve/short_weierstrass/point.rs +++ b/math/src/elliptic_curve/short_weierstrass/point.rs @@ -50,7 +50,7 @@ impl ShortWeierstrassProjectivePoint { Self(self.0.to_affine()) } - fn double(&self) -> Self { + pub fn double(&self) -> Self { let [px, py, pz] = self.coordinates(); let px_square = px * px; diff --git a/provers/stark/src/fri/mod.rs b/provers/stark/src/fri/mod.rs index 548adc836d..ffb8b1ff38 100644 --- a/provers/stark/src/fri/mod.rs +++ b/provers/stark/src/fri/mod.rs @@ -64,7 +64,7 @@ where let last_value = last_poly .coefficients() - .get(0) + .first() .unwrap_or(&FieldElement::zero()) .clone(); From 7aa42400c3579385b274cd76ef13dca9c7550fc4 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> Date: Tue, 2 Jan 2024 17:56:54 -0300 Subject: [PATCH 25/29] Stark: Add Prover and Verifier docs (#732) * wip * cast to fieldextension * fmt * clippy * fmt * fix parallel * prover with generic air argument * verifier with generic argument * clippy, fmt * wip, prover refactor * wip * fix stone serialization * add safe scope for unwraps * minor refactor * clippy, fmt * remove commented code. Fix typo in docs * fix compilation error * fmt * fix parallel * remove unnecessary trait bound * fix wasm * fix wasm * change wasm proof bytes * fix number of bytes * fix wasm proof * fix number of bytes * wip * wip, compiles * wip * wip. valid proofs * fix trace ood element send order * fix adapter * clippy * clippy * clippy * fmt * remove unnecessary methods * rename * minor refactor. Avoid clone * minor refactor * add docstrings * fix parallel * add docstrings * clippy * fix test * fix debug assert * add prover docs * add docs for transcript * add verifier docs * minor comments on fields * Apply clippy suggestion --------- Co-authored-by: Mariano Nicolini --- math/src/field/fields/winterfell.rs | 3 + math/src/field/traits.rs | 1 + provers/stark/src/prover.rs | 91 ++++++++++++++++++++++++----- provers/stark/src/transcript.rs | 14 +++++ provers/stark/src/verifier.rs | 31 +++++++++- 5 files changed, 125 insertions(+), 15 deletions(-) diff --git a/math/src/field/fields/winterfell.rs b/math/src/field/fields/winterfell.rs index b9ea51cf51..5c98cc9dd8 100644 --- a/math/src/field/fields/winterfell.rs +++ b/math/src/field/fields/winterfell.rs @@ -15,6 +15,9 @@ use miden_core::QuadExtension; pub use winter_math::fields::f128::BaseElement; use winter_math::{ExtensionOf, FieldElement as IsWinterfellFieldElement, StarkField}; +// Implementation of Lambdaworks' different field traits for Miden's base field element `Felt` and +// its quadratic extension `QuadFelt`. + impl IsFFTField for Felt { const TWO_ADICITY: u64 = ::TWO_ADICITY as u64; const TWO_ADIC_PRIMITVE_ROOT_OF_UNITY: Self::BaseType = Felt::TWO_ADIC_ROOT_OF_UNITY; diff --git a/math/src/field/traits.rs b/math/src/field/traits.rs index 67df42e8d4..fb63cd10d5 100644 --- a/math/src/field/traits.rs +++ b/math/src/field/traits.rs @@ -14,6 +14,7 @@ pub enum RootsConfig { BitReverseInversed, // same as above but exponents are negated. } +/// Represents the subfield relation between two fields. pub trait IsSubFieldOf: IsField { fn mul(a: &Self::BaseType, b: &F::BaseType) -> F::BaseType; fn add(a: &Self::BaseType, b: &F::BaseType) -> F::BaseType; diff --git a/provers/stark/src/prover.rs b/provers/stark/src/prover.rs index 77ee87d0f9..93ff6d359c 100644 --- a/provers/stark/src/prover.rs +++ b/provers/stark/src/prover.rs @@ -33,6 +33,7 @@ use super::proof::stark::{DeepPolynomialOpening, StarkProof}; use super::trace::TraceTable; use super::traits::AIR; +/// A default STARK prover implementing `IsStarkProver`. pub struct Prover { phantom: PhantomData, } @@ -44,25 +45,35 @@ pub enum ProvingError { WrongParameter(String), } +/// A container for the intermediate results of the commitments to a trace table, main or auxiliary in case of RAP, +/// in the first round of the STARK Prove protocol. pub struct Round1CommitmentData where F: IsField, FieldElement: Serializable + Send + Sync, { + /// The result of the interpolation of the columns of the trace table. pub(crate) trace_polys: Vec>>, + /// The Merkle trees constructed to obtain the commitment of the entire trace table. pub(crate) lde_trace_merkle_tree: BatchedMerkleTree, + /// The root of the Merkle tree in `lde_trace_merkle_tree`. pub(crate) lde_trace_merkle_root: Commitment, } +/// A container for the results of the first round of the STARK Prove protocol. pub struct Round1 where A: AIR, FieldElement: Serializable + Sync + Send, FieldElement: Serializable + Sync + Send, { + /// The table of evaluations over the LDE of the main and auxiliary trace tables. pub(crate) lde_table: EvaluationTable, + /// The intermediate results of the commitment to the main trace table. pub(crate) main: Round1CommitmentData, + /// The intermediate results of the commitment to the auxiliary trace table in case of RAP. pub(crate) aux: Option>, + /// The challenges of the RAP round. pub(crate) rap_challenges: A::RAPChallenges, } @@ -72,6 +83,9 @@ where FieldElement: Serializable + Sync + Send, FieldElement: Serializable + Sync + Send, { + /// Returns the full list of the polynomials interpolating the trace. It includes both + /// main and auxiliary trace polynomials. The main trace polynomials are casted to + /// polynomials with coefficients over `Self::FieldExtension`. fn all_trace_polys(&self) -> Vec>> { let mut trace_polys: Vec<_> = self .main @@ -87,29 +101,50 @@ where trace_polys } } + +/// A container for the results of the second round of the STARK Prove protocol. pub struct Round2 where F: IsField, FieldElement: Serializable + Sync + Send, { + /// The list of polynomials `Hβ‚€, ..., Hβ‚™` such that `H = βˆ‘α΅’XⁱH(Xⁿ)`, where H is the composition polynomial. pub(crate) composition_poly_parts: Vec>>, + /// Evaluations of the composition polynomial parts over the LDE domain. pub(crate) lde_composition_poly_evaluations: Vec>>, + /// The Merkle tree built to compute the commitment to the composition polynomial parts. pub(crate) composition_poly_merkle_tree: BatchedMerkleTree, + /// The commitment to the composition polynomial parts. pub(crate) composition_poly_root: Commitment, } +/// A container for the results of the third round of the STARK Prove protocol. pub struct Round3 { + /// Evaluations of the trace polynomials, main ans auxiliary, at the out-of-domain challenge. trace_ood_evaluations: EvaluationTable, + /// Evaluations of the composition polynomial parts at the out-of-domain challenge. composition_poly_parts_ood_evaluation: Vec>, } +/// A container for the results of the fourth round of the STARK Prove protocol. pub struct Round4, E: IsField> { + /// The final value resulting from folding the Deep composition polynomial all the way down to a constant value. fri_last_value: FieldElement, + /// The commitments to the fold polynomials of the inner layers of FRI. fri_layers_merkle_roots: Vec, + /// The values and proofs of validity of the evaluations of the trace polynomials and the composition polynomials + /// parts at the domain values corresponding to the FRI query challenges and their symmetric counterparts. deep_poly_openings: DeepPolynomialOpenings, + /// The values and proofs of validity of the evaluations of the fold polynomials of the inner + /// layers of FRI at the values corresponding to the symmetrics of the FRI query challenges. query_list: Vec>, + /// The proof of work nonce. nonce: Option, } + +/// Returns the evaluations of the polynomial `p` over the lde domain defined by the given +/// `blowup_factor`, `domain_size` and `offset`. The number of evaluations returned is `domain_size +/// * blowup_factor`. The domain generator used is the one given by the implementation of `F` as `IsFFTField`. pub fn evaluate_polynomial_on_lde_domain( p: &Polynomial>, blowup_factor: usize, @@ -128,7 +163,12 @@ where } } +/// The functionality of a STARK prover providing methods to run the STARK Prove protocol +/// https://lambdaclass.github.io/lambdaworks/starks/protocol.html +/// The default implementation is complete and is compatible with Stone prover +/// https://github.com/starkware-libs/stone-prover pub trait IsStarkProver { + /// Returns the Merkle tree and the commitment to the vectors `vectors`. fn batch_commit(vectors: &[Vec>]) -> (BatchedMerkleTree, Commitment) where FieldElement: Serializable + Sync + Send, @@ -142,6 +182,13 @@ pub trait IsStarkProver { (tree, commitment) } + /// Given a `TraceTable`, this method interpolates its columns, computes the commitment to the + /// table and appends it to the transcript. + /// Output: a touple of length 4 with the following: + /// β€’ The polynomials interpolating the columns of `trace`. + /// β€’ The evaluations of the above polynomials over the domain `domain`. + /// β€’ The Merkle tree of evaluations of the above polynomials over the domain `domain`. + /// β€’ The roots of the above Merkle trees. #[allow(clippy::type_complexity)] fn interpolate_and_commit( trace: &TraceTable, @@ -160,6 +207,7 @@ pub trait IsStarkProver { E: IsSubFieldOf, A::Field: IsSubFieldOf, { + // Interpolate columns of `trace`. let trace_polys = trace.compute_trace_polys::(); // Evaluate those polynomials t_j on the large domain D_LDE. @@ -171,11 +219,11 @@ pub trait IsStarkProver { in_place_bit_reverse_permute(col); } - // Compute commitments [t_j]. + // Compute commitment. let lde_trace = TraceTable::from_columns(lde_trace_permuted, A::STEP_SIZE); let (lde_trace_merkle_tree, lde_trace_merkle_root) = Self::batch_commit(&lde_trace.rows()); - // >>>> Send commitments: [tβ±Ό] + // >>>> Send commitment. transcript.append_bytes(&lde_trace_merkle_root); ( @@ -186,6 +234,8 @@ pub trait IsStarkProver { ) } + /// Evaluate polynomials `trace_polys` over the domain `domain`. + /// The i-th entry of the returned vector contains the evaluations of the i-th polynomial in `trace_polys`. fn compute_lde_trace_evaluations( trace_polys: &[Polynomial>], domain: &Domain, @@ -214,6 +264,7 @@ pub trait IsStarkProver { .unwrap() } + /// Returns the result of the first round of the STARK Prove protocol. fn round_1_randomized_air_with_preprocessing( air: &A, main_trace: &TraceTable, @@ -259,6 +310,8 @@ pub trait IsStarkProver { }) } + /// Returns the Merkle tree and the commitment to the evaluations of the parts of the + /// composition polynomial. fn commit_composition_polynomial( lde_composition_poly_parts_evaluations: &[Vec>], ) -> (BatchedMerkleTree, Commitment) @@ -288,6 +341,7 @@ pub trait IsStarkProver { Self::batch_commit(&lde_composition_poly_evaluations_merged) } + /// Returns the result of the second round of the STARK Prove protocol. fn round_2_compute_composition_polynomial( air: &A, domain: &Domain, @@ -344,6 +398,7 @@ pub trait IsStarkProver { } } + /// Returns the result of the third round of the STARK Prove protocol. fn round_3_evaluate_polynomials_in_out_of_domain_element( air: &A, domain: &Domain, @@ -392,6 +447,7 @@ pub trait IsStarkProver { } } + /// Returns the result of the fourth round of the STARK Prove protocol. fn round_4_compute_and_run_fri_on_the_deep_composition_polynomial( air: &A, domain: &Domain, @@ -573,9 +629,12 @@ pub trait IsStarkProver { h_terms + trace_terms } + /// Adds to `accumulator` the term corresponding to the trace polynomial `t_j` of the Deep + /// composition polynomial. That is, returns `accumulator + \sum_i \gamma_i \frac{ t_j - t_j(zg^i) }{ X - zg^i }`, + /// where `i` ranges from `T * j` to `T * j + T - 1`, where `T` is the number of offsets in every frame. fn compute_trace_term( - trace_terms: &Polynomial>, - (i, t_j): (usize, &Polynomial>), + accumulator: &Polynomial>, + (j, t_j): (usize, &Polynomial>), trace_frame_length: usize, trace_terms_gammas: &[FieldElement], trace_frame_evaluations: &[Vec>], @@ -586,11 +645,8 @@ pub trait IsStarkProver { FieldElement: Serializable + Send + Sync, FieldElement: Serializable + Send + Sync, { - let i_times_trace_frame_evaluation = i * trace_frame_length; - let iter_trace_gammas = trace_terms_gammas - .iter() - .skip(i_times_trace_frame_evaluation); - let trace_int = trace_frame_evaluations[i] + let iter_trace_gammas = trace_terms_gammas.iter().skip(j * trace_frame_length); + let trace_int = trace_frame_evaluations[j] .iter() .zip(transition_offsets) .zip(iter_trace_gammas) @@ -605,9 +661,12 @@ pub trait IsStarkProver { }, ); - trace_terms + trace_int + accumulator + trace_int } + /// Computes values and validity proofs of the evaluations of the composition polynomial parts + /// at the domain value corresponding to the FRI query challenge `index` and its symmetric + /// element. fn open_composition_poly( composition_poly_merkle_tree: &BatchedMerkleTree, lde_composition_poly_evaluations: &[Vec>], @@ -647,6 +706,9 @@ pub trait IsStarkProver { } } + /// Computes values and validity proofs of the evaluations of the trace polynomials + /// at the domain value corresponding to the FRI query challenge `index` and its symmetric + /// element. fn open_trace_polys( domain: &Domain, tree: &BatchedMerkleTree, @@ -675,8 +737,7 @@ pub trait IsStarkProver { } } - /// Open the deep composition polynomial on a list of indexes - /// and their symmetric elements. + /// Open the deep composition polynomial on a list of indexes and their symmetric elements. fn open_deep_composition_poly( domain: &Domain, round_1_result: &Round1, @@ -723,6 +784,8 @@ pub trait IsStarkProver { } // FIXME remove unwrap() calls and return errors + /// Generates a STARK proof for the trace `main_trace` with public inputs `pub_inputs`. + /// Warning: the transcript must be safely initializated before passing it to this method. fn prove( main_trace: &TraceTable, pub_inputs: &A::PublicInputs, @@ -910,9 +973,9 @@ pub trait IsStarkProver { info!("End proof generation"); Ok(StarkProof:: { - // [tβ±Ό] + // [t] lde_trace_main_merkle_root: round_1_result.main.lde_trace_merkle_root, - // [tβ±Ό] + // [t] lde_trace_aux_merkle_root: round_1_result.aux.map(|x| x.lde_trace_merkle_root), // tβ±Ό(zgᡏ) trace_ood_evaluations: round_3_result.trace_ood_evaluations, diff --git a/provers/stark/src/transcript.rs b/provers/stark/src/transcript.rs index 15700848a0..84a3bc478a 100644 --- a/provers/stark/src/transcript.rs +++ b/provers/stark/src/transcript.rs @@ -9,12 +9,19 @@ use lambdaworks_math::{ }; use sha3::{Digest, Keccak256}; +/// The functionality of a transcript to be used in the STARK Prove and Verify protocols. pub trait IsStarkTranscript { + /// Appends a field element to the transcript. fn append_field_element(&mut self, element: &FieldElement); + /// Appends a bytes to the transcript. fn append_bytes(&mut self, new_bytes: &[u8]); + /// Returns the inner state of the transcript that fully determines its outputs. fn state(&self) -> [u8; 32]; + /// Returns a random field element. fn sample_field_element(&mut self) -> FieldElement; + /// Returns a random index between 0 and `upper_bound`. fn sample_u64(&mut self, upper_bound: u64) -> u64; + /// Returns a field element not contained in `lde_roots_of_unity_coset` or `trace_roots_of_unity`. fn sample_z_ood>( &mut self, lde_roots_of_unity_coset: &[FieldElement], @@ -38,6 +45,7 @@ pub trait IsStarkTranscript { } } +/// A transcript implementing `IsStarkTranscript` and compatible with Stone (https://github.com/starkware-libs/stone-prover). pub struct StoneProverTranscript { state: [u8; 32], seed_increment: U256, @@ -46,9 +54,14 @@ pub struct StoneProverTranscript { } impl StoneProverTranscript { + /// The maximum multiple of the modulus of `p` that fits in 256 bits, where + /// `p = 0x800000000000011000000000000000000000000000000000000000000000001` const MODULUS_MAX_MULTIPLE: U256 = U256::from_hex_unchecked( "f80000000000020f00000000000000000000000000000000000000000000001f", ); + + /// The value of `2^{-256} mod p`, where + /// `p = 0x800000000000011000000000000000000000000000000000000000000000001` const R_INV: U256 = U256::from_hex_unchecked( "0x40000000000001100000000000012100000000000000000000000000000000", ); @@ -154,6 +167,7 @@ impl IsStarkTranscript for StoneProverTranscript { } } +/// Returns a batch of size `size` of field elements sampled from the transcript `transcript`. pub fn batch_sample_challenges( size: usize, transcript: &mut impl IsStarkTranscript, diff --git a/provers/stark/src/verifier.rs b/provers/stark/src/verifier.rs index 6755664528..72fad35585 100644 --- a/provers/stark/src/verifier.rs +++ b/provers/stark/src/verifier.rs @@ -29,30 +29,44 @@ use super::{ traits::AIR, }; +/// A default STARK verifier implementing `IsStarkVerifier`. pub struct Verifier { phantom: PhantomData, } impl IsStarkVerifier for Verifier {} +/// A container holding the complete list of challenges sent to the prover along with the seed used +/// to validate the proof-of-work nonce. pub struct Challenges where F: IsField, A: AIR, { + /// The out-of-domain challenge. pub z: FieldElement, + /// The composition polynomial coefficients corresponding to the boundary constraints terms. pub boundary_coeffs: Vec>, + /// The composition polynomial coefficients corresponding to the transition constraints terms. pub transition_coeffs: Vec>, + /// The deep composition polynomial coefficients corresponding to the trace polynomial terms. pub trace_term_coeffs: Vec>>, + /// The deep composition polynomial coefficients corresponding to the composition polynomial parts terms. pub gammas: Vec>, + /// The list of FRI commit phase folding challenges. pub zetas: Vec>, + /// The list of FRI query phase index challenges. pub iotas: Vec, + /// The challenges used to build the auxiliary trace. pub rap_challenges: A::RAPChallenges, + /// The seed used to verify the proof-of-work nonce. pub grinding_seed: [u8; 32], } pub type DeepPolynomialEvaluations = (Vec>, Vec>); +/// The functionality of a STARK verifier providing methods to run the STARK Verify protocol +/// https://lambdaclass.github.io/lambdaworks/starks/protocol.html pub trait IsStarkVerifier { fn sample_query_indexes( number_of_queries: usize, @@ -65,6 +79,7 @@ pub trait IsStarkVerifier { .collect::>() } + /// Returns the list of challenges sent to the prover. fn step_1_replay_rounds_and_recover_challenges( air: &A, proof: &StarkProof, @@ -201,6 +216,9 @@ pub trait IsStarkVerifier { } } + /// Checks whether the purported evaluations of the composition polynomial parts and the trace + /// polynomials at the out-of-domain challenge are consistent. + /// See https://lambdaclass.github.io/lambdaworks/starks/protocol.html#step-2-verify-claimed-composition-polynomial fn step_2_verify_claimed_composition_polynomial( air: &A, proof: &StarkProof, @@ -305,6 +323,9 @@ pub trait IsStarkVerifier { composition_poly_claimed_ood_evaluation == composition_poly_ood_evaluation } + /// Reconstructs the Deep composition polynomial evaluations at the challenge indices values using the provided + /// openings of the trace polynomials and the composition polynomial parts. It then uses these to verify that the + /// FRI decommitments are valid and correspond to the Deep composition polynomial. fn step_3_verify_fri( proof: &StarkProof, domain: &Domain, @@ -333,7 +354,6 @@ pub trait IsStarkVerifier { .zip(evaluation_point_inverse) .enumerate() .fold(true, |mut result, (i, ((proof_s, iota_s), eval))| { - // this is done in constant time result &= Self::verify_query_and_sym_openings( proof, &challenges.zetas, @@ -347,6 +367,7 @@ pub trait IsStarkVerifier { }) } + /// Returns the field element element of the domain `domain` corresponding to the given FRI query index challenge `iota`. fn query_challenge_to_evaluation_point( iota: usize, domain: &Domain, @@ -356,6 +377,7 @@ pub trait IsStarkVerifier { .clone() } + /// Returns the symmetric field element element of the domain `domain` corresponding to the given FRI query index challenge `iota`. fn query_challenge_to_evaluation_point_sym( iota: usize, domain: &Domain, @@ -365,6 +387,7 @@ pub trait IsStarkVerifier { .clone() } + /// Verifies the validity of the opening proof. fn verify_opening( proof: &Proof, root: &Commitment, @@ -458,6 +481,9 @@ pub trait IsStarkVerifier { ) } + /// Verifies the validity of the purported values of the trace polynomials and the composition polynomial + /// parts at the domain elements and their symmetric counterparts corresponding to all the FRI query + /// index challenges. fn step_4_verify_trace_and_composition_openings( proof: &StarkProof, challenges: &Challenges, @@ -481,6 +507,7 @@ pub trait IsStarkVerifier { ) } + /// Verifies the openings of a fold polynomial of an inner layer of FRI. fn verify_fri_layer_openings( merkle_root: &Commitment, auth_path_sym: &Proof, @@ -686,6 +713,8 @@ pub trait IsStarkVerifier { trace_term + h_terms } + /// Verifies a STARK proof with public inputs `pub_inputs`. + /// Warning: the transcript must be safely initializated before passing it to this method. fn verify( proof: &StarkProof, pub_input: &A::PublicInputs, From 152a7fd1f702bf7bdc7d48a9e736fd9eb4c6efc1 Mon Sep 17 00:00:00 2001 From: Emir Soyturk Date: Wed, 3 Jan 2024 00:14:30 +0300 Subject: [PATCH 26/29] =?UTF-8?q?feat(math):=20Add=20to=5Fhex(&self)=20->?= =?UTF-8?q?=20String=20to=20FieldElement=20of=20a=20IsPrimeF=E2=80=A6=20(#?= =?UTF-8?q?735)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(math): Add to_hex(&self) -> String to FieldElement of a IsPrimeField * fix: mark winterfell to_hex as todo * fix: unused variable error * fix(math): conditional compilation flag added to to_hex functions * fix: lint tab error * fix: clippy accessing first element error * fix: changing comment to documentation * fix: adding comment for unsigned integer --------- Co-authored-by: Mariano A. Nicolini --- exercises/message/src/starks/fri/mod.rs | 2 +- math/src/field/element.rs | 6 ++++ math/src/field/fields/mersenne31/field.rs | 12 +++++++ .../fields/montgomery_backed_prime_fields.rs | 34 +++++++++++++++++++ .../fields/p448_goldilocks_prime_field.rs | 24 +++++++++++++ math/src/field/fields/u64_goldilocks_field.rs | 12 +++++++ math/src/field/fields/u64_prime_field.rs | 19 +++++++++++ math/src/field/fields/winterfell.rs | 4 +++ math/src/field/test_fields/u32_test_field.rs | 12 +++++++ math/src/field/test_fields/u64_test_field.rs | 12 +++++++ math/src/field/traits.rs | 4 +++ math/src/unsigned_integer/element.rs | 17 ++++++++++ 12 files changed, 157 insertions(+), 1 deletion(-) diff --git a/exercises/message/src/starks/fri/mod.rs b/exercises/message/src/starks/fri/mod.rs index c93a1cd411..f7fb9fc186 100644 --- a/exercises/message/src/starks/fri/mod.rs +++ b/exercises/message/src/starks/fri/mod.rs @@ -61,7 +61,7 @@ where let last_value = last_poly .coefficients() - .get(0) + .first() .unwrap_or(&FieldElement::zero()) .clone(); diff --git a/math/src/field/element.rs b/math/src/field/element.rs index 0404b9fe48..4007195070 100644 --- a/math/src/field/element.rs +++ b/math/src/field/element.rs @@ -488,6 +488,12 @@ impl FieldElement { value: F::from_hex(hex_string)?, }) } + + #[cfg(feature = "std")] + /// Creates a hexstring from a `FieldElement` without `0x`. + pub fn to_hex(&self) -> String { + F::to_hex(&self.value) + } } #[cfg(feature = "lambdaworks-serde-binary")] diff --git a/math/src/field/fields/mersenne31/field.rs b/math/src/field/fields/mersenne31/field.rs index 7a07597f95..5454cd6814 100644 --- a/math/src/field/fields/mersenne31/field.rs +++ b/math/src/field/fields/mersenne31/field.rs @@ -173,6 +173,11 @@ impl IsPrimeField for Mersenne31Field { } u32::from_str_radix(hex_string, 16).map_err(|_| CreationError::InvalidHexString) } + + #[cfg(feature = "std")] + fn to_hex(x: &u32) -> String { + format!("{:X}", x) + } } impl FieldElement { @@ -399,4 +404,11 @@ mod tests { let b = F::from_base_type(1u32); assert_eq!(b, F::one()); } + + #[cfg(feature = "std")] + #[test] + fn to_hex_test() { + let num = F::from_hex("B").unwrap(); + assert_eq!(F::to_hex(&num), "B"); + } } diff --git a/math/src/field/fields/montgomery_backed_prime_fields.rs b/math/src/field/fields/montgomery_backed_prime_fields.rs index 01e3e59059..e2e0f66a45 100644 --- a/math/src/field/fields/montgomery_backed_prime_fields.rs +++ b/math/src/field/fields/montgomery_backed_prime_fields.rs @@ -309,6 +309,11 @@ where &MontgomeryBackendPrimeField::::MU, )) } + + #[cfg(feature = "std")] + fn to_hex(x: &Self::BaseType) -> String { + Self::BaseType::to_hex(x) + } } impl FieldElement> where @@ -1129,6 +1134,35 @@ mod tests_u256_prime_fields { assert_eq!(a, b); } + #[cfg(feature = "std")] + #[test] + fn to_hex_test_works_1() { + let a = U256FP1Element::from_hex_unchecked("eb235f6144d9e91f4b14"); + let b = U256FP1Element::new(U256 { + limbs: [0, 0, 60195, 6872850209053821716], + }); + + assert_eq!(U256FP1Element::to_hex(&a), U256FP1Element::to_hex(&b)); + } + + #[cfg(feature = "std")] + #[test] + fn to_hex_test_works_2() { + let a = U256F29Element::from_hex_unchecked("1d"); + let b = U256F29Element::zero(); + + assert_eq!(U256F29Element::to_hex(&a), U256F29Element::to_hex(&b)); + } + + #[cfg(feature = "std")] + #[test] + fn to_hex_test_works_3() { + let a = U256F29Element::from_hex_unchecked("aa"); + let b = U256F29Element::from(25); + + assert_eq!(U256F29Element::to_hex(&a), U256F29Element::to_hex(&b)); + } + // Goldilocks #[derive(Clone, Debug)] struct GoldilocksModulus; diff --git a/math/src/field/fields/p448_goldilocks_prime_field.rs b/math/src/field/fields/p448_goldilocks_prime_field.rs index 30400988ba..5e927386c6 100644 --- a/math/src/field/fields/p448_goldilocks_prime_field.rs +++ b/math/src/field/fields/p448_goldilocks_prime_field.rs @@ -217,6 +217,11 @@ impl IsPrimeField for P448GoldilocksPrimeField { U56x8::from_hex(hex_string) } + #[cfg(feature = "std")] + fn to_hex(x: &U56x8) -> String { + U56x8::to_hex(x) + } + fn field_bit_size() -> usize { 448 } @@ -310,6 +315,15 @@ impl U56x8 { Ok(U56x8 { limbs: result }) } + + #[cfg(feature = "std")] + pub fn to_hex(&self) -> String { + let mut hex_string = String::new(); + for &limb in self.limbs.iter().rev() { + hex_string.push_str(&format!("{:014X}", limb)); + } + hex_string.trim_start_matches('0').to_string() + } } #[cfg(test)] @@ -435,4 +449,14 @@ mod tests { U56x8::from_hex("564a75b90ae34f8155d5821d7e9484").unwrap() ); } + + #[cfg(feature = "std")] + #[test] + fn to_hex_test() { + let mut limbs = [0u64; 8]; + limbs[0] = 15372427657916355716u64; + limbs[1] = 6217911673150459564u64; + let num = U56x8::from_hex("564A75B90AE34F8155D5821D7E9484").unwrap(); + assert_eq!(U56x8::to_hex(&num), "564A75B90AE34F8155D5821D7E9484") + } } diff --git a/math/src/field/fields/u64_goldilocks_field.rs b/math/src/field/fields/u64_goldilocks_field.rs index 82be9ef4de..bfbd7802cd 100644 --- a/math/src/field/fields/u64_goldilocks_field.rs +++ b/math/src/field/fields/u64_goldilocks_field.rs @@ -177,6 +177,11 @@ impl IsPrimeField for Goldilocks64Field { } u64::from_str_radix(hex_string, 16).map_err(|_| CreationError::InvalidHexString) } + + #[cfg(feature = "std")] + fn to_hex(x: &u64) -> String { + format!("{:X}", x) + } } #[inline(always)] @@ -473,4 +478,11 @@ mod tests { let b = F::from_base_type(1u64); assert_eq!(b, F::one()); } + + #[cfg(feature = "std")] + #[test] + fn to_hex_test() { + let num = F::from_hex("B").unwrap(); + assert_eq!(F::to_hex(&num), "B"); + } } diff --git a/math/src/field/fields/u64_prime_field.rs b/math/src/field/fields/u64_prime_field.rs index f3096740d6..b300464bda 100644 --- a/math/src/field/fields/u64_prime_field.rs +++ b/math/src/field/fields/u64_prime_field.rs @@ -102,6 +102,11 @@ impl IsPrimeField for U64PrimeField { u64::from_str_radix(hex_string, 16).map_err(|_| CreationError::InvalidHexString) } + + #[cfg(feature = "std")] + fn to_hex(x: &u64) -> String { + format!("{:X}", x) + } } /// Represents an element in Fp. (E.g: 0, 1, 2 are the elements of F3) @@ -176,6 +181,20 @@ mod tests { assert_eq!(F::from_hex("0x1a").unwrap(), 26); } + #[cfg(feature = "std")] + #[test] + fn to_hex_test_works_1() { + let num = F::from_hex("B").unwrap(); + assert_eq!(F::to_hex(&num), "B"); + } + + #[cfg(feature = "std")] + #[test] + fn to_hex_test_works_2() { + let num = F::from_hex("0x1a").unwrap(); + assert_eq!(F::to_hex(&num), "1A"); + } + #[test] fn bit_size_of_mod_13_field_is_4() { assert_eq!( diff --git a/math/src/field/fields/winterfell.rs b/math/src/field/fields/winterfell.rs index 5c98cc9dd8..5a2bd89e14 100644 --- a/math/src/field/fields/winterfell.rs +++ b/math/src/field/fields/winterfell.rs @@ -34,6 +34,10 @@ impl IsPrimeField for Felt { todo!() } + fn to_hex(_a: &Self::BaseType) -> String { + todo!() + } + fn field_bit_size() -> usize { 128 // TODO } diff --git a/math/src/field/test_fields/u32_test_field.rs b/math/src/field/test_fields/u32_test_field.rs index d4dd139915..40de22bcd1 100644 --- a/math/src/field/test_fields/u32_test_field.rs +++ b/math/src/field/test_fields/u32_test_field.rs @@ -116,6 +116,11 @@ impl IsPrimeField for U32Field { u32::from_str_radix(hex_string, 16).map_err(|_| CreationError::InvalidHexString) } + + #[cfg(feature = "std")] + fn to_hex(x: &u32) -> String { + format!("{:X}", x) + } } // 15 * 2^27 + 1; @@ -136,6 +141,13 @@ mod tests_u32_test_field { assert_eq!(U32TestField::from_hex("B").unwrap(), 11); } + #[cfg(feature = "std")] + #[test] + fn to_hex_test() { + let num = U32TestField::from_hex("B").unwrap(); + assert_eq!(U32TestField::to_hex(&num), "B"); + } + #[test] fn bit_size_of_test_field_is_31() { assert_eq!( diff --git a/math/src/field/test_fields/u64_test_field.rs b/math/src/field/test_fields/u64_test_field.rs index bd24582f06..f3cbc0dbe8 100644 --- a/math/src/field/test_fields/u64_test_field.rs +++ b/math/src/field/test_fields/u64_test_field.rs @@ -88,6 +88,11 @@ impl IsPrimeField for U64Field { u64::from_str_radix(hex_string, 16).map_err(|_| CreationError::InvalidHexString) } + + #[cfg(feature = "std")] + fn to_hex(x: &u64) -> String { + format!("{:X}", x) + } } pub type U64TestField = U64Field<18446744069414584321>; @@ -139,4 +144,11 @@ mod tests_u64_test_field { let b = a.to_subfield_vec::(); assert_eq!(b, vec![FieldElement::from(1), FieldElement::from(3)]); } + + #[cfg(feature = "std")] + #[test] + fn to_hex_test() { + let num = U64TestField::from_hex("B").unwrap(); + assert_eq!(U64TestField::to_hex(&num), "B"); + } } diff --git a/math/src/field/traits.rs b/math/src/field/traits.rs index fb63cd10d5..9896259b4f 100644 --- a/math/src/field/traits.rs +++ b/math/src/field/traits.rs @@ -204,6 +204,10 @@ pub trait IsPrimeField: IsField { /// Returns an `CreationError::InvalidHexString`if the value is not a hexstring fn from_hex(hex_string: &str) -> Result; + #[cfg(feature = "std")] + /// Creates a hexstring from a `FieldElement` without `0x`. + fn to_hex(a: &Self::BaseType) -> String; + /// Returns the number of bits of the max element of the field, as per field documentation, not internal representation. /// This is `log2(max FE)` rounded up fn field_bit_size() -> usize; diff --git a/math/src/unsigned_integer/element.rs b/math/src/unsigned_integer/element.rs index 70e111854f..e65e0ae86d 100644 --- a/math/src/unsigned_integer/element.rs +++ b/math/src/unsigned_integer/element.rs @@ -486,6 +486,16 @@ impl UnsignedInteger { UnsignedInteger { limbs: result } } + /// Creates a hexstring from a `FieldElement` without `0x`. + #[cfg(feature = "std")] + pub fn to_hex(&self) -> String { + let mut hex_string = String::new(); + for &limb in self.limbs.iter() { + hex_string.push_str(&format!("{:014X}", limb)); + } + hex_string.trim_start_matches('0').to_string() + } + pub const fn const_ne(a: &UnsignedInteger, b: &UnsignedInteger) -> bool { let mut i = 0; while i < NUM_LIMBS { @@ -2968,4 +2978,11 @@ mod tests_u256 { ) ); } + + #[cfg(feature = "std")] + #[test] + fn to_hex_test() { + let a = U256::from_hex_unchecked("390aa99bead76bc0093b1bc1a8101f5ce"); + assert_eq!(U256::to_hex(&a), "390AA99BEAD76BC0093B1BC1A8101F5CE") + } } From 5b771eef2c86ebd20ab972b70c707411e3fe08b1 Mon Sep 17 00:00:00 2001 From: Marcos Villagra <71292159+mdvillagra@users.noreply.github.com> Date: Tue, 2 Jan 2024 18:42:31 -0300 Subject: [PATCH 27/29] Add Vesta curve basic functionality (#714) * added file structure * copy-pasted the code from pallas * added pub * `pub` type for vesta * unit tests failing * all unit tests succeed * cargo fmt --------- Co-authored-by: Mariano A. Nicolini --- .../short_weierstrass/curves/mod.rs | 1 + .../short_weierstrass/curves/vesta/curve.rs | 136 ++++++++++++++++++ .../short_weierstrass/curves/vesta/mod.rs | 1 + math/src/field/fields/mod.rs | 2 +- math/src/field/fields/vesta_field.rs | 7 +- 5 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 math/src/elliptic_curve/short_weierstrass/curves/vesta/curve.rs create mode 100644 math/src/elliptic_curve/short_weierstrass/curves/vesta/mod.rs diff --git a/math/src/elliptic_curve/short_weierstrass/curves/mod.rs b/math/src/elliptic_curve/short_weierstrass/curves/mod.rs index f10e096850..d48704395b 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/mod.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/mod.rs @@ -4,3 +4,4 @@ pub mod pallas; pub mod stark_curve; pub mod test_curve_1; pub mod test_curve_2; +pub mod vesta; diff --git a/math/src/elliptic_curve/short_weierstrass/curves/vesta/curve.rs b/math/src/elliptic_curve/short_weierstrass/curves/vesta/curve.rs new file mode 100644 index 0000000000..ac871a3d6f --- /dev/null +++ b/math/src/elliptic_curve/short_weierstrass/curves/vesta/curve.rs @@ -0,0 +1,136 @@ +use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint; +use crate::elliptic_curve::traits::IsEllipticCurve; +use crate::field::fields::vesta_field::Vesta255PrimeField; +use crate::{ + elliptic_curve::short_weierstrass::traits::IsShortWeierstrass, field::element::FieldElement, +}; + +#[derive(Clone, Debug)] +pub struct VestaCurve; + +impl IsEllipticCurve for VestaCurve { + type BaseField = Vesta255PrimeField; + type PointRepresentation = ShortWeierstrassProjectivePoint; + + fn generator() -> Self::PointRepresentation { + Self::PointRepresentation::new([ + -FieldElement::::one(), + FieldElement::::from(2), + FieldElement::one(), + ]) + } +} + +impl IsShortWeierstrass for VestaCurve { + fn a() -> FieldElement { + FieldElement::from(0) + } + + fn b() -> FieldElement { + FieldElement::from(5) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + cyclic_group::IsGroup, elliptic_curve::traits::EllipticCurveError, + field::element::FieldElement, + }; + + use super::VestaCurve; + + #[allow(clippy::upper_case_acronyms)] + type FE = FieldElement; + + fn point_1() -> ShortWeierstrassProjectivePoint { + let x = FE::from_hex_unchecked( + "c4e6a8789457a64e1638783181963d4c4399a5a8cdb30af4038664ce431033c", + ); + let y = FE::from_hex_unchecked( + "2d8c9125be9a3ac50371e462f63dfc3fbbf645e9a93d6b7da71c13d3065e3ce5", + ); + VestaCurve::create_point_from_affine(x, y).unwrap() + } + + fn point_1_times_5() -> ShortWeierstrassProjectivePoint { + let x = FE::from_hex_unchecked( + "1266f29f1478410eaa62fb1ab064f7d9259f515600544165972a89c9941c72c3", + ); + let y = FE::from_hex_unchecked( + "3a893b592bd487cd25c5d4237b02987e1b78206e70989f3209e24a40b89499fd", + ); + VestaCurve::create_point_from_affine(x, y).unwrap() + } + + #[test] + fn adding_five_times_point_1_works() { + let point_1 = point_1(); + let point_1_times_5 = point_1_times_5(); + assert_eq!(point_1.operate_with_self(5_u16), point_1_times_5); + } + + #[test] + fn create_valid_point_works() { + let p = point_1(); + assert_eq!( + *p.x(), + FE::from_hex_unchecked( + "c4e6a8789457a64e1638783181963d4c4399a5a8cdb30af4038664ce431033c" + ) + ); + assert_eq!( + *p.y(), + FE::from_hex_unchecked( + "2d8c9125be9a3ac50371e462f63dfc3fbbf645e9a93d6b7da71c13d3065e3ce5" + ) + ); + assert_eq!(*p.z(), FE::from_hex_unchecked("1")); + } + + #[test] + fn create_invalid_points_returns_an_error() { + assert_eq!( + VestaCurve::create_point_from_affine(FE::from(0), FE::from(1)), + Err(EllipticCurveError::InvalidPoint) + ); + } + + #[test] + fn equality_works() { + let g = VestaCurve::generator(); + let g2 = g.operate_with_self(2_u16); + let g2_other = g.operate_with(&g); + assert_ne!(&g2, &g); + assert_eq!(&g, &g); + assert_eq!(&g2, &g2_other); + } + + #[test] + fn g_operated_with_g_satifies_ec_equation() { + let g = VestaCurve::generator(); + let g2 = g.operate_with_self(2_u16); + + // get x and y from affine coordinates + let g2_affine = g2.to_affine(); + let x = g2_affine.x(); + let y = g2_affine.y(); + + // calculate both sides of Pallas curve equation + let five = VestaCurve::b(); + let y_sq_0 = x.pow(3_u16) + five; + let y_sq_1 = y.pow(2_u16); + + assert_eq!(y_sq_0, y_sq_1); + } + + #[test] + fn operate_with_self_works_1() { + let g = VestaCurve::generator(); + assert_eq!( + g.operate_with(&g).operate_with(&g), + g.operate_with_self(3_u16) + ); + } +} diff --git a/math/src/elliptic_curve/short_weierstrass/curves/vesta/mod.rs b/math/src/elliptic_curve/short_weierstrass/curves/vesta/mod.rs new file mode 100644 index 0000000000..201a862ce5 --- /dev/null +++ b/math/src/elliptic_curve/short_weierstrass/curves/vesta/mod.rs @@ -0,0 +1 @@ +pub mod curve; diff --git a/math/src/field/fields/mod.rs b/math/src/field/fields/mod.rs index d9d923d6c2..48b94e9010 100644 --- a/math/src/field/fields/mod.rs +++ b/math/src/field/fields/mod.rs @@ -17,4 +17,4 @@ pub mod u64_prime_field; pub mod winterfell; /// Implemenation of Vesta Prime field (p = 2^254 + 45560315531506369815346746415080538113) -mod vesta_field; +pub mod vesta_field; diff --git a/math/src/field/fields/vesta_field.rs b/math/src/field/fields/vesta_field.rs index 317f418333..c880bcebab 100644 --- a/math/src/field/fields/vesta_field.rs +++ b/math/src/field/fields/vesta_field.rs @@ -1,7 +1,10 @@ use crate::{ - field::fields::montgomery_backed_prime_fields::IsModulus, unsigned_integer::element::U256, + field::fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, + unsigned_integer::element::U256, }; +type VestaMontgomeryBackendPrimeField = MontgomeryBackendPrimeField; + #[derive(Debug, Clone, PartialEq, Eq)] pub struct MontgomeryConfigVesta255PrimeField; impl IsModulus for MontgomeryConfigVesta255PrimeField { @@ -9,3 +12,5 @@ impl IsModulus for MontgomeryConfigVesta255PrimeField { "0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001", ); } + +pub type Vesta255PrimeField = VestaMontgomeryBackendPrimeField; From 34af688068952e7b31e2bac78ab2a912b89288d8 Mon Sep 17 00:00:00 2001 From: Diego K <43053772+diegokingston@users.noreply.github.com> Date: Wed, 3 Jan 2024 11:15:12 -0300 Subject: [PATCH 28/29] Update readme (#738) * Update readme * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md --------- Co-authored-by: MauroFab --- README.md | 66 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 56 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index aeaf745d82..3ca089854c 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,33 @@ # LambdaWorks -From the heights of these towers of fields, forty centuries of mathematics look down on us. The library for kids who wanna learn how to do STARKs, SNARKs and learn other cryptographic stuff too. -

+The library for kids who wanna learn how to do STARKs, SNARKs and learn other cryptographic stuff too. + +> From the heights of these towers of fields, forty centuries of mathematics look down on us. + +This library provides efficient implementation of cryptographic primitives used to build proving systems. Along with it, many backends for proving systems are shipped, and compatibility with different frontends is supported. + +- [Our vision on ZKP](https://blog.lambdaclass.com/transforming-the-future-with-zero-knowledge-proofs-fully-homomorphic-encryption-and-new-distributed-systems-algorithms/) +- [Lambda Crypto Doctrine](https://blog.lambdaclass.com/lambda-crypto-doctrine/) +## Table of contents +
+ + +- [LambdaWorks](#lambdaworks) + - [Documentation](#documentation) + - [List of features](#list-of-features) + - [Main crates](#main-crates) + - [Crypto](#crypto) + - [Examples - mini apps](#examples---mini-apps) + - [Exercises and Challenges](#exercises-and-challenges) + - [Why did we build lambdaworks](#why-did-we-build-lambdaworks) + - [Additional tooling usage](#additional-tooling-usage) + - [Fuzzers](#fuzzers) + - [Documentation building](#documentation-building) + - [πŸ“Š Benchmarks](#-benchmarks) + - [πŸ“š References](#-references) + + [![Telegram Chat][tg-badge]][tg-url] [![codecov](https://img.shields.io/codecov/c/github/lambdaclass/lambdaworks)](https://codecov.io/gh/lambdaclass/lambdaworks) @@ -14,6 +39,7 @@ From the heights of these towers of fields, forty centuries of mathematics look ## [Documentation](https://lambdaclass.github.io/lambdaworks) ## List of features + Disclaimer: This list contains cryptographic primitives and mathematical structures that we want to support in Lambdaworks. It can be expanded later to include new primitives. If you find there is a mistake or there has been an update in another library, please let us know. List of symbols: @@ -35,8 +61,8 @@ List of symbols: | BLS12-381 | :heavy_check_mark: | :heavy_check_mark: | | | | | BLS12-377 | πŸ—οΈ | :heavy_check_mark: | | :heavy_check_mark: | | | BN-254 | :x: | :heavy_check_mark: | | | | -| Pallas | πŸ—οΈ | :heavy_check_mark: | | | | -| Vesta | πŸ—οΈ | :heavy_check_mark: | | | | +| Pallas | :heavy_check_mark: | :heavy_check_mark: | | | | +| Vesta | :heavy_check_mark: | :heavy_check_mark: | | | | | Bandersnatch | πŸ—οΈ | :heavy_check_mark: | | | | | **STARKs** | **Lambdaworks** | **Arkworks** | **Halo2** | **gnark** | **Constantine** | | STARK Prover | :heavy_check_mark: | :x: | | :x: | | @@ -58,16 +84,26 @@ List of symbols: | Protostar | :x: | | | | | | Protogalaxy | :x: | | | | | +Additionally, provers are compatible with the following frontends and VMs: -Zero-Knowledge and Validity Proofs have gained a lot of attention over the last few years. We strongly believe in this potential and that is why we decided to start working in this challenging ecosystem, where math, cryptography and distributed systems meet. The main barrier in the beginning was not the cryptography or math but the lack of good libraries which are performant and developer friendly. There are some exceptions, though, like gnark or halo2. Some have nice APIs and are easy to work with, but they are not written in Rust, and some are written in Rust but have poor programming and engineering practices. Most of them don't have support for CUDA, Metal and WebGPU or distributed FFT calculation using schedulers like Dask. - -So, we decided to build our library, focusing on performance, with clear documentation and developer-focused. Our core team is a group of passionate people from different backgrounds and different strengths; we think that the whole is greater than just the addition of the parts. We don't want to be a compilation of every research result in the ZK space. We want this to be a library that can be used in production, not just in academic research. We want to offer developers the main building blocks and proof systems so that they can build their applications on top of this library. +| Backend | Frontend | Status | +|---------|----------|--------| +| Groth16 | Arkworks | :heavy_check_mark: | +| Groth16 | Gnark | :x: | +| Groth16 | Circom | πŸ—οΈ | +| Plonk | Gnark | πŸ—οΈ | +| Plonk | Noir | :x: | +| Stark | Winterfell | :heavy_check_mark: | +| Stark | Miden | :heavy_check_mark: | +| Stark | Cairo | :heavy_check_mark: | +This can be used in a multi prover setting for extra security, or as a standalone to be used with Rust. ## Main crates - [Math](https://github.com/lambdaclass/lambdaworks/tree/main/math) - [Crypto primitives](https://github.com/lambdaclass/lambdaworks/tree/main/crypto) +- [STARK Prover](https://github.com/lambdaclass/lambdaworks/tree/main/provers/stark) - [Plonk Prover](https://github.com/lambdaclass/lambdaworks/tree/main/provers/plonk) - [Cairo Prover](https://github.com/lambdaclass/lambdaworks/tree/main/provers/cairo) - [Groth 16](https://github.com/lambdaclass/lambdaworks/tree/main/provers/groth16) @@ -77,6 +113,7 @@ If you are interested in proving Cairo programs, use the Cairo Prover CLI. ### Crypto - [Elliptic curves](https://github.com/lambdaclass/lambdaworks/tree/main/math/src/elliptic_curve) - [Multiscalar multiplication](https://github.com/lambdaclass/lambdaworks/tree/main/math/src/msm) +- [Hashes](https://github.com/lambdaclass/lambdaworks/tree/main/crypto/src/hash) Finite Field crate fully supports no-std with `no-default-features` @@ -87,6 +124,9 @@ Both Math and Crypto support wasm with target `wasm32-unknown-unknown` by defaul ## Exercises and Challenges - [Lambdaworks exercises and challenges](https://github.com/lambdaclass/lambdaworks_exercises/tree/main) +- [Roadmap for Sparkling Water Bootcamp](https://github.com/lambdaclass/sparkling_water_bootcamp/blob/main/README.md) + +## Citing Lambdaworks If you use ```Lambdaworks``` libraries in your research projects, please cite them using the following template: @@ -99,9 +139,15 @@ If you use ```Lambdaworks``` libraries in your research projects, please cite th } ``` -### Gadgets +## Why we built Lambdaworks + +Zero-Knowledge and Validity Proofs have gained a lot of attention over the last few years. We strongly believe in this potential and that is why we decided to start working in this challenging ecosystem, where math, cryptography and distributed systems meet. The main barrier in the beginning was not the cryptography or math but the lack of good libraries which are performant and developer friendly. There are some exceptions, though, like gnark or halo2. Some have nice APIs and are easy to work with, but they are not written in Rust, and some are written in Rust but have poor programming and engineering practices. Most of them don't have support for CUDA, Metal and WebGPU or distributed FFT calculation using schedulers like Dask. + +So, we decided to build our library, focusing on performance, with clear documentation and developer-focused. Our core team is a group of passionate people from different backgrounds and different strengths; we think that the whole is greater than just the addition of the parts. We don't want to be a compilation of every research result in the ZK space. We want this to be a library that can be used in production, not just in academic research. We want to offer developers the main building blocks and proof systems so that they can build their applications on top of this library. + +## Additional tooling usage -## Fuzzers +### Fuzzers Fuzzers are divided between the ones that use only the CPU, the ones that use Metal, and the ones that use CUDA. @@ -124,7 +170,7 @@ Run a specific fuzzer from the ones contained in **fuzz/fuzz_targets/** folder w make run-fuzzer FUZZER=field_from_hex ``` -## Documentation +### Documentation building To serve the documentation locally, first install both [mdbook](https://rust-lang.github.io/mdBook/guide/installation.html) and the [Katex preprocessor](https://github.com/lzanini/mdbook-katex#getting-started) to render LaTeX, then run From 17f2c7cd6e3d7c548eb3a755d6224e97d9fb68f0 Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Wed, 3 Jan 2024 16:08:56 -0300 Subject: [PATCH 29/29] Release v0.4.0: Ristretto Nero (#741) --- Cargo.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 611dc73ea3..8ae4e92f74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,18 +4,18 @@ exclude = ["ensure-no_std"] resolver = "2" [workspace.package] -version = "0.3.0" +version = "0.4.0" edition = "2021" license = "Apache-2.0" repository = "https://github.com/lambdaclass/lambdaworks" [workspace.dependencies] iai-callgrind = "0.3.1" -lambdaworks-crypto = { path = "./crypto", version = "0.3.0" } -lambdaworks-gpu = { path = "./gpu", version = "0.3.0" } -lambdaworks-math = { path = "./math", version = "0.3.0" } -stark-platinum-prover = { path = "./provers/stark", version = "0.3.0" } -cairo-platinum-prover = { path = "./provers/cairo", version = "0.3.0" } +lambdaworks-crypto = { path = "./crypto", version = "0.4.0" } +lambdaworks-gpu = { path = "./gpu", version = "0.4.0" } +lambdaworks-math = { path = "./math", version = "0.4.0" } +stark-platinum-prover = { path = "./provers/stark", version = "0.4.0" } +cairo-platinum-prover = { path = "./provers/cairo", version = "0.4.0" } [patch.crates-io] winter-air = { git = "https://github.com/lambdaclass/winterfell-for-lambdaworks.git", branch = "derive-clone-v6.4"}