From 3b9ee6a5492789969644446575de1f99800dedd4 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Fri, 7 Aug 2026 14:52:36 +0200 Subject: [PATCH 1/6] feat: add falcon shake accounts --- .github/workflows/test.yml | 3 + LICENSE | 9 + Scarb.lock | 7 + Scarb.toml | 1 + packages/account/README.md | 10 + packages/account/Scarb.toml | 3 + packages/account/src/falcon_512.cairo | 99 + packages/account/src/falcon_512/account.cairo | 180 + packages/account/src/falcon_512/falcon.cairo | 524 + .../src/falcon_512/falcon_512_shake.cairo | 57 + .../falcon_512/falcon_512_shake_direct.cairo | 58 + packages/account/src/falcon_512/hashing.cairo | 7 + .../falcon_512/hashing/hash_to_point.cairo | 153 + .../src/falcon_512/hashing/shake256.cairo | 334 + packages/account/src/falcon_512/ntt.cairo | 11 + .../account/src/falcon_512/ntt/bitrev.cairo | 43 + .../account/src/falcon_512/ntt/engine.cairo | 382 + .../src/falcon_512/ntt/falcon512.cairo | 81 + .../src/falcon_512/ntt/falcon512_fast.cairo | 12797 ++++++++++++++++ .../src/falcon_512/ntt/roots_felt.cairo | 81 + .../src/falcon_512/ntt/roots_scaled.cairo | 86 + packages/account/src/falcon_512/packing.cairo | 335 + packages/account/src/falcon_512/zq.cairo | 148 + packages/account/src/lib.cairo | 2 + packages/account/src/tests.cairo | 3 + .../src/tests/falcon_512_fixture.cairo | 111 + .../account/src/tests/test_falcon_512.cairo | 443 + .../src/tests/test_falcon_512_ntt.cairo | 153 + packages/interfaces/README.md | 2 + .../interfaces/src/account/accounts.cairo | 21 + 30 files changed, 16144 insertions(+) create mode 100644 packages/account/src/falcon_512.cairo create mode 100644 packages/account/src/falcon_512/account.cairo create mode 100644 packages/account/src/falcon_512/falcon.cairo create mode 100644 packages/account/src/falcon_512/falcon_512_shake.cairo create mode 100644 packages/account/src/falcon_512/falcon_512_shake_direct.cairo create mode 100644 packages/account/src/falcon_512/hashing.cairo create mode 100644 packages/account/src/falcon_512/hashing/hash_to_point.cairo create mode 100644 packages/account/src/falcon_512/hashing/shake256.cairo create mode 100644 packages/account/src/falcon_512/ntt.cairo create mode 100644 packages/account/src/falcon_512/ntt/bitrev.cairo create mode 100644 packages/account/src/falcon_512/ntt/engine.cairo create mode 100644 packages/account/src/falcon_512/ntt/falcon512.cairo create mode 100644 packages/account/src/falcon_512/ntt/falcon512_fast.cairo create mode 100644 packages/account/src/falcon_512/ntt/roots_felt.cairo create mode 100644 packages/account/src/falcon_512/ntt/roots_scaled.cairo create mode 100644 packages/account/src/falcon_512/packing.cairo create mode 100644 packages/account/src/falcon_512/zq.cairo create mode 100644 packages/account/src/tests/falcon_512_fixture.cairo create mode 100644 packages/account/src/tests/test_falcon_512.cairo create mode 100644 packages/account/src/tests/test_falcon_512_ntt.cairo diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a0e962c42..8100335d1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -50,6 +50,9 @@ jobs: - name: Run tests run: snforge test --workspace --features fuzzing --fuzzer-runs 200 + - name: Run Falcon production-path tests + run: snforge test -p openzeppelin_account --release --features falcon_fast_tests falcon_512 + - name: Run tests and generate coverage report run: snforge test --workspace --coverage diff --git a/LICENSE b/LICENSE index cdbf7bcad..edef7d6fb 100644 --- a/LICENSE +++ b/LICENSE @@ -19,3 +19,12 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--- + +Third-party notices + +Portions of this software (the NTT engine and parts of the Falcon-512 verifier) are +ported from s2morrow (https://github.com/feltroidprime/s2morrow and +https://github.com/starkware-bitcoin/s2morrow), +Copyright (c) 2025 StarkWare Industries Ltd., licensed under the MIT License. diff --git a/Scarb.lock b/Scarb.lock index ebba35f2c..1f56f879b 100644 --- a/Scarb.lock +++ b/Scarb.lock @@ -1,6 +1,12 @@ # Code generated by scarb DO NOT EDIT. version = 1 +[[package]] +name = "corelib_imports" +version = "0.1.3" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:1dbf8982484a6a085a143f2352dfbc716261c50c60551eb03bff5f0167260ad9" + [[package]] name = "openzeppelin" version = "4.0.0-alpha.1" @@ -39,6 +45,7 @@ dependencies = [ name = "openzeppelin_account" version = "4.0.0-alpha.1" dependencies = [ + "corelib_imports", "openzeppelin_interfaces", "openzeppelin_introspection", "openzeppelin_test_common", diff --git a/Scarb.toml b/Scarb.toml index 89c6376cc..5c623f7ed 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -44,6 +44,7 @@ keywords = [ [workspace.dependencies] assert_macros = "2.18.0" +corelib_imports = "0.1.2" starknet = "2.18.0" snforge_std = "0.62.1" diff --git a/packages/account/README.md b/packages/account/README.md index c7e1dfa09..4b80df626 100644 --- a/packages/account/README.md +++ b/packages/account/README.md @@ -10,6 +10,16 @@ This crate provides components to implement account contracts that can be used f - `EthAccount` validates transactions from signatures over the [Secp256k1 curve](https://en.bitcoin.it/wiki/Secp256k1). +- `Falcon512ShakeAccount` validates legacy Falcon-512 submission-algorithm signatures + using SHAKE-256 hash-to-point and a verifier-bound product hint. + +- `Falcon512ShakeDirectAccount` validates the corresponding hint-free signatures by + recomputing the polynomial product on-chain. + +> **WARNING:** The Falcon accounts use immutable keys and a contract-specific felt encoding. +> They do not implement a finalized NIST FN-DSA standard. Key loss, key compromise, or a +> verifier revision requires migration to a new account address. + ### Interfaces - [`ISRC6`](https://docs.openzeppelin.com/contracts-cairo/3.x/api/account#ISRC6) diff --git a/packages/account/Scarb.toml b/packages/account/Scarb.toml index c0e826d07..008d9d360 100644 --- a/packages/account/Scarb.toml +++ b/packages/account/Scarb.toml @@ -23,6 +23,7 @@ fmt.workspace = true scarb.workspace = true [dependencies] +corelib_imports.workspace = true starknet.workspace = true openzeppelin_introspection = { path = "../introspection" } openzeppelin_interfaces = { path = "../interfaces" } @@ -36,6 +37,8 @@ openzeppelin_test_common = { path = "../test_common" } [features] fuzzing = [] +# Executes the generated production NTT path; requires the release compiler profile. +falcon_fast_tests = [] [lib] diff --git a/packages/account/src/falcon_512.cairo b/packages/account/src/falcon_512.cairo new file mode 100644 index 000000000..40e505f2b --- /dev/null +++ b/packages/account/src/falcon_512.cairo @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512.cairo) + +//! Falcon-512 SHAKE account contracts and their verification implementation. +//! +//! These accounts implement the legacy Falcon submission algorithm with SHAKE-256 +//! hash-to-point. Their packed felt public keys and signatures are contract-specific; +//! they are not the encoding or finalized behavior of a NIST FN-DSA standard. + +pub(crate) mod account; +pub(crate) mod falcon; +pub mod falcon_512_shake; +pub mod falcon_512_shake_direct; +pub(crate) mod hashing; +pub(crate) mod ntt; +pub(crate) mod packing; +pub(crate) mod zq; +use account::Falcon512SignatureVerifier; + +pub use falcon_512_shake::Falcon512ShakeAccount; +pub use falcon_512_shake_direct::Falcon512ShakeDirectAccount; +use hashing::hash_to_point::hash_to_point_shake_512; + +/// Number of felts in a packed Falcon-512 public key. +pub(crate) const PUBLIC_KEY_FELTS: u32 = 29; + +/// Number of felts in a Falcon-512 signature carrying a product hint. +pub(crate) const SIGNATURE_FELTS: u32 = 60; + +/// Number of felts in a hint-free Falcon-512 signature. +pub(crate) const DIRECT_SIGNATURE_FELTS: u32 = 31; + +/// Verifier for the 60-felt SHAKE-256 signature carrying a polynomial-product hint. +pub(crate) impl Falcon512ShakeVerifier of Falcon512SignatureVerifier { + fn verify(message_hash: felt252, public_key: Span, signature: Span) -> bool { + if public_key.len() != PUBLIC_KEY_FELTS || signature.len() != SIGNATURE_FELTS { + return false; + } + let h_ntt = match packing::unpack_512_u16(public_key) { + Some(value) => value, + None => { return false; }, + }; + let s1 = match packing::unpack_512_u16(signature.slice(0, 29)) { + Some(value) => value, + None => { return false; }, + }; + let salt_a = *signature.at(29); + let salt_b = *signature.at(30); + let mul_hint = match packing::unpack_512_u16(signature.slice(31, 29)) { + Some(value) => value, + None => { return false; }, + }; + let message_point = match hash_to_point_shake_512(message_hash, salt_a, salt_b) { + Some(value) => value, + None => { return false; }, + }; + falcon::verify_512_with_hint_u16( + s1.span(), h_ntt.span(), mul_hint.span(), message_point.span(), + ) + } + + fn is_valid_public_key(public_key: Span) -> bool { + if public_key.len() != PUBLIC_KEY_FELTS { + return false; + } + match packing::unpack_512_u16(public_key) { + Some(_) => true, + None => false, + } + } +} + +/// Verifier for the 31-felt SHAKE-256 signature that recomputes the product on-chain. +pub(crate) impl Falcon512ShakeDirectVerifier of Falcon512SignatureVerifier { + fn verify(message_hash: felt252, public_key: Span, signature: Span) -> bool { + if public_key.len() != PUBLIC_KEY_FELTS || signature.len() != DIRECT_SIGNATURE_FELTS { + return false; + } + let h_ntt = match packing::unpack_512_u16(public_key) { + Some(value) => value, + None => { return false; }, + }; + let s1 = match packing::unpack_512_u16(signature.slice(0, 29)) { + Some(value) => value, + None => { return false; }, + }; + let salt_a = *signature.at(29); + let salt_b = *signature.at(30); + let message_point = match hash_to_point_shake_512(message_hash, salt_a, salt_b) { + Some(value) => value, + None => { return false; }, + }; + falcon::verify_512_direct_u16(s1.span(), h_ntt.span(), message_point.span()) + } + + fn is_valid_public_key(public_key: Span) -> bool { + Falcon512ShakeVerifier::is_valid_public_key(public_key) + } +} diff --git a/packages/account/src/falcon_512/account.cairo b/packages/account/src/falcon_512/account.cairo new file mode 100644 index 000000000..0ba3dfd68 --- /dev/null +++ b/packages/account/src/falcon_512/account.cairo @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/account.cairo) + +//! Shared account component for the Falcon-512 SHAKE account contracts. + +/// Verification surface implemented by each Falcon-512 account variant. +pub(crate) trait Falcon512SignatureVerifier { + /// Returns whether `signature` authenticates `message_hash` under `public_key`. + fn verify(message_hash: felt252, public_key: Span, signature: Span) -> bool; + + /// Returns whether the packed public key has the required canonical encoding. + fn is_valid_public_key(public_key: Span) -> bool; +} + +/// Account component for immutable, array-encoded Falcon-512 public keys. +#[starknet::component] +pub(crate) mod Falcon512AccountComponent { + use core::num::traits::Zero; + use openzeppelin_interfaces::accounts as interface; + use openzeppelin_introspection::src5::SRC5Component; + use openzeppelin_introspection::src5::SRC5Component::InternalTrait as SRC5InternalTrait; + use openzeppelin_utils::execution::execute_single_call; + use starknet::account::Call; + use starknet::storage::{MutableVecTrait, StoragePointerReadAccess, Vec, VecTrait}; + use crate::utils::is_tx_version_valid; + use super::Falcon512SignatureVerifier; + + #[storage] + pub struct Storage { + pub public_key: Vec, + } + + #[event] + #[derive(Drop, Debug, PartialEq, starknet::Event)] + pub enum Event {} + + pub mod Errors { + pub const INVALID_CALLER: felt252 = 'Account: invalid caller'; + pub const INVALID_PUBLIC_KEY: felt252 = 'Account: invalid public key'; + pub const INVALID_SIGNATURE: felt252 = 'Account: invalid signature'; + pub const INVALID_TX_VERSION: felt252 = 'Account: invalid tx version'; + } + + #[embeddable_as(SRC6Impl)] + impl SRC6< + TContractState, + impl Verifier: Falcon512SignatureVerifier, + +HasComponent, + +SRC5Component::HasComponent, + +Drop, + > of interface::ISRC6> { + /// Executes calls forwarded by the account after protocol validation succeeds. + fn __execute__(self: @ComponentState, calls: Array) { + let sender = starknet::get_caller_address(); + assert(sender.is_zero(), Errors::INVALID_CALLER); + assert(is_tx_version_valid(), Errors::INVALID_TX_VERSION); + + for call in calls.span() { + execute_single_call(call); + } + } + + /// Validates an invoke transaction with the current transaction signature. + fn __validate__(self: @ComponentState, calls: Array) -> felt252 { + let _ = calls; + self.validate_transaction::() + } + + /// Verifies a signature for an arbitrary message hash. + fn is_valid_signature( + self: @ComponentState, hash: felt252, signature: Array, + ) -> felt252 { + if Verifier::verify(hash, self.read_public_key().span(), signature.span()) { + starknet::VALIDATED + } else { + 0 + } + } + } + + #[embeddable_as(DeclarerImpl)] + impl Declarer< + TContractState, + impl Verifier: Falcon512SignatureVerifier, + +HasComponent, + +SRC5Component::HasComponent, + +Drop, + > of interface::IDeclarer> { + /// Validates a declare transaction with the current transaction signature. + fn __validate_declare__( + self: @ComponentState, class_hash: felt252, + ) -> felt252 { + let _ = class_hash; + self.validate_transaction::() + } + } + + #[embeddable_as(DeployableImpl)] + impl Deployable< + TContractState, + impl Verifier: Falcon512SignatureVerifier, + +HasComponent, + +SRC5Component::HasComponent, + +Drop, + > of interface::IFeltArrayDeployable> { + /// Validates a deploy-account transaction with the current transaction signature. + fn __validate_deploy__( + self: @ComponentState, + class_hash: felt252, + contract_address_salt: felt252, + public_key: Array, + ) -> felt252 { + let _ = class_hash; + let _ = contract_address_salt; + let _ = public_key; + self.validate_transaction::() + } + } + + #[embeddable_as(PublicKeyImpl)] + impl PublicKey< + TContractState, + +HasComponent, + +SRC5Component::HasComponent, + +Drop, + > of interface::IFeltArrayPublicKey> { + /// Returns the immutable packed Falcon-512 public key. + fn get_public_key(self: @ComponentState) -> Array { + self.read_public_key() + } + } + + #[generate_trait] + pub impl InternalImpl< + TContractState, + +HasComponent, + impl SRC5: SRC5Component::HasComponent, + +Drop, + > of InternalTrait { + /// Validates and stores the immutable packed public key and registers SRC6 support. + fn initializer( + ref self: ComponentState, public_key: Array, + ) { + assert(Verifier::is_valid_public_key(public_key.span()), Errors::INVALID_PUBLIC_KEY); + + let mut src5_component = get_dep_component_mut!(ref self, SRC5); + src5_component.register_interface(interface::ISRC6_ID); + + for felt in public_key { + self.public_key.push(felt); + } + } + + /// Validates the transaction hash against the transaction signature. + fn validate_transaction( + self: @ComponentState, + ) -> felt252 { + let tx_info = starknet::get_tx_info().unbox(); + assert( + Verifier::verify( + tx_info.transaction_hash, self.read_public_key().span(), tx_info.signature, + ), + Errors::INVALID_SIGNATURE, + ); + starknet::VALIDATED + } + + /// Reads the stored public key into its 29-felt packed representation. + fn read_public_key(self: @ComponentState) -> Array { + let mut public_key = array![]; + let len = self.public_key.len(); + let mut index = 0; + while index != len { + public_key.append(self.public_key.at(index).read()); + index += 1; + } + public_key + } + } +} diff --git a/packages/account/src/falcon_512/falcon.cairo b/packages/account/src/falcon_512/falcon.cairo new file mode 100644 index 000000000..7b4687f05 --- /dev/null +++ b/packages/account/src/falcon_512/falcon.cairo @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/falcon.cairo) + +//! Falcon-512 signature verification. +//! +//! Hint variant: verifies `s1 * h == mul_hint` via two forward NTTs and a pointwise +//! product check (the NTT is a bijection, so the check binds `mul_hint` to exactly +//! `s1 * h mod (q, phi)`), then accepts iff +//! `||msg_point - mul_hint||^2 + ||s1||^2 <= SIG_BOUND_512` over centered +//! representatives. Since `msg_point - s1*h = s0`, this is the Falcon verification +//! equation with the polynomial multiplication delegated to a signer-supplied hint; +//! a bad hint yields `false`. Both transforms use the generated fixed-parameter +//! Falcon-512 path, and the pointwise check tests `s1n*h == hn (mod q)`. +//! +//! Direct variant: computes `s1 * h` on-chain as `INTT(NTT(s1) ∘ h_ntt)` — the +//! generated forward transform's reduced pointwise products feed the generic inverse +//! engine with their exact bound. +//! +//! The verifier path decodes packed coefficients into canonical `u16` values and keeps +//! that representation through the generated NTT and norm calculation. The public core +//! functions also accept validated felt coefficients for standalone callers. + +use super::ntt::engine::intt; +#[cfg(test)] +#[cfg(not(feature: 'falcon_fast_tests'))] +use super::ntt::engine::ntt; +use super::ntt::falcon512::{PRODUCT_BITS, PRODUCT_BOUND_FELT, config}; +#[cfg(not(test))] +use super::ntt::falcon512_fast::ntt_falcon512_fast_u16_unchecked; +#[cfg(test)] +#[cfg(feature: 'falcon_fast_tests')] +use super::ntt::falcon512_fast::ntt_falcon512_fast_u16_unchecked; +use super::zq::{Q32, center_sq, centered_difference_sq}; + +/// Maximum allowed `||s0||^2 + ||s1||^2` for the legacy Falcon-512 submission algorithm. +pub const SIG_BOUND_512: u64 = 34034726; + +/// The Falcon modulus as a u32 divisor. +const Q32_NZ: NonZero = 12289; + +#[cfg(not(test))] +#[inline(always)] +fn forward_ntt(values: Span) -> Array { + ntt_falcon512_fast_u16_unchecked(values) +} + +#[cfg(test)] +#[cfg(feature: 'falcon_fast_tests')] +#[inline(always)] +fn forward_ntt(values: Span) -> Array { + ntt_falcon512_fast_u16_unchecked(values) +} + +// The repository's coverage profile disables inlining. The universal Sierra compiler +// cannot currently lower the generated 512-value straight-line transform in that profile, +// so contract tests use the equivalent generic engine. The generated production path is +// exercised in release mode with the `falcon_fast_tests` feature. +#[cfg(test)] +#[cfg(not(feature: 'falcon_fast_tests'))] +fn forward_ntt(mut values: Span) -> Array { + let mut input = array![]; + while let Some(value) = values.pop_front() { + input.append((*value).into()); + } + let mut transformed = ntt(input.span(), @config()).span(); + let mut output = array![]; + while let Some(value) = transformed.pop_front() { + output.append((*value).try_into().unwrap()); + } + output +} + +#[inline(always)] +fn felts_to_u16(mut values: Span) -> Array { + let mut out: Array = array![]; + while let Some(value) = values.pop_front() { + out.append((*value).try_into().unwrap()); + } + out +} + +#[inline(always)] +fn product_difference(a: u16, b: u16, c: u16) -> u32 { + let a: u32 = a.into(); + let b: u32 = b.into(); + let c: u32 = c.into(); + a * b + Q32 - c +} + +#[inline(always)] +fn product_as_felt(a: u16, b: u16) -> felt252 { + let a: u32 = a.into(); + let b: u32 = b.into(); + (a * b).into() +} + +/// Verify with a signer-supplied product hint. All spans must have length 512 with +/// coefficients in `[0, Q)` — guaranteed by `packing::unpack_512` and +/// `hash_to_point::hash_to_point_512`. +pub fn verify_512_with_hint( + s1: Span, h_ntt: Span, mul_hint: Span, msg_point: Span, +) -> bool { + assert(s1.len() == 512, 's1 must be 512 coeffs'); + assert(h_ntt.len() == 512, 'h_ntt must be 512 coeffs'); + assert(mul_hint.len() == 512, 'mul_hint must be 512 coeffs'); + assert(msg_point.len() == 512, 'msg_point must be 512 coeffs'); + + let s1 = felts_to_u16(s1); + let h_ntt = felts_to_u16(h_ntt); + let mul_hint = felts_to_u16(mul_hint); + verify_512_with_hint_u16(s1.span(), h_ntt.span(), mul_hint.span(), msg_point) +} + +/// Verify the hint equation over canonical `u16` coefficients. +pub(crate) fn verify_512_with_hint_u16( + s1: Span, h_ntt: Span, mul_hint: Span, msg_point: Span, +) -> bool { + assert(s1.len() == 512, 's1 must be 512 coeffs'); + assert(h_ntt.len() == 512, 'h_ntt must be 512 coeffs'); + assert(mul_hint.len() == 512, 'mul_hint must be 512 coeffs'); + assert(msg_point.len() == 512, 'msg_point must be 512 coeffs'); + + // Both spans came from canonical base-Q unpacking (or the public wrapper's documented + // canonical-coefficient precondition), so the generated unchecked NTT is sound here. + let s1_ntt = forward_ntt(s1); + let hint_ntt = forward_ntt(mul_hint); + + let mut s1_ntt_iter = s1_ntt.span(); + let mut hint_ntt_iter = hint_ntt.span(); + let mut h_iter = h_ntt; + let mut msg_iter = msg_point; + let mut hint_iter = mul_hint; + let mut s1_iter = s1; + + // Fused pass, 16-way unrolled (512 = 16 * 32, no remainder): hint check and + // centered-norm accumulation per coefficient. + // Max acc = 512 * 2 * 6144^2 < 2^36, so the felt252 accumulator cannot wrap + // and always fits u64. + let mut acc: felt252 = 0; + let mut ok = true; + while let Some(s1n_c) = s1_ntt_iter.multi_pop_front::<16>() { + let [ + s1n0, + s1n1, + s1n2, + s1n3, + s1n4, + s1n5, + s1n6, + s1n7, + s1n8, + s1n9, + s1n10, + s1n11, + s1n12, + s1n13, + s1n14, + s1n15, + ] = + (*s1n_c) + .unbox(); + let [hn0, hn1, hn2, hn3, hn4, hn5, hn6, hn7, hn8, hn9, hn10, hn11, hn12, hn13, hn14, hn15] = + (*hint_ntt_iter + .multi_pop_front::<16>() + .unwrap()) + .unbox(); + let [h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11, h12, h13, h14, h15] = (*h_iter + .multi_pop_front::<16>() + .unwrap()) + .unbox(); + // s1n*h + q - hn has zero remainder iff the two NTT coordinates agree mod q, + // which by bijectivity binds mul_hint to s1*h. + let (_, r0) = DivRem::div_rem(product_difference(s1n0, h0, hn0), Q32_NZ); + let (_, r1) = DivRem::div_rem(product_difference(s1n1, h1, hn1), Q32_NZ); + let (_, r2) = DivRem::div_rem(product_difference(s1n2, h2, hn2), Q32_NZ); + let (_, r3) = DivRem::div_rem(product_difference(s1n3, h3, hn3), Q32_NZ); + let (_, r4) = DivRem::div_rem(product_difference(s1n4, h4, hn4), Q32_NZ); + let (_, r5) = DivRem::div_rem(product_difference(s1n5, h5, hn5), Q32_NZ); + let (_, r6) = DivRem::div_rem(product_difference(s1n6, h6, hn6), Q32_NZ); + let (_, r7) = DivRem::div_rem(product_difference(s1n7, h7, hn7), Q32_NZ); + let (_, r8) = DivRem::div_rem(product_difference(s1n8, h8, hn8), Q32_NZ); + let (_, r9) = DivRem::div_rem(product_difference(s1n9, h9, hn9), Q32_NZ); + let (_, r10) = DivRem::div_rem(product_difference(s1n10, h10, hn10), Q32_NZ); + let (_, r11) = DivRem::div_rem(product_difference(s1n11, h11, hn11), Q32_NZ); + let (_, r12) = DivRem::div_rem(product_difference(s1n12, h12, hn12), Q32_NZ); + let (_, r13) = DivRem::div_rem(product_difference(s1n13, h13, hn13), Q32_NZ); + let (_, r14) = DivRem::div_rem(product_difference(s1n14, h14, hn14), Q32_NZ); + let (_, r15) = DivRem::div_rem(product_difference(s1n15, h15, hn15), Q32_NZ); + // Remainders are non-negative, so the sum is zero iff all sixteen are. + if r0 + + r1 + + r2 + + r3 + + r4 + + r5 + + r6 + + r7 + + r8 + + r9 + + r10 + + r11 + + r12 + + r13 + + r14 + + r15 != 0 { + ok = false; + break; + } + let [m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15] = (*msg_iter + .multi_pop_front::<16>() + .unwrap()) + .unbox(); + let [t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15] = (*hint_iter + .multi_pop_front::<16>() + .unwrap()) + .unbox(); + let [v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15] = (*s1_iter + .multi_pop_front::<16>() + .unwrap()) + .unbox(); + acc += centered_difference_sq(m0, t0) + center_sq(v0); + acc += centered_difference_sq(m1, t1) + center_sq(v1); + acc += centered_difference_sq(m2, t2) + center_sq(v2); + acc += centered_difference_sq(m3, t3) + center_sq(v3); + acc += centered_difference_sq(m4, t4) + center_sq(v4); + acc += centered_difference_sq(m5, t5) + center_sq(v5); + acc += centered_difference_sq(m6, t6) + center_sq(v6); + acc += centered_difference_sq(m7, t7) + center_sq(v7); + acc += centered_difference_sq(m8, t8) + center_sq(v8); + acc += centered_difference_sq(m9, t9) + center_sq(v9); + acc += centered_difference_sq(m10, t10) + center_sq(v10); + acc += centered_difference_sq(m11, t11) + center_sq(v11); + acc += centered_difference_sq(m12, t12) + center_sq(v12); + acc += centered_difference_sq(m13, t13) + center_sq(v13); + acc += centered_difference_sq(m14, t14) + center_sq(v14); + acc += centered_difference_sq(m15, t15) + center_sq(v15); + } + if !ok { + return false; + } + let norm: u64 = acc.try_into().unwrap(); + norm <= SIG_BOUND_512 +} + +/// Verify without a hint (direct variant): compute `s1 * h` on-chain as +/// `INTT(NTT(s1) ∘ h_ntt)` — 1 NTT + 1 INTT because the public key is already stored in +/// the NTT domain — then accept iff `||msg_point - s1*h||^2 + ||s1||^2 <= SIG_BOUND_512`. +/// Same trust surface as the textbook equation (no signer-supplied hint), 29 fewer +/// signature felts than [`verify_512_with_hint`], at the cost of the INTT. +pub fn verify_512_direct(s1: Span, h_ntt: Span, msg_point: Span) -> bool { + assert(s1.len() == 512, 's1 must be 512 coeffs'); + assert(h_ntt.len() == 512, 'h_ntt must be 512 coeffs'); + assert(msg_point.len() == 512, 'msg_point must be 512 coeffs'); + + let s1 = felts_to_u16(s1); + let h_ntt = felts_to_u16(h_ntt); + verify_512_direct_u16(s1.span(), h_ntt.span(), msg_point) +} + +/// Verify the direct equation over canonical `u16` coefficients. +pub(crate) fn verify_512_direct_u16(s1: Span, h_ntt: Span, msg_point: Span) -> bool { + assert(s1.len() == 512, 's1 must be 512 coeffs'); + assert(h_ntt.len() == 512, 'h_ntt must be 512 coeffs'); + assert(msg_point.len() == 512, 'msg_point must be 512 coeffs'); + + let cfg = config(); + // `s1` is canonical by the same verifier/wrapper precondition as the hint path. + let s1_ntt = forward_ntt(s1); + + // Pointwise products of two reduced inputs are below q^2. They feed the inverse + // engine unreduced under its exact product bound. + let mut prods: Array = array![]; + let mut s1n_iter = s1_ntt.span(); + let mut h_iter = h_ntt; + while let Some(s1n_c) = s1n_iter.multi_pop_front::<16>() { + let [ + s1n0, + s1n1, + s1n2, + s1n3, + s1n4, + s1n5, + s1n6, + s1n7, + s1n8, + s1n9, + s1n10, + s1n11, + s1n12, + s1n13, + s1n14, + s1n15, + ] = + (*s1n_c) + .unbox(); + let [h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11, h12, h13, h14, h15] = (*h_iter + .multi_pop_front::<16>() + .unwrap()) + .unbox(); + prods.append(product_as_felt(s1n0, h0)); + prods.append(product_as_felt(s1n1, h1)); + prods.append(product_as_felt(s1n2, h2)); + prods.append(product_as_felt(s1n3, h3)); + prods.append(product_as_felt(s1n4, h4)); + prods.append(product_as_felt(s1n5, h5)); + prods.append(product_as_felt(s1n6, h6)); + prods.append(product_as_felt(s1n7, h7)); + prods.append(product_as_felt(s1n8, h8)); + prods.append(product_as_felt(s1n9, h9)); + prods.append(product_as_felt(s1n10, h10)); + prods.append(product_as_felt(s1n11, h11)); + prods.append(product_as_felt(s1n12, h12)); + prods.append(product_as_felt(s1n13, h13)); + prods.append(product_as_felt(s1n14, h14)); + prods.append(product_as_felt(s1n15, h15)); + } + let s1h = intt(prods.span(), PRODUCT_BITS, PRODUCT_BOUND_FELT, @cfg); + + let mut s1h_iter = s1h.span(); + let mut msg_iter = msg_point; + let mut s1_iter = s1; + // Norm pass, 16-way unrolled (512 = 16 * 32, no remainder). + // Same accumulator bound argument as in `verify_512_with_hint`. + let mut acc: felt252 = 0; + while let Some(pc) = s1h_iter.multi_pop_front::<16>() { + let [p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15] = (*pc).unbox(); + let [m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15] = (*msg_iter + .multi_pop_front::<16>() + .unwrap()) + .unbox(); + let [v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15] = (*s1_iter + .multi_pop_front::<16>() + .unwrap()) + .unbox(); + // INTT outputs are reduced, so their u16 downcasts cannot fail. + acc += centered_difference_sq(m0, p0.try_into().unwrap()) + center_sq(v0); + acc += centered_difference_sq(m1, p1.try_into().unwrap()) + center_sq(v1); + acc += centered_difference_sq(m2, p2.try_into().unwrap()) + center_sq(v2); + acc += centered_difference_sq(m3, p3.try_into().unwrap()) + center_sq(v3); + acc += centered_difference_sq(m4, p4.try_into().unwrap()) + center_sq(v4); + acc += centered_difference_sq(m5, p5.try_into().unwrap()) + center_sq(v5); + acc += centered_difference_sq(m6, p6.try_into().unwrap()) + center_sq(v6); + acc += centered_difference_sq(m7, p7.try_into().unwrap()) + center_sq(v7); + acc += centered_difference_sq(m8, p8.try_into().unwrap()) + center_sq(v8); + acc += centered_difference_sq(m9, p9.try_into().unwrap()) + center_sq(v9); + acc += centered_difference_sq(m10, p10.try_into().unwrap()) + center_sq(v10); + acc += centered_difference_sq(m11, p11.try_into().unwrap()) + center_sq(v11); + acc += centered_difference_sq(m12, p12.try_into().unwrap()) + center_sq(v12); + acc += centered_difference_sq(m13, p13.try_into().unwrap()) + center_sq(v13); + acc += centered_difference_sq(m14, p14.try_into().unwrap()) + center_sq(v14); + acc += centered_difference_sq(m15, p15.try_into().unwrap()) + center_sq(v15); + } + let norm: u64 = acc.try_into().unwrap(); + norm <= SIG_BOUND_512 +} + +#[cfg(test)] +mod tests { + use super::super::ntt::engine::{intt, ntt}; + use super::super::ntt::falcon512::{PRODUCT_BITS, PRODUCT_BOUND_FELT, config}; + use super::super::zq::add_mod; + use super::{verify_512_direct, verify_512_with_hint}; + + fn to_u16(mut vals: Span) -> Array { + let mut out: Array = array![]; + while let Some(v) = vals.pop_front() { + out.append((*v).try_into().unwrap()); + } + out + } + + /// Negacyclic product in Z_q[x]/(x^512 + 1) (test helper). + fn mul_zq(f: Span, g: Span) -> Array { + let cfg = config(); + let f_ntt = ntt(f, @cfg); + let g_ntt = ntt(g, @cfg); + let mut prods: Array = array![]; + let mut fi = f_ntt.span(); + let mut gi = g_ntt.span(); + while let Some(a) = fi.pop_front() { + prods.append(*a * *gi.pop_front().unwrap()); + } + intt(prods.span(), PRODUCT_BITS, PRODUCT_BOUND_FELT, @cfg) + } + + fn pseudorandom_coeffs(seed: u64) -> Array { + let mut f: Array = array![]; + let mut state: u64 = seed; + for _ in 0_u32..512 { + state = (state * 1664525 + 1013904223) % 0x100000000; + f.append((state % 12289).into()); + } + f + } + + fn small_coeffs(seed: u64) -> Array { + // Centered coefficients in [-16, 16]: comfortably inside the norm bound. + let mut f: Array = array![]; + let mut state: u64 = seed; + for _ in 0_u32..512 { + state = (state * 1664525 + 1013904223) % 0x100000000; + let centered = state % 33; + if centered <= 16 { + f.append(centered.into()); + } else { + f.append((12289 - (centered - 16)).into()); + } + } + f + } + + /// msg_point = s0 + prod coefficient-wise, downcast to the norm side's u16 form. + fn add_points(s0: Span, prod: Span) -> Array { + let mut out: Array = array![]; + let mut s0_iter = to_u16(s0).span(); + let mut prod_iter = to_u16(prod).span(); + while let Some(a) = s0_iter.pop_front() { + out.append(add_mod(*a, *prod_iter.pop_front().unwrap())); + } + out + } + + fn norm_vector(a: u16, b: u16, c: u16, d: u16) -> Array { + let mut values: Array = array![a.into(), b.into(), c.into(), d.into()]; + while values.len() != 512 { + values.append(0); + } + values + } + + // Synthetic instance built from the verification equation itself: pick small s1 and + // small s0, any h, set mul_hint = s1*h and msg_point = s0 + s1*h. A correct verifier + // must accept it, and reject once the hint or the norm side is broken. + #[test] + fn test_verify_synthetic_instance() { + let s1 = small_coeffs(1); + let s0 = small_coeffs(2); + let h = pseudorandom_coeffs(3); + let h_ntt = ntt(h.span(), @config()); + let mul_hint = mul_zq(s1.span(), h.span()); + let msg_point = add_points(s0.span(), mul_hint.span()); + assert!(verify_512_with_hint(s1.span(), h_ntt.span(), mul_hint.span(), msg_point.span())); + } + + #[test] + fn test_verify_rejects_bad_hint() { + let s1 = small_coeffs(1); + let h = pseudorandom_coeffs(3); + let h_ntt = ntt(h.span(), @config()); + let good_hint = mul_zq(s1.span(), h.span()); + // Flip one coefficient of the hint. + let mut hint_iter = good_hint.span(); + let first: u16 = (*hint_iter.pop_front().unwrap()).try_into().unwrap(); + let mut tampered: Array = array![((first + 1) % 12289).into()]; + while let Some(c) = hint_iter.pop_front() { + tampered.append(*c); + } + // msg_point = mul_hint would give norm ||s1||^2 only — accepted if hint passed. + let msg_point = to_u16(mul_zq(s1.span(), h.span()).span()); + assert!(!verify_512_with_hint(s1.span(), h_ntt.span(), tampered.span(), msg_point.span())); + } + + // The direct variant must agree with the hint variant on the same instances. + #[test] + fn test_verify_direct_synthetic_instance() { + let s1 = small_coeffs(1); + let s0 = small_coeffs(2); + let h = pseudorandom_coeffs(3); + let h_ntt = ntt(h.span(), @config()); + let prod = mul_zq(s1.span(), h.span()); + let msg_point = add_points(s0.span(), prod.span()); + assert!(verify_512_direct(s1.span(), h_ntt.span(), msg_point.span())); + // Perturb one msg_point coefficient far from the lattice point: norm blows up. + let mut bad_msg: Array = array![add_mod(*msg_point.span().at(0), 6144)]; + let mut rest = msg_point.span().slice(1, 511); + while let Some(c) = rest.pop_front() { + bad_msg.append(*c); + } + assert!(!verify_512_direct(s1.span(), h_ntt.span(), bad_msg.span())); + } + + #[test] + fn test_verify_direct_rejects_large_norm() { + let s1 = pseudorandom_coeffs(9); + let h = pseudorandom_coeffs(3); + let h_ntt = ntt(h.span(), @config()); + // msg_point = s1*h so s0 = 0; ||s1||^2 alone is far above the bound. + let msg_point = to_u16(mul_zq(s1.span(), h.span()).span()); + assert!(!verify_512_direct(s1.span(), h_ntt.span(), msg_point.span())); + } + + #[test] + fn test_verify_rejects_large_norm() { + // s1 uniformly random in [0, Q): astronomically above the centered-norm bound, + // with a consistent hint and msg_point = s1*h (so s0 = 0). + let s1 = pseudorandom_coeffs(9); + let h = pseudorandom_coeffs(3); + let h_ntt = ntt(h.span(), @config()); + let mul_hint = mul_zq(s1.span(), h.span()); + let msg_point = to_u16(mul_hint.span()); + assert!(!verify_512_with_hint(s1.span(), h_ntt.span(), mul_hint.span(), msg_point.span())); + } + + #[test] + fn test_verify_accepts_exact_norm_bound_and_rejects_next_value() { + // 34,034,726 = 5833² + 89² + 54². + let at_bound = norm_vector(5833, 89, 54, 0); + // 34,034,727 = 5833² + 103² + 15² + 2². + let over_bound = norm_vector(5833, 103, 15, 2); + let zeros = norm_vector(0, 0, 0, 0); + let message_point = to_u16(zeros.span()); + + assert!( + verify_512_with_hint(at_bound.span(), zeros.span(), zeros.span(), message_point.span()), + ); + assert!(verify_512_direct(at_bound.span(), zeros.span(), message_point.span())); + assert!( + !verify_512_with_hint( + over_bound.span(), zeros.span(), zeros.span(), message_point.span(), + ), + ); + assert!(!verify_512_direct(over_bound.span(), zeros.span(), message_point.span())); + } +} diff --git a/packages/account/src/falcon_512/falcon_512_shake.cairo b/packages/account/src/falcon_512/falcon_512_shake.cairo new file mode 100644 index 000000000..6bb13c2c5 --- /dev/null +++ b/packages/account/src/falcon_512/falcon_512_shake.cairo @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/falcon_512_shake.cairo) + +/// # Falcon-512 SHAKE Account +/// +/// Immutable account using the legacy Falcon-512 submission algorithm's SHAKE-256 +/// hash-to-point and a 60-felt signature with a signer-supplied, verifier-bound product +/// hint. The felt encoding is specific to this contract and is not a standardized FN-DSA +/// signature format. +#[starknet::contract(account)] +pub mod Falcon512ShakeAccount { + use openzeppelin_introspection::src5::SRC5Component; + use super::super::Falcon512ShakeVerifier; + use super::super::account::Falcon512AccountComponent; + + component!(path: Falcon512AccountComponent, storage: account, event: AccountEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + #[abi(embed_v0)] + impl SRC6Impl = + Falcon512AccountComponent::SRC6Impl; + #[abi(embed_v0)] + impl DeclarerImpl = + Falcon512AccountComponent::DeclarerImpl; + #[abi(embed_v0)] + impl DeployableImpl = + Falcon512AccountComponent::DeployableImpl; + #[abi(embed_v0)] + impl PublicKeyImpl = Falcon512AccountComponent::PublicKeyImpl; + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; + + impl AccountInternalImpl = Falcon512AccountComponent::InternalImpl; + + #[storage] + pub struct Storage { + #[substorage(v0)] + pub account: Falcon512AccountComponent::Storage, + #[substorage(v0)] + pub src5: SRC5Component::Storage, + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + AccountEvent: Falcon512AccountComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event, + } + + /// Initializes the account with a canonical 29-felt, NTT-domain public key. + #[constructor] + pub fn constructor(ref self: ContractState, public_key: Array) { + self.account.initializer::(public_key); + } +} diff --git a/packages/account/src/falcon_512/falcon_512_shake_direct.cairo b/packages/account/src/falcon_512/falcon_512_shake_direct.cairo new file mode 100644 index 000000000..ead722759 --- /dev/null +++ b/packages/account/src/falcon_512/falcon_512_shake_direct.cairo @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 +// (account/src/falcon_512/falcon_512_shake_direct.cairo) + +/// # Falcon-512 SHAKE Direct Account +/// +/// Immutable account using the legacy Falcon-512 submission algorithm's SHAKE-256 +/// hash-to-point and a 31-felt signature. It recomputes the polynomial product on-chain +/// instead of accepting a signer-supplied hint. The felt encoding is specific to this +/// contract and is not a standardized FN-DSA signature format. +#[starknet::contract(account)] +pub mod Falcon512ShakeDirectAccount { + use openzeppelin_introspection::src5::SRC5Component; + use super::super::Falcon512ShakeDirectVerifier; + use super::super::account::Falcon512AccountComponent; + + component!(path: Falcon512AccountComponent, storage: account, event: AccountEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + #[abi(embed_v0)] + impl SRC6Impl = + Falcon512AccountComponent::SRC6Impl; + #[abi(embed_v0)] + impl DeclarerImpl = + Falcon512AccountComponent::DeclarerImpl; + #[abi(embed_v0)] + impl DeployableImpl = + Falcon512AccountComponent::DeployableImpl; + #[abi(embed_v0)] + impl PublicKeyImpl = Falcon512AccountComponent::PublicKeyImpl; + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; + + impl AccountInternalImpl = Falcon512AccountComponent::InternalImpl; + + #[storage] + pub struct Storage { + #[substorage(v0)] + pub account: Falcon512AccountComponent::Storage, + #[substorage(v0)] + pub src5: SRC5Component::Storage, + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + AccountEvent: Falcon512AccountComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event, + } + + /// Initializes the account with a canonical 29-felt, NTT-domain public key. + #[constructor] + pub fn constructor(ref self: ContractState, public_key: Array) { + self.account.initializer::(public_key); + } +} diff --git a/packages/account/src/falcon_512/hashing.cairo b/packages/account/src/falcon_512/hashing.cairo new file mode 100644 index 000000000..7c0e98c3f --- /dev/null +++ b/packages/account/src/falcon_512/hashing.cairo @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/hashing.cairo) + +//! SHAKE-256 primitives used to derive Falcon-512 message points. + +pub(crate) mod hash_to_point; +pub(crate) mod shake256; diff --git a/packages/account/src/falcon_512/hashing/hash_to_point.cairo b/packages/account/src/falcon_512/hashing/hash_to_point.cairo new file mode 100644 index 000000000..73e54d35c --- /dev/null +++ b/packages/account/src/falcon_512/hashing/hash_to_point.cairo @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 +// (account/src/falcon_512/hashing/hash_to_point.cairo) + +//! SHAKE-256 hash-to-point for the legacy Falcon-512 submission algorithm. +//! +//! The function absorbs a 40-byte salt followed by the message hash's 32-byte +//! little-endian representation. It reads the SHAKE-256 output as big-endian u16 words +//! and rejection-samples each word into Z_q until it has 512 coefficients. + +use super::shake256::keccak_f1600; + +/// Rejection bound: the largest multiple of q = 12289 below 2^16. +const REJECT_BOUND: u32 = 61445; + +/// Each salt felt must fit in 20 bytes. +const TWO_POW_160: u256 = 0x10000000000000000000000000000000000000000; + +/// Hashes `(message_hash, salt)` to 512 coefficients in `[0, 12289)`. +/// +/// Returns `None` if either salt felt exceeds 20 bytes. +pub(crate) fn hash_to_point_shake_512( + message_hash: felt252, salt_a: felt252, salt_b: felt252, +) -> Option> { + let sa: u256 = salt_a.into(); + let sb: u256 = salt_b.into(); + if sa >= TWO_POW_160 || sb >= TWO_POW_160 { + return None; + } + let mh: u256 = message_hash.into(); + + // The 72-byte input fits one SHAKE-256 rate block. Bytes 0..40 hold the salt, + // bytes 40..72 hold the message hash, byte 72 is the SHAKE domain byte, and byte + // 135 is the pad10*1 terminator. + let two64: NonZero = 0x10000000000000000_u128.try_into().unwrap(); + let two32: NonZero = 0x100000000_u128.try_into().unwrap(); + let (a1, a0) = DivRem::div_rem(sa.low, two64); + let (sbq, sb0) = DivRem::div_rem(sb.low, two32); + let (sb12, l3) = DivRem::div_rem(sbq, two64); + let (m1, m0) = DivRem::div_rem(mh.low, two64); + let (m3, m2) = DivRem::div_rem(mh.high, two64); + let l2: u128 = (sa.high.into() + sb0.into() * 0x100000000).try_into().unwrap(); + let l4: u128 = (sb12.into() + sb.high.into() * 0x100000000).try_into().unwrap(); + + let mut state = keccak_f1600( + [ + a0, a1, l2, l3, l4, m0, m1, m2, m3, 0x1f, 0, 0, 0, 0, 0, 0, 0x8000000000000000, 0, 0, 0, + 0, 0, 0, 0, 0, + ], + ); + + let q32: NonZero = 12289_u32.try_into().unwrap(); + let two16: NonZero = 0x10000_u64.try_into().unwrap(); + let b256: NonZero = 0x100_u64.try_into().unwrap(); + let mut coeffs: Array = array![]; + loop { + let mut lanes = state.span().slice(0, 17); + while let Some(lane) = lanes.pop_front() { + push_lane_words(*lane, two16, b256, q32, ref coeffs); + } + if coeffs.len() == 512 { + break; + } + state = keccak_f1600(state); + } + Some(coeffs) +} + +/// Reads one squeezed lane as four big-endian u16 candidate words. +#[inline(always)] +fn push_lane_words( + lane: u128, two16: NonZero, b256: NonZero, q32: NonZero, ref coeffs: Array, +) { + let lane64: u64 = lane.try_into().unwrap(); + let (rest, c0) = DivRem::div_rem(lane64, two16); + let (rest, c1) = DivRem::div_rem(rest, two16); + let (c3, c2) = DivRem::div_rem(rest, two16); + + let (b1, b0) = DivRem::div_rem(c0, b256); + push_candidate((b0 * 256 + b1).try_into().unwrap(), q32, ref coeffs); + let (b1, b0) = DivRem::div_rem(c1, b256); + push_candidate((b0 * 256 + b1).try_into().unwrap(), q32, ref coeffs); + let (b1, b0) = DivRem::div_rem(c2, b256); + push_candidate((b0 * 256 + b1).try_into().unwrap(), q32, ref coeffs); + let (b1, b0) = DivRem::div_rem(c3, b256); + push_candidate((b0 * 256 + b1).try_into().unwrap(), q32, ref coeffs); +} + +/// Appends `candidate mod q` when the word lies below the rejection bound and the +/// output is not complete. +#[inline(always)] +fn push_candidate(candidate: u32, q32: NonZero, ref coeffs: Array) { + if coeffs.len() != 512 && candidate < REJECT_BOUND { + let (_, remainder) = DivRem::div_rem(candidate, q32); + coeffs.append(remainder.try_into().unwrap()); + } +} + +#[cfg(test)] +mod tests { + use super::{REJECT_BOUND, TWO_POW_160, hash_to_point_shake_512, push_candidate}; + + #[test] + fn test_hash_to_point_reference_vector() { + // Independently generated with Python hashlib.shake_256 over + // salt_a[20 LE] || salt_b[20 LE] || message_hash[32 LE]. + let coefficients = hash_to_point_shake_512(3, 1, 2).unwrap(); + assert_eq!(coefficients.len(), 512); + assert_eq!( + coefficients.span().slice(0, 8), + [2750, 10132, 11472, 1877, 11061, 11208, 12103, 6446].span(), + ); + assert_eq!(coefficients.span().slice(254, 6), [937, 8133, 7551, 334, 4678, 4603].span()); + assert_eq!( + coefficients.span().slice(504, 8), + [5448, 3366, 6941, 8171, 6950, 4708, 3646, 5435].span(), + ); + } + + #[test] + fn test_hash_to_point_domain_separation() { + let base = hash_to_point_shake_512(3, 1, 2).unwrap(); + assert_ne!(base.span(), hash_to_point_shake_512(4, 1, 2).unwrap().span()); + assert_ne!(base.span(), hash_to_point_shake_512(3, 2, 1).unwrap().span()); + assert_ne!(base.span(), hash_to_point_shake_512(3, 1, 3).unwrap().span()); + } + + #[test] + fn test_hash_to_point_salt_bounds() { + let too_big: felt252 = TWO_POW_160.try_into().unwrap(); + assert!(hash_to_point_shake_512(3, too_big, 0).is_none()); + assert!(hash_to_point_shake_512(3, 0, too_big).is_none()); + assert!(hash_to_point_shake_512(3, too_big - 1, too_big - 1).is_some()); + } + + #[test] + fn test_push_candidate_accept_reject_and_complete_branches() { + let q32: NonZero = 12289_u32.try_into().unwrap(); + let mut coefficients: Array = array![]; + + push_candidate(12290, q32, ref coefficients); + assert_eq!(coefficients.span(), [1].span()); + + push_candidate(REJECT_BOUND, q32, ref coefficients); + assert_eq!(coefficients.len(), 1); + + while coefficients.len() != 512 { + coefficients.append(0); + } + push_candidate(1, q32, ref coefficients); + assert_eq!(coefficients.len(), 512); + } +} diff --git a/packages/account/src/falcon_512/hashing/shake256.cairo b/packages/account/src/falcon_512/hashing/shake256.cairo new file mode 100644 index 000000000..abe83da92 --- /dev/null +++ b/packages/account/src/falcon_512/hashing/shake256.cairo @@ -0,0 +1,334 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/hashing/shake256.cairo) + +//! SHAKE-256 extendable-output function (FIPS 202), implemented in pure Cairo. +//! +//! Written from the FIPS 202 specification (Keccak-f[1600] sponge, rate 1088 bits = +//! 136 bytes, capacity 512, domain-separation `0x1F`, pad10*1). Starknet exposes no +//! SHAKE primitive and its `keccak` syscall bakes in keccak256's `0x01` padding and a +//! fixed 256-bit output, so neither is reusable here; the permutation is implemented +//! directly. A future `keccak_f1600` syscall (SNIP-32) would replace [`keccak_f1600`] +//! and collapse the cost. +//! +//! State: 25 lanes of 64 bits, `lane[x + 5y]`, bytes mapped little-endian per lane. +//! +//! The permutation dominates on-chain cost, so it is written flat: +//! - each round is a straight-line expression over 25 lane locals — no round-local +//! arrays, no index arithmetic, no table lookups; θ is fused into the ρ+π terms and +//! ι into the first χ lane; +//! - lanes are carried as `u128` — the bitwise builtin's native operand width — so +//! XOR/AND need no per-operation downcast. The `< 2^64` lane invariant is +//! maintained structurally: XOR/AND of 64-bit values is 64-bit, complements are +//! `MASK64 - b` (exact on 64-bit values, no builtin), and rotations reassemble +//! disjoint bit ranges below bit 64; +//! - rotations are division-free: every rotation amount is baked in as its power of +//! two, so a left-rotate is one felt252 multiply and one u128 `div_rem` by 2^64 — +//! the wrapped high bits and the shifted low bits occupy disjoint positions, so +//! their sum is the rotation. +//! +//! [`keccak_f1600`] is shared with the hash-to-point squeezer +//! (`hash_to_point::hash_to_point_shake_512`), which drives the sponge lazily and +//! reads candidates straight from the rate lanes; the byte-oriented [`shake256`] here +//! is the generic XOF, pinned to the FIPS 202 known-answer vectors below. + +const MASK64: u128 = 0xffffffffffffffff; +const RATE_LANES: u32 = 17; // 136 bytes / 8 +const RATE_BYTES: u32 = 136; + +/// Keccak-f[1600] round constants (FIPS 202, Table 1). +const ROUND_CONSTANTS: [u128; 24] = [ + 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000, + 0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009, + 0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a, + 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003, + 0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a, + 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008, +]; + +/// Rotate a 64-bit lane left, given `pow = 2^r` with 0 < r < 64. The widened felt +/// product `x * 2^r < 2^127` splits at bit 64 into the bits that stay (`lo`) and the +/// bits that wrap to the bottom (`hi`); they occupy disjoint positions, so `lo + hi` +/// is the rotation. +#[inline(always)] +fn rotl(x: u128, pow: felt252, two64: NonZero) -> u128 { + let wide: u128 = (x.into() * pow).try_into().unwrap(); + let (hi, lo) = DivRem::div_rem(wide, two64); + lo + hi +} + +/// Keccak-f[1600]: 24 rounds of θ, ρ, π, χ, ι on the 25-lane state, fully unrolled +/// over lane locals. The ρ+π terms read `b[dst] = rotl(a[src] ^ d[src mod 5], ρ[dst])` +/// with the π source indices and ρ offsets from FIPS 202, precomposed per destination. +pub(crate) fn keccak_f1600(state: [u128; 25]) -> [u128; 25] { + let two64: NonZero = 0x10000000000000000_u128.try_into().unwrap(); + let mut s = state; + for rc in ROUND_CONSTANTS.span() { + let rc = *rc; + let [ + s00, + s01, + s02, + s03, + s04, + s05, + s06, + s07, + s08, + s09, + s10, + s11, + s12, + s13, + s14, + s15, + s16, + s17, + s18, + s19, + s20, + s21, + s22, + s23, + s24, + ] = + s; + + // theta: column parities and the per-column mixing term d[x] = c[x-1] ^ rotl(c[x+1], 1). + let c0 = s00 ^ s05 ^ s10 ^ s15 ^ s20; + let c1 = s01 ^ s06 ^ s11 ^ s16 ^ s21; + let c2 = s02 ^ s07 ^ s12 ^ s17 ^ s22; + let c3 = s03 ^ s08 ^ s13 ^ s18 ^ s23; + let c4 = s04 ^ s09 ^ s14 ^ s19 ^ s24; + let d0 = c4 ^ rotl(c1, 0x2, two64); + let d1 = c0 ^ rotl(c2, 0x2, two64); + let d2 = c1 ^ rotl(c3, 0x2, two64); + let d3 = c2 ^ rotl(c4, 0x2, two64); + let d4 = c3 ^ rotl(c0, 0x2, two64); + + // theta (fused) + rho + pi, straight into destination order. + let b00 = s00 ^ d0; + let b01 = rotl(s06 ^ d1, 0x100000000000, two64); + let b02 = rotl(s12 ^ d2, 0x80000000000, two64); + let b03 = rotl(s18 ^ d3, 0x200000, two64); + let b04 = rotl(s24 ^ d4, 0x4000, two64); + let b05 = rotl(s03 ^ d3, 0x10000000, two64); + let b06 = rotl(s09 ^ d4, 0x100000, two64); + let b07 = rotl(s10 ^ d0, 0x8, two64); + let b08 = rotl(s16 ^ d1, 0x200000000000, two64); + let b09 = rotl(s22 ^ d2, 0x2000000000000000, two64); + let b10 = rotl(s01 ^ d1, 0x2, two64); + let b11 = rotl(s07 ^ d2, 0x40, two64); + let b12 = rotl(s13 ^ d3, 0x2000000, two64); + let b13 = rotl(s19 ^ d4, 0x100, two64); + let b14 = rotl(s20 ^ d0, 0x40000, two64); + let b15 = rotl(s04 ^ d4, 0x8000000, two64); + let b16 = rotl(s05 ^ d0, 0x1000000000, two64); + let b17 = rotl(s11 ^ d1, 0x400, two64); + let b18 = rotl(s17 ^ d2, 0x8000, two64); + let b19 = rotl(s23 ^ d3, 0x100000000000000, two64); + let b20 = rotl(s02 ^ d2, 0x4000000000000000, two64); + let b21 = rotl(s08 ^ d3, 0x80000000000000, two64); + let b22 = rotl(s14 ^ d4, 0x8000000000, two64); + let b23 = rotl(s15 ^ d0, 0x20000000000, two64); + let b24 = rotl(s21 ^ d1, 0x4, two64); + + // chi (complement as MASK64 - b), with iota folded into lane 0. + s = + [ + b00 ^ ((MASK64 - b01) & b02) ^ rc, b01 ^ ((MASK64 - b02) & b03), + b02 ^ ((MASK64 - b03) & b04), b03 ^ ((MASK64 - b04) & b00), + b04 ^ ((MASK64 - b00) & b01), b05 ^ ((MASK64 - b06) & b07), + b06 ^ ((MASK64 - b07) & b08), b07 ^ ((MASK64 - b08) & b09), + b08 ^ ((MASK64 - b09) & b05), b09 ^ ((MASK64 - b05) & b06), + b10 ^ ((MASK64 - b11) & b12), b11 ^ ((MASK64 - b12) & b13), + b12 ^ ((MASK64 - b13) & b14), b13 ^ ((MASK64 - b14) & b10), + b14 ^ ((MASK64 - b10) & b11), b15 ^ ((MASK64 - b16) & b17), + b16 ^ ((MASK64 - b17) & b18), b17 ^ ((MASK64 - b18) & b19), + b18 ^ ((MASK64 - b19) & b15), b19 ^ ((MASK64 - b15) & b16), + b20 ^ ((MASK64 - b21) & b22), b21 ^ ((MASK64 - b22) & b23), + b22 ^ ((MASK64 - b23) & b24), b23 ^ ((MASK64 - b24) & b20), + b24 ^ ((MASK64 - b20) & b21), + ]; + } + s +} + +/// SHAKE-256: absorb `input`, squeeze `out_bytes` bytes. +pub(crate) fn shake256(input: Array, out_bytes: u32) -> Array { + // Pad (pad10*1 with SHAKE domain 0x1F) to a multiple of the rate. + let mut padded = input; + let l = padded.len(); + let pad_len = RATE_BYTES - (l % RATE_BYTES); // in [1, RATE_BYTES] + padded.append(0x1f); + let mut z = 1; + while z != pad_len { + padded.append(0); + z += 1; + } + let last_idx = l + pad_len - 1; + let bytes = pad_ored_msb(padded, last_idx); + + // Absorb rate-sized blocks: XOR each block's 17 lanes into the state, permute. + let mut state = [0; 25]; + let n_blocks = (l + pad_len) / RATE_BYTES; + let mut blk = 0; + while blk != n_blocks { + let base = blk * RATE_BYTES; + let [ + s00, + s01, + s02, + s03, + s04, + s05, + s06, + s07, + s08, + s09, + s10, + s11, + s12, + s13, + s14, + s15, + s16, + s17, + s18, + s19, + s20, + s21, + s22, + s23, + s24, + ] = + state; + state = + keccak_f1600( + [ + s00 ^ load_lane_le(@bytes, base), s01 ^ load_lane_le(@bytes, base + 8), + s02 ^ load_lane_le(@bytes, base + 16), s03 ^ load_lane_le(@bytes, base + 24), + s04 ^ load_lane_le(@bytes, base + 32), s05 ^ load_lane_le(@bytes, base + 40), + s06 ^ load_lane_le(@bytes, base + 48), s07 ^ load_lane_le(@bytes, base + 56), + s08 ^ load_lane_le(@bytes, base + 64), s09 ^ load_lane_le(@bytes, base + 72), + s10 ^ load_lane_le(@bytes, base + 80), s11 ^ load_lane_le(@bytes, base + 88), + s12 ^ load_lane_le(@bytes, base + 96), s13 ^ load_lane_le(@bytes, base + 104), + s14 ^ load_lane_le(@bytes, base + 112), s15 ^ load_lane_le(@bytes, base + 120), + s16 ^ load_lane_le(@bytes, base + 128), s17, s18, s19, s20, s21, s22, s23, s24, + ], + ); + blk += 1; + } + + // Squeeze rate-sized blocks, permuting only when more output is needed. + let mut out: Array = array![]; + loop { + let mut lanes = state.span().slice(0, RATE_LANES); + while let Some(lane) = lanes.pop_front() { + emit_lane_le(*lane, ref out, out_bytes); + } + if out.len() == out_bytes { + break; + } + state = keccak_f1600(state); + } + out +} + +/// Append up to 8 little-endian bytes of the lane `v` (< 2^64), stopping at `out_bytes`. +fn emit_lane_le(v: u128, ref out: Array, out_bytes: u32) { + let mut rem: u64 = v.try_into().unwrap(); + let mut k = 0; + while k != 8 && out.len() < out_bytes { + out.append((rem % 256).try_into().unwrap()); + rem = rem / 256; + k += 1; + } +} + +/// Rebuild `bytes` with `bytes[idx] |= 0x80` (that byte is < 0x80 here, so `+ 0x80`). +fn pad_ored_msb(bytes: Array, idx: u32) -> Array { + let mut out: Array = array![]; + let span = bytes.span(); + let mut i = 0; + while i != span.len() { + if i == idx { + out.append(*span[i] + 0x80); + } else { + out.append(*span[i]); + } + i += 1; + } + out +} + +/// Little-endian 64-bit lane from 8 bytes at `off`. +fn load_lane_le(bytes: @Array, off: u32) -> u128 { + let span = bytes.span(); + let mut acc: u128 = 0; + let mut k = 8; + while k != 0 { + k -= 1; + acc = acc * 256 + (*span[off + k]).into(); + } + acc +} + +#[cfg(test)] +mod tests { + use super::shake256; + + fn to_hex(bytes: Array) -> ByteArray { + let hexchars: ByteArray = "0123456789abcdef"; + let mut s: ByteArray = ""; + let mut span = bytes.span(); + while let Some(b) = span.pop_front() { + s.append_byte(hexchars.at((*b / 16).into()).unwrap()); + s.append_byte(hexchars.at((*b % 16).into()).unwrap()); + } + s + } + + // KAT vectors from Python `hashlib.shake_256` (FIPS 202). + #[test] + fn test_shake256_empty() { + let got = to_hex(shake256(array![], 32)); + assert!(got == "46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f"); + } + + #[test] + fn test_shake256_abc() { + let got = to_hex(shake256(array![0x61, 0x62, 0x63], 32)); + assert!(got == "483366601360a8771c6863080cc4114d8db44530f8f1e1ee4f94ea37e78b5739"); + } + + // 200 bytes of 0xA3: a 2-block absorb (input > 136-byte rate). NIST SHAKE-256 vector. + #[test] + fn test_shake256_multiblock() { + let mut input: Array = array![]; + let mut i = 0; + while i != 200 { + input.append(0xa3); + i += 1; + } + let got = to_hex(shake256(input, 32)); + assert!(got == "cd8a920ed141aa0407a22d59288652e9d9f1a7ee0c1e7c1ca699424da84a904d"); + } + + // Squeeze past one rate block (137 bytes > 136): exercises the squeeze-side + // permutation. Tail bytes from Python `hashlib.shake_256(b"abc").hexdigest(137)`. + #[test] + fn test_shake256_multiblock_squeeze() { + let out = shake256(array![0x61, 0x62, 0x63], 137); + assert_eq!(out.len(), 137); + // First bytes match the 32-byte KAT above. + assert_eq!(*out.at(0), 0x48); + assert_eq!(*out.at(1), 0x33); + // Bytes 128.. of the stream: e8 a2 d7 ec 71 a7 cc 29 cf (last is out[136]). + let tail: [u8; 9] = [0xe8, 0xa2, 0xd7, 0xec, 0x71, 0xa7, 0xcc, 0x29, 0xcf]; + let mut i: u32 = 128; + for expected in tail.span() { + assert_eq!(*out.at(i), *expected); + i += 1; + } + } +} diff --git a/packages/account/src/falcon_512/ntt.cairo b/packages/account/src/falcon_512/ntt.cairo new file mode 100644 index 000000000..688cf5258 --- /dev/null +++ b/packages/account/src/falcon_512/ntt.cairo @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/ntt.cairo) + +//! Number-theoretic transform implementation used by the Falcon-512 verifiers. + +pub(crate) mod bitrev; +pub(crate) mod engine; +pub(crate) mod falcon512; +pub(crate) mod falcon512_fast; +pub(crate) mod roots_felt; +pub(crate) mod roots_scaled; diff --git a/packages/account/src/falcon_512/ntt/bitrev.cairo b/packages/account/src/falcon_512/ntt/bitrev.cairo new file mode 100644 index 000000000..4ff54a7e5 --- /dev/null +++ b/packages/account/src/falcon_512/ntt/bitrev.cairo @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/ntt/bitrev.cairo) + +// Generated from the Falcon-512 NTT parameters — do not edit by hand. +// Bit-reversal permutation for n = 512 (self-inverse). The iterative +// engine permutes inputs into recursion-leaf order before its merge +// levels, and INTT outputs back to natural order. + +//! Bit-reversal permutation table (generated). + +pub(crate) const BITREV_512: [u16; 512] = [ + 0, 256, 128, 384, 64, 320, 192, 448, 32, 288, 160, 416, 96, 352, 224, 480, 16, 272, 144, 400, + 80, 336, 208, 464, 48, 304, 176, 432, 112, 368, 240, 496, 8, 264, 136, 392, 72, 328, 200, 456, + 40, 296, 168, 424, 104, 360, 232, 488, 24, 280, 152, 408, 88, 344, 216, 472, 56, 312, 184, 440, + 120, 376, 248, 504, 4, 260, 132, 388, 68, 324, 196, 452, 36, 292, 164, 420, 100, 356, 228, 484, + 20, 276, 148, 404, 84, 340, 212, 468, 52, 308, 180, 436, 116, 372, 244, 500, 12, 268, 140, 396, + 76, 332, 204, 460, 44, 300, 172, 428, 108, 364, 236, 492, 28, 284, 156, 412, 92, 348, 220, 476, + 60, 316, 188, 444, 124, 380, 252, 508, 2, 258, 130, 386, 66, 322, 194, 450, 34, 290, 162, 418, + 98, 354, 226, 482, 18, 274, 146, 402, 82, 338, 210, 466, 50, 306, 178, 434, 114, 370, 242, 498, + 10, 266, 138, 394, 74, 330, 202, 458, 42, 298, 170, 426, 106, 362, 234, 490, 26, 282, 154, 410, + 90, 346, 218, 474, 58, 314, 186, 442, 122, 378, 250, 506, 6, 262, 134, 390, 70, 326, 198, 454, + 38, 294, 166, 422, 102, 358, 230, 486, 22, 278, 150, 406, 86, 342, 214, 470, 54, 310, 182, 438, + 118, 374, 246, 502, 14, 270, 142, 398, 78, 334, 206, 462, 46, 302, 174, 430, 110, 366, 238, 494, + 30, 286, 158, 414, 94, 350, 222, 478, 62, 318, 190, 446, 126, 382, 254, 510, 1, 257, 129, 385, + 65, 321, 193, 449, 33, 289, 161, 417, 97, 353, 225, 481, 17, 273, 145, 401, 81, 337, 209, 465, + 49, 305, 177, 433, 113, 369, 241, 497, 9, 265, 137, 393, 73, 329, 201, 457, 41, 297, 169, 425, + 105, 361, 233, 489, 25, 281, 153, 409, 89, 345, 217, 473, 57, 313, 185, 441, 121, 377, 249, 505, + 5, 261, 133, 389, 69, 325, 197, 453, 37, 293, 165, 421, 101, 357, 229, 485, 21, 277, 149, 405, + 85, 341, 213, 469, 53, 309, 181, 437, 117, 373, 245, 501, 13, 269, 141, 397, 77, 333, 205, 461, + 45, 301, 173, 429, 109, 365, 237, 493, 29, 285, 157, 413, 93, 349, 221, 477, 61, 317, 189, 445, + 125, 381, 253, 509, 3, 259, 131, 387, 67, 323, 195, 451, 35, 291, 163, 419, 99, 355, 227, 483, + 19, 275, 147, 403, 83, 339, 211, 467, 51, 307, 179, 435, 115, 371, 243, 499, 11, 267, 139, 395, + 75, 331, 203, 459, 43, 299, 171, 427, 107, 363, 235, 491, 27, 283, 155, 411, 91, 347, 219, 475, + 59, 315, 187, 443, 123, 379, 251, 507, 7, 263, 135, 391, 71, 327, 199, 455, 39, 295, 167, 423, + 103, 359, 231, 487, 23, 279, 151, 407, 87, 343, 215, 471, 55, 311, 183, 439, 119, 375, 247, 503, + 15, 271, 143, 399, 79, 335, 207, 463, 47, 303, 175, 431, 111, 367, 239, 495, 31, 287, 159, 415, + 95, 351, 223, 479, 63, 319, 191, 447, 127, 383, 255, 511, +]; + +/// The 512-entry bit-reversal permutation as a span. +pub fn bitrev_512() -> Span { + BITREV_512.span() +} diff --git a/packages/account/src/falcon_512/ntt/engine.cairo b/packages/account/src/falcon_512/ntt/engine.cairo new file mode 100644 index 000000000..bdde9fb54 --- /dev/null +++ b/packages/account/src/falcon_512/ntt/engine.cairo @@ -0,0 +1,382 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/ntt/engine.cairo) + +//! Iterative NTT engine with felt252 lazy reduction. +//! +//! # Algorithm +//! +//! This computes exactly the recursive split/merge NTT of tprest/falcon.py (the +//! convention s2morrow and the Falcon-512 verifier use), reformulated iteratively: +//! +//! - **Forward** ([`ntt`]): permute the input into recursion-leaf order (bit reversal), +//! then run merge levels bottom-up. The level merging blocks into size `2h` reads two +//! half-blocks `f0`, `f1` sequentially and appends `f0[i] ± w[i]·f1[i]` interleaved — +//! every read and write is sequential, matching Cairo's append-only arrays. The +//! permutation is fused into the first level (`h = 1`), which is also specialized: +//! its blocks are single butterflies sharing one root. +//! - **Inverse** ([`intt`]): the exact mirror — split levels top-down (reading +//! interleaved pairs, writing the two half-blocks), then one fused +//! permute-and-reduce pass back to natural order. The per-level `1/2` factor is +//! folded into the pre-scaled inverse root tables (`tinv[i] = 2^-1 · w[i]^-1 mod q`). +//! The last level (`h = 1`) writes adjacent outputs and needs no half-block buffer. +//! +//! Generic levels with `h >= 8` run 8-way unrolled over `multi_pop_front` chunks. +//! +//! # Lazy reduction (the Starknet optimization) +//! +//! Coefficients are carried as raw `felt252` integers WITHOUT per-operation modular +//! reduction: a butterfly is one field multiplication and a few field additions +//! (~1 step each), instead of the mul + divmod + range-check chain of eager `% q` +//! arithmetic. Subtractions are made non-negative by adding `off = bound · q` — a +//! multiple of q, so residues are unchanged. +//! +//! Soundness of the delay: the engine tracks an exclusive upper `bound` on the +//! coefficient integers (exactly, as a felt) and its bit size (conservatively). +//! Per forward level `bound *= q+1`; per inverse level `bound *= 2q`. Before a level +//! that could push values to 2^126 or beyond, and once at the end, a reduction pass +//! maps every coefficient to `[0, q)` via u128 divmod. Since all intermediates stay +//! below 2^126 < P/2^125, felt252 arithmetic never wraps and every reduction input +//! fits u128. For q = 12289 this schedule costs exactly TWO passes per 512-point +//! transform. [`ntt_lazy`] additionally exposes the transform WITHOUT its final +//! reduction pass, returning the tracked `(bits, bound)` alongside the unreduced +//! values, so callers whose follow-up arithmetic tolerates the bound (a divisibility +//! check, a lazy-product INTT) can skip that pass entirely. +//! The generated tables and forward transform are tested against the recursive +//! reference on random and adversarial inputs. + +use core::traits::DivRem; + +/// Everything a parameter set provides to the engine. Spans make configs cheap to +/// build and copy; see `falcon512::config()` for the canonical instance. +#[derive(Copy, Drop)] +pub struct NttConfig { + /// Transform size (a power of two). + pub n: u32, + /// log2(n): number of merge/split levels. + pub levels: u32, + /// The modulus, as a u128 divisor for reduction passes. + pub q_nz: NonZero, + /// The modulus as a felt (offset construction). + pub q_felt: felt252, + /// 2^-1 mod q (even branch of the inverse split). + pub i2_felt: felt252, + /// Bit size of a reduced value: values in [0, q) fit `qbits` bits. + pub qbits: u32, + /// Per-forward-level bound growth, exact and in bits: bound *= q+1. + pub fwd_growth_felt: felt252, + pub fwd_growth_bits: u32, + /// Per-inverse-level bound growth, exact and in bits: bound *= 2q. + pub inv_growth_felt: felt252, + pub inv_growth_bits: u32, + /// Leaf permutation (self-inverse bit reversal), entries < n. + pub perm: Span, + /// Merge root tables: index ℓ holds the size-2^(ℓ+1) table (length 2^ℓ). + pub merge_roots: Span>, + /// I2-prescaled inverse root tables, same indexing as `merge_roots`. + pub split_roots_scaled: Span>, +} + +/// Intermediates must stay below 2^126: felt252 arithmetic cannot wrap (2^126 ≪ P) +/// and reduction inputs fit u128. +const SAFE_BITS: u32 = 126; + +/// Reduce every coefficient to `[0, q)`. Inputs must be < 2^128 (guaranteed by the +/// engine's bound schedule). 8-way unrolled. +fn reduce_pass(mut vals: Span, q_nz: NonZero) -> Array { + let mut out: Array = array![]; + while let Some(chunk) = vals.multi_pop_front::<8>() { + let [v0, v1, v2, v3, v4, v5, v6, v7] = (*chunk).unbox(); + let (_, r0) = DivRem::div_rem(TryInto::::try_into(v0).unwrap(), q_nz); + let (_, r1) = DivRem::div_rem(TryInto::::try_into(v1).unwrap(), q_nz); + let (_, r2) = DivRem::div_rem(TryInto::::try_into(v2).unwrap(), q_nz); + let (_, r3) = DivRem::div_rem(TryInto::::try_into(v3).unwrap(), q_nz); + let (_, r4) = DivRem::div_rem(TryInto::::try_into(v4).unwrap(), q_nz); + let (_, r5) = DivRem::div_rem(TryInto::::try_into(v5).unwrap(), q_nz); + let (_, r6) = DivRem::div_rem(TryInto::::try_into(v6).unwrap(), q_nz); + let (_, r7) = DivRem::div_rem(TryInto::::try_into(v7).unwrap(), q_nz); + out.append(r0.into()); + out.append(r1.into()); + out.append(r2.into()); + out.append(r3.into()); + out.append(r4.into()); + out.append(r5.into()); + out.append(r6.into()); + out.append(r7.into()); + } + while let Some(v) = vals.pop_front() { + let vu: u128 = (*v).try_into().unwrap(); + let (_, rem) = DivRem::div_rem(vu, q_nz); + out.append(rem.into()); + } + out +} + +/// Reduce a single lazily-computed value (< 2^128) to `[0, q)`. +pub fn reduce_felt(v: felt252, q_nz: NonZero) -> felt252 { + let vu: u128 = v.try_into().unwrap(); + let (_, rem) = DivRem::div_rem(vu, q_nz); + rem.into() +} + +/// One generic merge level: blocks of output size `2h`; `f0`/`f1` are each block's +/// half-spans, outputs interleave `f0[i] ± w[i]·f1[i]` (offset keeps them non-negative). +#[inline] +fn merge_level( + src: Span, roots: Span, n: u32, h: u32, off: felt252, +) -> Array { + let mut out: Array = array![]; + let mut b: u32 = 0; + if h >= 8 { + while b != n { + let mut f0 = src.slice(b, h); + let mut f1 = src.slice(b + h, h); + let mut r = roots; + while let Some(rc) = r.multi_pop_front::<8>() { + let [w0, w1, w2, w3, w4, w5, w6, w7] = (*rc).unbox(); + let [a0, a1, a2, a3, a4, a5, a6, a7] = (*f0.multi_pop_front::<8>().unwrap()) + .unbox(); + let [b0, b1, b2, b3, b4, b5, b6, b7] = (*f1.multi_pop_front::<8>().unwrap()) + .unbox(); + let t0 = w0 * b0; + out.append(a0 + t0); + out.append(a0 + off - t0); + let t1 = w1 * b1; + out.append(a1 + t1); + out.append(a1 + off - t1); + let t2 = w2 * b2; + out.append(a2 + t2); + out.append(a2 + off - t2); + let t3 = w3 * b3; + out.append(a3 + t3); + out.append(a3 + off - t3); + let t4 = w4 * b4; + out.append(a4 + t4); + out.append(a4 + off - t4); + let t5 = w5 * b5; + out.append(a5 + t5); + out.append(a5 + off - t5); + let t6 = w6 * b6; + out.append(a6 + t6); + out.append(a6 + off - t6); + let t7 = w7 * b7; + out.append(a7 + t7); + out.append(a7 + off - t7); + } + b += 2 * h; + } + } else { + while b != n { + let mut f0 = src.slice(b, h); + let mut f1 = src.slice(b + h, h); + let mut r = roots; + while let Some(w) = r.pop_front() { + let x0 = *f0.pop_front().unwrap(); + let x1 = *f1.pop_front().unwrap(); + let t = *w * x1; + out.append(x0 + t); + out.append(x0 + off - t); + } + b += 2 * h; + } + } + out +} + +/// Forward NTT. `f` must hold `cfg.n` coefficients in `[0, q)`. Returns the transform +/// in the parameter set's evaluation order, reduced to `[0, q)`. +pub fn ntt(f: Span, cfg: @NttConfig) -> Array { + let (out, _, _) = ntt_core(f, cfg); + reduce_pass(out.span(), *cfg.q_nz) +} + +/// Forward NTT without the final reduction pass: the same transform as [`ntt`], with +/// outputs returned as unreduced integers congruent to the reduced transform mod q. +/// Returns `(values, bits, bound)` where `bound` is the exact exclusive upper bound on +/// the output integers under the engine's reduction schedule and `bits` its +/// conservative bit size. Callers fold the bound into their own arithmetic (e.g. a +/// divisibility check or a lazy-product INTT) instead of paying a full reduction pass. +pub fn ntt_lazy(f: Span, cfg: @NttConfig) -> (Array, u32, felt252) { + ntt_core(f, cfg) +} + +/// The merge levels shared by [`ntt`] and [`ntt_lazy`], inlined into both so neither +/// entry point pays an extra call boundary. +#[inline(always)] +fn ntt_core(f: Span, cfg: @NttConfig) -> (Array, u32, felt252) { + let n = *cfg.n; + assert(f.len() == n, 'ntt: bad input length'); + let q_felt = *cfg.q_felt; + let q_nz = *cfg.q_nz; + let qbits = *cfg.qbits; + let growth_felt = *cfg.fwd_growth_felt; + let growth_bits = *cfg.fwd_growth_bits; + let merge_roots = *cfg.merge_roots; + + // Level h = 1, fused with the leaf permutation: block b is the single butterfly + // (f[perm[2b]], f[perm[2b+1]]), and every block shares the size-2 table's one root. + let r0 = *(*merge_roots.at(0)).at(0); + let off0 = q_felt * q_felt; // bound = q + let mut cur: Array = array![]; + let mut perm = *cfg.perm; + while let Some(pc) = perm.multi_pop_front::<2>() { + let [p0, p1] = (*pc).unbox(); + let x0 = *f.at(p0.into()); + let x1 = *f.at(p1.into()); + let t = r0 * x1; + cur.append(x0 + t); + cur.append(x0 + off0 - t); + } + let mut bits: u32 = qbits + growth_bits; + let mut bound: felt252 = q_felt * growth_felt; + + // Remaining merge levels. + let mut h: u32 = 2; + let mut level: u32 = 1; + while h != n { + if bits + growth_bits > SAFE_BITS { + cur = reduce_pass(cur.span(), q_nz); + bits = qbits; + bound = q_felt; + } + // off = bound·q: a multiple of q dominating any w·x1 (w < q, x1 < bound), so + // odd outputs stay non-negative with residues unchanged. + cur = merge_level(cur.span(), *merge_roots.at(level), n, h, bound * q_felt); + // Outputs < bound + q·bound = bound·(q+1). + bound = bound * growth_felt; + bits += growth_bits; + h = 2 * h; + level += 1; + } + (cur, bits, bound) +} + +/// One generic split level: read interleaved pairs per block of size `2h`, write the +/// even results followed by the buffered odd half-block. +#[inline] +fn split_level( + src: Span, tinv: Span, i2: felt252, n: u32, h: u32, off: felt252, +) -> Array { + let mut out: Array = array![]; + let mut b: u32 = 0; + if h >= 8 { + while b != n { + let mut pairs = src.slice(b, 2 * h); + let mut r = tinv; + let mut tmp: Array = array![]; + while let Some(rc) = r.multi_pop_front::<4>() { + let [w0, w1, w2, w3] = (*rc).unbox(); + let [x00, x01, x10, x11, x20, x21, x30, x31] = (*pairs + .multi_pop_front::<8>() + .unwrap()) + .unbox(); + out.append(i2 * (x00 + x01)); + tmp.append(w0 * x00 + off - w0 * x01); + out.append(i2 * (x10 + x11)); + tmp.append(w1 * x10 + off - w1 * x11); + out.append(i2 * (x20 + x21)); + tmp.append(w2 * x20 + off - w2 * x21); + out.append(i2 * (x30 + x31)); + tmp.append(w3 * x30 + off - w3 * x31); + } + let mut ts = tmp.span(); + while let Some(tc) = ts.multi_pop_front::<4>() { + let [t0, t1, t2, t3] = (*tc).unbox(); + out.append(t0); + out.append(t1); + out.append(t2); + out.append(t3); + } + b += 2 * h; + } + } else { + while b != n { + let mut pairs = src.slice(b, 2 * h); + let mut r = tinv; + let mut tmp: Array = array![]; + while let Some(w) = r.pop_front() { + let x0 = *pairs.pop_front().unwrap(); + let x1 = *pairs.pop_front().unwrap(); + out.append(i2 * (x0 + x1)); + tmp.append(*w * x0 + off - *w * x1); + } + let mut ts = tmp.span(); + while let Some(v) = ts.pop_front() { + out.append(*v); + } + b += 2 * h; + } + } + out +} + +/// Inverse NTT. `f` must hold `cfg.n` values below `2^input_bits` (as integers), with +/// `input_bound` their exact exclusive bound: pass `(cfg.qbits, cfg.q_felt)` for +/// reduced inputs, or the product bound for unreduced pointwise products (the +/// lazy-product path: `intt(a_ntt ∘ b_ntt)` without reducing the products first). +/// Returns coefficients in natural order, reduced to `[0, q)`. +pub fn intt( + f: Span, input_bits: u32, input_bound: felt252, cfg: @NttConfig, +) -> Array { + let n = *cfg.n; + assert(f.len() == n, 'intt: bad input length'); + let q_felt = *cfg.q_felt; + let q_nz = *cfg.q_nz; + let qbits = *cfg.qbits; + let i2 = *cfg.i2_felt; + let growth_felt = *cfg.inv_growth_felt; + let growth_bits = *cfg.inv_growth_bits; + let split_roots = *cfg.split_roots_scaled; + assert(input_bits + growth_bits <= SAFE_BITS, 'intt: input too large'); + + // Split levels top-down (h = n/2 .. 2 generic; h = 1 specialized below). + let mut bits: u32 = input_bits; + let mut bound: felt252 = input_bound; + let mut cur: Array = array![]; + let mut src0 = f; + while let Some(v) = src0.pop_front() { + cur.append(*v); + } + let mut h: u32 = n / 2; + let mut level: u32 = *cfg.levels - 1; + while h != 1 { + if bits + growth_bits > SAFE_BITS { + cur = reduce_pass(cur.span(), q_nz); + bits = qbits; + bound = q_felt; + } + cur = split_level(cur.span(), *split_roots.at(level), i2, n, h, bound * q_felt); + // Both branches < 2q·bound. + bound = bound * growth_felt; + bits += growth_bits; + h = h / 2; + level -= 1; + } + + // Last level (h = 1): every block is one adjacent pair sharing the size-2 scaled + // root — outputs are adjacent too, so no half-block buffering. + if bits + growth_bits > SAFE_BITS { + cur = reduce_pass(cur.span(), q_nz); + bound = q_felt; + } + let t0 = *(*split_roots.at(0)).at(0); + let off = bound * q_felt; + let src = cur.span(); + let mut last: Array = array![]; + let mut pairs = src; + while let Some(pc) = pairs.multi_pop_front::<2>() { + let [x0, x1] = (*pc).unbox(); + last.append(i2 * (x0 + x1)); + last.append(t0 * x0 + off - t0 * x1); + } + + // Fused final pass: bit-reversal back to natural order + reduction. + let out_src = last.span(); + let mut out: Array = array![]; + let mut perm = *cfg.perm; + while let Some(p) = perm.pop_front() { + let vu: u128 = (*out_src.at((*p).into())).try_into().unwrap(); + let (_, rem) = DivRem::div_rem(vu, q_nz); + out.append(rem.into()); + } + out +} diff --git a/packages/account/src/falcon_512/ntt/falcon512.cairo b/packages/account/src/falcon_512/ntt/falcon512.cairo new file mode 100644 index 000000000..9a6cd72ad --- /dev/null +++ b/packages/account/src/falcon_512/ntt/falcon512.cairo @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/ntt/falcon512.cairo) + +//! Falcon parameter set for the NTT engine: q = 12289, negacyclic (x^n + 1), +//! tprest/falcon.py evaluation order — interoperable with the s2morrow convention and +//! the falcon.py reference signer. + +use super::bitrev::bitrev_512; +use super::engine::NttConfig; +use super::roots_felt::get_even_roots_felt; +use super::roots_scaled::get_scaled_inv_roots; + +/// The Falcon modulus q = 12289 = 12·1024 + 1. +pub const Q: u16 = 12289; +/// q as a felt. +pub const Q_FELT: felt252 = 12289; +/// 2^-1 mod q. +pub const I2_FELT: felt252 = 6145; +/// Reduced values are < q < 2^14. +pub const REDUCED_BITS: u32 = 14; +/// Unreduced pointwise products of two reduced values are < q^2 < 2^28. +pub const PRODUCT_BITS: u32 = 28; +/// q^2 as a felt: the exact bound matching [`PRODUCT_BITS`]. +pub const PRODUCT_BOUND_FELT: felt252 = 151019521; + +/// Engine configuration for the production size n = 512 (table-driven permutation). +pub fn config() -> NttConfig { + config_with_perm(512, 9, bitrev_512()) +} + +/// Engine configuration for any supported degree (4..512), computing the bit-reversal +/// permutation programmatically. Meant for tests and auxiliary tooling; production +/// callers use [`config`], which reads the pinned table instead. +pub fn config_for_degree(n: u32, levels: u32) -> NttConfig { + let mut perm: Array = array![]; + let mut i: u32 = 0; + while i != n { + let mut x = i; + let mut acc: u32 = 0; + let mut j: u32 = 0; + while j != levels { + acc = 2 * acc + (x % 2); + x = x / 2; + j += 1; + } + perm.append(acc.try_into().unwrap()); + i += 1; + } + config_with_perm(n, levels, perm.span()) +} + +fn config_with_perm(n: u32, levels: u32, perm: Span) -> NttConfig { + // Root-table spans, one per level: level ℓ merges into size 2^(ℓ+1). The tables + // are stored as felt252 constants (generated), so no per-call conversion runs. + let mut merge_roots: Array> = array![]; + let mut split_scaled: Array> = array![]; + let mut size: u32 = 2; + loop { + merge_roots.append(get_even_roots_felt(size)); + split_scaled.append(get_scaled_inv_roots(size)); + if size == n { + break; + } + size = 2 * size; + } + NttConfig { + n, + levels, + q_nz: 12289, + q_felt: Q_FELT, + i2_felt: I2_FELT, + qbits: REDUCED_BITS, + fwd_growth_felt: 12290, // q + 1 + fwd_growth_bits: 14, + inv_growth_felt: 24578, // 2q + inv_growth_bits: 15, + perm, + merge_roots: merge_roots.span(), + split_roots_scaled: split_scaled.span(), + } +} diff --git a/packages/account/src/falcon_512/ntt/falcon512_fast.cairo b/packages/account/src/falcon_512/ntt/falcon512_fast.cairo new file mode 100644 index 000000000..ab5a96646 --- /dev/null +++ b/packages/account/src/falcon_512/ntt/falcon512_fast.cairo @@ -0,0 +1,12797 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/ntt/falcon512_fast.cairo) + +// Generated from the Falcon-512 NTT parameters — do not edit by hand. +// Fully unrolled Falcon-512 forward NTT for n = 512 and q = 12289. +// The operation graph and its bounds are derived from roots.cairo; each +// output is reduced once after the straight-line felt252 arithmetic. + +//! Generated Falcon-512 forward NTT. +//! +//! Inputs are 512 coefficients in `[0, 12289)`. The unchecked public entry points +//! rely on callers to enforce that precondition; the fixed operation graph computes +//! the Falcon evaluation order and returns 512 reduced coefficients. + +use corelib_imports::bounded_int::{BoundedInt, DivRemHelper, UnitInt, bounded_int_div_rem, upcast}; +use corelib_imports::integer::{U128sFromFelt252Result, u128s_from_felt252}; + +type Falcon512Zq = BoundedInt<0, 12288>; +type Falcon512Q = UnitInt<12289>; +type U128AsBounded = BoundedInt<0, 340282366920938463463374607431768211455>; + +const FALCON512_Q_NZ: NonZero = 12289; +const SHIFT: felt252 = 2305326338167692580222332728864932068; + +impl Falcon512FastDivRemImpl of DivRemHelper { + type DivT = BoundedInt<0, 27689996494502275487295516920153650>; + type RemT = Falcon512Zq; +} + +#[inline(always)] +fn felt252_as_u128(value: felt252) -> u128 { + // Exact generated bounds put canonical shifted outputs below 2^128. + match u128s_from_felt252(value) { + U128sFromFelt252Result::Narrow(low) => low, + U128sFromFelt252Result::Wide((_, low)) => low, + } +} + +#[inline(always)] +fn ntt_falcon512_fast_inner( + f0: felt252, + f1: felt252, + f2: felt252, + f3: felt252, + f4: felt252, + f5: felt252, + f6: felt252, + f7: felt252, + f8: felt252, + f9: felt252, + f10: felt252, + f11: felt252, + f12: felt252, + f13: felt252, + f14: felt252, + f15: felt252, + f16: felt252, + f17: felt252, + f18: felt252, + f19: felt252, + f20: felt252, + f21: felt252, + f22: felt252, + f23: felt252, + f24: felt252, + f25: felt252, + f26: felt252, + f27: felt252, + f28: felt252, + f29: felt252, + f30: felt252, + f31: felt252, + f32: felt252, + f33: felt252, + f34: felt252, + f35: felt252, + f36: felt252, + f37: felt252, + f38: felt252, + f39: felt252, + f40: felt252, + f41: felt252, + f42: felt252, + f43: felt252, + f44: felt252, + f45: felt252, + f46: felt252, + f47: felt252, + f48: felt252, + f49: felt252, + f50: felt252, + f51: felt252, + f52: felt252, + f53: felt252, + f54: felt252, + f55: felt252, + f56: felt252, + f57: felt252, + f58: felt252, + f59: felt252, + f60: felt252, + f61: felt252, + f62: felt252, + f63: felt252, + f64: felt252, + f65: felt252, + f66: felt252, + f67: felt252, + f68: felt252, + f69: felt252, + f70: felt252, + f71: felt252, + f72: felt252, + f73: felt252, + f74: felt252, + f75: felt252, + f76: felt252, + f77: felt252, + f78: felt252, + f79: felt252, + f80: felt252, + f81: felt252, + f82: felt252, + f83: felt252, + f84: felt252, + f85: felt252, + f86: felt252, + f87: felt252, + f88: felt252, + f89: felt252, + f90: felt252, + f91: felt252, + f92: felt252, + f93: felt252, + f94: felt252, + f95: felt252, + f96: felt252, + f97: felt252, + f98: felt252, + f99: felt252, + f100: felt252, + f101: felt252, + f102: felt252, + f103: felt252, + f104: felt252, + f105: felt252, + f106: felt252, + f107: felt252, + f108: felt252, + f109: felt252, + f110: felt252, + f111: felt252, + f112: felt252, + f113: felt252, + f114: felt252, + f115: felt252, + f116: felt252, + f117: felt252, + f118: felt252, + f119: felt252, + f120: felt252, + f121: felt252, + f122: felt252, + f123: felt252, + f124: felt252, + f125: felt252, + f126: felt252, + f127: felt252, + f128: felt252, + f129: felt252, + f130: felt252, + f131: felt252, + f132: felt252, + f133: felt252, + f134: felt252, + f135: felt252, + f136: felt252, + f137: felt252, + f138: felt252, + f139: felt252, + f140: felt252, + f141: felt252, + f142: felt252, + f143: felt252, + f144: felt252, + f145: felt252, + f146: felt252, + f147: felt252, + f148: felt252, + f149: felt252, + f150: felt252, + f151: felt252, + f152: felt252, + f153: felt252, + f154: felt252, + f155: felt252, + f156: felt252, + f157: felt252, + f158: felt252, + f159: felt252, + f160: felt252, + f161: felt252, + f162: felt252, + f163: felt252, + f164: felt252, + f165: felt252, + f166: felt252, + f167: felt252, + f168: felt252, + f169: felt252, + f170: felt252, + f171: felt252, + f172: felt252, + f173: felt252, + f174: felt252, + f175: felt252, + f176: felt252, + f177: felt252, + f178: felt252, + f179: felt252, + f180: felt252, + f181: felt252, + f182: felt252, + f183: felt252, + f184: felt252, + f185: felt252, + f186: felt252, + f187: felt252, + f188: felt252, + f189: felt252, + f190: felt252, + f191: felt252, + f192: felt252, + f193: felt252, + f194: felt252, + f195: felt252, + f196: felt252, + f197: felt252, + f198: felt252, + f199: felt252, + f200: felt252, + f201: felt252, + f202: felt252, + f203: felt252, + f204: felt252, + f205: felt252, + f206: felt252, + f207: felt252, + f208: felt252, + f209: felt252, + f210: felt252, + f211: felt252, + f212: felt252, + f213: felt252, + f214: felt252, + f215: felt252, + f216: felt252, + f217: felt252, + f218: felt252, + f219: felt252, + f220: felt252, + f221: felt252, + f222: felt252, + f223: felt252, + f224: felt252, + f225: felt252, + f226: felt252, + f227: felt252, + f228: felt252, + f229: felt252, + f230: felt252, + f231: felt252, + f232: felt252, + f233: felt252, + f234: felt252, + f235: felt252, + f236: felt252, + f237: felt252, + f238: felt252, + f239: felt252, + f240: felt252, + f241: felt252, + f242: felt252, + f243: felt252, + f244: felt252, + f245: felt252, + f246: felt252, + f247: felt252, + f248: felt252, + f249: felt252, + f250: felt252, + f251: felt252, + f252: felt252, + f253: felt252, + f254: felt252, + f255: felt252, + f256: felt252, + f257: felt252, + f258: felt252, + f259: felt252, + f260: felt252, + f261: felt252, + f262: felt252, + f263: felt252, + f264: felt252, + f265: felt252, + f266: felt252, + f267: felt252, + f268: felt252, + f269: felt252, + f270: felt252, + f271: felt252, + f272: felt252, + f273: felt252, + f274: felt252, + f275: felt252, + f276: felt252, + f277: felt252, + f278: felt252, + f279: felt252, + f280: felt252, + f281: felt252, + f282: felt252, + f283: felt252, + f284: felt252, + f285: felt252, + f286: felt252, + f287: felt252, + f288: felt252, + f289: felt252, + f290: felt252, + f291: felt252, + f292: felt252, + f293: felt252, + f294: felt252, + f295: felt252, + f296: felt252, + f297: felt252, + f298: felt252, + f299: felt252, + f300: felt252, + f301: felt252, + f302: felt252, + f303: felt252, + f304: felt252, + f305: felt252, + f306: felt252, + f307: felt252, + f308: felt252, + f309: felt252, + f310: felt252, + f311: felt252, + f312: felt252, + f313: felt252, + f314: felt252, + f315: felt252, + f316: felt252, + f317: felt252, + f318: felt252, + f319: felt252, + f320: felt252, + f321: felt252, + f322: felt252, + f323: felt252, + f324: felt252, + f325: felt252, + f326: felt252, + f327: felt252, + f328: felt252, + f329: felt252, + f330: felt252, + f331: felt252, + f332: felt252, + f333: felt252, + f334: felt252, + f335: felt252, + f336: felt252, + f337: felt252, + f338: felt252, + f339: felt252, + f340: felt252, + f341: felt252, + f342: felt252, + f343: felt252, + f344: felt252, + f345: felt252, + f346: felt252, + f347: felt252, + f348: felt252, + f349: felt252, + f350: felt252, + f351: felt252, + f352: felt252, + f353: felt252, + f354: felt252, + f355: felt252, + f356: felt252, + f357: felt252, + f358: felt252, + f359: felt252, + f360: felt252, + f361: felt252, + f362: felt252, + f363: felt252, + f364: felt252, + f365: felt252, + f366: felt252, + f367: felt252, + f368: felt252, + f369: felt252, + f370: felt252, + f371: felt252, + f372: felt252, + f373: felt252, + f374: felt252, + f375: felt252, + f376: felt252, + f377: felt252, + f378: felt252, + f379: felt252, + f380: felt252, + f381: felt252, + f382: felt252, + f383: felt252, + f384: felt252, + f385: felt252, + f386: felt252, + f387: felt252, + f388: felt252, + f389: felt252, + f390: felt252, + f391: felt252, + f392: felt252, + f393: felt252, + f394: felt252, + f395: felt252, + f396: felt252, + f397: felt252, + f398: felt252, + f399: felt252, + f400: felt252, + f401: felt252, + f402: felt252, + f403: felt252, + f404: felt252, + f405: felt252, + f406: felt252, + f407: felt252, + f408: felt252, + f409: felt252, + f410: felt252, + f411: felt252, + f412: felt252, + f413: felt252, + f414: felt252, + f415: felt252, + f416: felt252, + f417: felt252, + f418: felt252, + f419: felt252, + f420: felt252, + f421: felt252, + f422: felt252, + f423: felt252, + f424: felt252, + f425: felt252, + f426: felt252, + f427: felt252, + f428: felt252, + f429: felt252, + f430: felt252, + f431: felt252, + f432: felt252, + f433: felt252, + f434: felt252, + f435: felt252, + f436: felt252, + f437: felt252, + f438: felt252, + f439: felt252, + f440: felt252, + f441: felt252, + f442: felt252, + f443: felt252, + f444: felt252, + f445: felt252, + f446: felt252, + f447: felt252, + f448: felt252, + f449: felt252, + f450: felt252, + f451: felt252, + f452: felt252, + f453: felt252, + f454: felt252, + f455: felt252, + f456: felt252, + f457: felt252, + f458: felt252, + f459: felt252, + f460: felt252, + f461: felt252, + f462: felt252, + f463: felt252, + f464: felt252, + f465: felt252, + f466: felt252, + f467: felt252, + f468: felt252, + f469: felt252, + f470: felt252, + f471: felt252, + f472: felt252, + f473: felt252, + f474: felt252, + f475: felt252, + f476: felt252, + f477: felt252, + f478: felt252, + f479: felt252, + f480: felt252, + f481: felt252, + f482: felt252, + f483: felt252, + f484: felt252, + f485: felt252, + f486: felt252, + f487: felt252, + f488: felt252, + f489: felt252, + f490: felt252, + f491: felt252, + f492: felt252, + f493: felt252, + f494: felt252, + f495: felt252, + f496: felt252, + f497: felt252, + f498: felt252, + f499: felt252, + f500: felt252, + f501: felt252, + f502: felt252, + f503: felt252, + f504: felt252, + f505: felt252, + f506: felt252, + f507: felt252, + f508: felt252, + f509: felt252, + f510: felt252, + f511: felt252, +) -> ( + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, + Falcon512Zq, +) { + let v0 = f256 * 1479; + let v1 = f0 + v0; + let v2 = f0 - v0; + let v3 = f384 * 1479; + let v4 = f128 + v3; + let v5 = f128 - v3; + let v6 = v4 * 4043; + let v7 = v1 + v6; + let v8 = v1 - v6; + let v9 = v5 * 5146; + let v10 = v2 + v9; + let v11 = v2 - v9; + let v12 = f320 * 1479; + let v13 = f64 + v12; + let v14 = f64 - v12; + let v15 = f448 * 1479; + let v16 = f192 + v15; + let v17 = f192 - v15; + let v18 = v16 * 4043; + let v19 = v13 + v18; + let v20 = v13 - v18; + let v21 = v17 * 5146; + let v22 = v14 + v21; + let v23 = v14 - v21; + let v24 = v19 * 5736; + let v25 = v7 + v24; + let v26 = v7 - v24; + let v27 = v20 * 4134; + let v28 = v8 + v27; + let v29 = v8 - v27; + let v30 = v22 * 722; + let v31 = v10 + v30; + let v32 = v10 - v30; + let v33 = v23 * 1305; + let v34 = v11 + v33; + let v35 = v11 - v33; + let v36 = f288 * 1479; + let v37 = f32 + v36; + let v38 = f32 - v36; + let v39 = f416 * 1479; + let v40 = f160 + v39; + let v41 = f160 - v39; + let v42 = v40 * 4043; + let v43 = v37 + v42; + let v44 = v37 - v42; + let v45 = v41 * 5146; + let v46 = v38 + v45; + let v47 = v38 - v45; + let v48 = f352 * 1479; + let v49 = f96 + v48; + let v50 = f96 - v48; + let v51 = f480 * 1479; + let v52 = f224 + v51; + let v53 = f224 - v51; + let v54 = v52 * 4043; + let v55 = v49 + v54; + let v56 = v49 - v54; + let v57 = v53 * 5146; + let v58 = v50 + v57; + let v59 = v50 - v57; + let v60 = v55 * 5736; + let v61 = v43 + v60; + let v62 = v43 - v60; + let v63 = v56 * 4134; + let v64 = v44 + v63; + let v65 = v44 - v63; + let v66 = v58 * 722; + let v67 = v46 + v66; + let v68 = v46 - v66; + let v69 = v59 * 1305; + let v70 = v47 + v69; + let v71 = v47 - v69; + let v72 = v61 * 1646; + let v73 = v25 + v72; + let v74 = v25 - v72; + let v75 = v62 * 1212; + let v76 = v26 + v75; + let v77 = v26 - v75; + let v78 = v64 * 5860; + let v79 = v28 + v78; + let v80 = v28 - v78; + let v81 = v65 * 3195; + let v82 = v29 + v81; + let v83 = v29 - v81; + let v84 = v67 * 2545; + let v85 = v31 + v84; + let v86 = v31 - v84; + let v87 = v68 * 3621; + let v88 = v32 + v87; + let v89 = v32 - v87; + let v90 = v70 * 3504; + let v91 = v34 + v90; + let v92 = v34 - v90; + let v93 = v71 * 3542; + let v94 = v35 + v93; + let v95 = v35 - v93; + let v96 = f272 * 1479; + let v97 = f16 + v96; + let v98 = f16 - v96; + let v99 = f400 * 1479; + let v100 = f144 + v99; + let v101 = f144 - v99; + let v102 = v100 * 4043; + let v103 = v97 + v102; + let v104 = v97 - v102; + let v105 = v101 * 5146; + let v106 = v98 + v105; + let v107 = v98 - v105; + let v108 = f336 * 1479; + let v109 = f80 + v108; + let v110 = f80 - v108; + let v111 = f464 * 1479; + let v112 = f208 + v111; + let v113 = f208 - v111; + let v114 = v112 * 4043; + let v115 = v109 + v114; + let v116 = v109 - v114; + let v117 = v113 * 5146; + let v118 = v110 + v117; + let v119 = v110 - v117; + let v120 = v115 * 5736; + let v121 = v103 + v120; + let v122 = v103 - v120; + let v123 = v116 * 4134; + let v124 = v104 + v123; + let v125 = v104 - v123; + let v126 = v118 * 722; + let v127 = v106 + v126; + let v128 = v106 - v126; + let v129 = v119 * 1305; + let v130 = v107 + v129; + let v131 = v107 - v129; + let v132 = f304 * 1479; + let v133 = f48 + v132; + let v134 = f48 - v132; + let v135 = f432 * 1479; + let v136 = f176 + v135; + let v137 = f176 - v135; + let v138 = v136 * 4043; + let v139 = v133 + v138; + let v140 = v133 - v138; + let v141 = v137 * 5146; + let v142 = v134 + v141; + let v143 = v134 - v141; + let v144 = f368 * 1479; + let v145 = f112 + v144; + let v146 = f112 - v144; + let v147 = f496 * 1479; + let v148 = f240 + v147; + let v149 = f240 - v147; + let v150 = v148 * 4043; + let v151 = v145 + v150; + let v152 = v145 - v150; + let v153 = v149 * 5146; + let v154 = v146 + v153; + let v155 = v146 - v153; + let v156 = v151 * 5736; + let v157 = v139 + v156; + let v158 = v139 - v156; + let v159 = v152 * 4134; + let v160 = v140 + v159; + let v161 = v140 - v159; + let v162 = v154 * 722; + let v163 = v142 + v162; + let v164 = v142 - v162; + let v165 = v155 * 1305; + let v166 = v143 + v165; + let v167 = v143 - v165; + let v168 = v157 * 1646; + let v169 = v121 + v168; + let v170 = v121 - v168; + let v171 = v158 * 1212; + let v172 = v122 + v171; + let v173 = v122 - v171; + let v174 = v160 * 5860; + let v175 = v124 + v174; + let v176 = v124 - v174; + let v177 = v161 * 3195; + let v178 = v125 + v177; + let v179 = v125 - v177; + let v180 = v163 * 2545; + let v181 = v127 + v180; + let v182 = v127 - v180; + let v183 = v164 * 3621; + let v184 = v128 + v183; + let v185 = v128 - v183; + let v186 = v166 * 3504; + let v187 = v130 + v186; + let v188 = v130 - v186; + let v189 = v167 * 3542; + let v190 = v131 + v189; + let v191 = v131 - v189; + let v192 = v169 * 4591; + let v193 = v73 + v192; + let v194 = v73 - v192; + let v195 = v170 * 5728; + let v196 = v74 + v195; + let v197 = v74 - v195; + let v198 = v172 * 5023; + let v199 = v76 + v198; + let v200 = v76 - v198; + let v201 = v173 * 5828; + let v202 = v77 + v201; + let v203 = v77 - v201; + let v204 = v175 * 4978; + let v205 = v79 + v204; + let v206 = v79 - v204; + let v207 = v176 * 1351; + let v208 = v80 + v207; + let v209 = v80 - v207; + let v210 = v178 * 3328; + let v211 = v82 + v210; + let v212 = v82 - v210; + let v213 = v179 * 5777; + let v214 = v83 + v213; + let v215 = v83 - v213; + let v216 = v181 * 2975; + let v217 = v85 + v216; + let v218 = v85 - v216; + let v219 = v182 * 563; + let v220 = v86 + v219; + let v221 = v86 - v219; + let v222 = v184 * 3006; + let v223 = v88 + v222; + let v224 = v88 - v222; + let v225 = v185 * 2744; + let v226 = v89 + v225; + let v227 = v89 - v225; + let v228 = v187 * 949; + let v229 = v91 + v228; + let v230 = v91 - v228; + let v231 = v188 * 2625; + let v232 = v92 + v231; + let v233 = v92 - v231; + let v234 = v190 * 4821; + let v235 = v94 + v234; + let v236 = v94 - v234; + let v237 = v191 * 2639; + let v238 = v95 + v237; + let v239 = v95 - v237; + let v240 = f264 * 1479; + let v241 = f8 + v240; + let v242 = f8 - v240; + let v243 = f392 * 1479; + let v244 = f136 + v243; + let v245 = f136 - v243; + let v246 = v244 * 4043; + let v247 = v241 + v246; + let v248 = v241 - v246; + let v249 = v245 * 5146; + let v250 = v242 + v249; + let v251 = v242 - v249; + let v252 = f328 * 1479; + let v253 = f72 + v252; + let v254 = f72 - v252; + let v255 = f456 * 1479; + let v256 = f200 + v255; + let v257 = f200 - v255; + let v258 = v256 * 4043; + let v259 = v253 + v258; + let v260 = v253 - v258; + let v261 = v257 * 5146; + let v262 = v254 + v261; + let v263 = v254 - v261; + let v264 = v259 * 5736; + let v265 = v247 + v264; + let v266 = v247 - v264; + let v267 = v260 * 4134; + let v268 = v248 + v267; + let v269 = v248 - v267; + let v270 = v262 * 722; + let v271 = v250 + v270; + let v272 = v250 - v270; + let v273 = v263 * 1305; + let v274 = v251 + v273; + let v275 = v251 - v273; + let v276 = f296 * 1479; + let v277 = f40 + v276; + let v278 = f40 - v276; + let v279 = f424 * 1479; + let v280 = f168 + v279; + let v281 = f168 - v279; + let v282 = v280 * 4043; + let v283 = v277 + v282; + let v284 = v277 - v282; + let v285 = v281 * 5146; + let v286 = v278 + v285; + let v287 = v278 - v285; + let v288 = f360 * 1479; + let v289 = f104 + v288; + let v290 = f104 - v288; + let v291 = f488 * 1479; + let v292 = f232 + v291; + let v293 = f232 - v291; + let v294 = v292 * 4043; + let v295 = v289 + v294; + let v296 = v289 - v294; + let v297 = v293 * 5146; + let v298 = v290 + v297; + let v299 = v290 - v297; + let v300 = v295 * 5736; + let v301 = v283 + v300; + let v302 = v283 - v300; + let v303 = v296 * 4134; + let v304 = v284 + v303; + let v305 = v284 - v303; + let v306 = v298 * 722; + let v307 = v286 + v306; + let v308 = v286 - v306; + let v309 = v299 * 1305; + let v310 = v287 + v309; + let v311 = v287 - v309; + let v312 = v301 * 1646; + let v313 = v265 + v312; + let v314 = v265 - v312; + let v315 = v302 * 1212; + let v316 = v266 + v315; + let v317 = v266 - v315; + let v318 = v304 * 5860; + let v319 = v268 + v318; + let v320 = v268 - v318; + let v321 = v305 * 3195; + let v322 = v269 + v321; + let v323 = v269 - v321; + let v324 = v307 * 2545; + let v325 = v271 + v324; + let v326 = v271 - v324; + let v327 = v308 * 3621; + let v328 = v272 + v327; + let v329 = v272 - v327; + let v330 = v310 * 3504; + let v331 = v274 + v330; + let v332 = v274 - v330; + let v333 = v311 * 3542; + let v334 = v275 + v333; + let v335 = v275 - v333; + let v336 = f280 * 1479; + let v337 = f24 + v336; + let v338 = f24 - v336; + let v339 = f408 * 1479; + let v340 = f152 + v339; + let v341 = f152 - v339; + let v342 = v340 * 4043; + let v343 = v337 + v342; + let v344 = v337 - v342; + let v345 = v341 * 5146; + let v346 = v338 + v345; + let v347 = v338 - v345; + let v348 = f344 * 1479; + let v349 = f88 + v348; + let v350 = f88 - v348; + let v351 = f472 * 1479; + let v352 = f216 + v351; + let v353 = f216 - v351; + let v354 = v352 * 4043; + let v355 = v349 + v354; + let v356 = v349 - v354; + let v357 = v353 * 5146; + let v358 = v350 + v357; + let v359 = v350 - v357; + let v360 = v355 * 5736; + let v361 = v343 + v360; + let v362 = v343 - v360; + let v363 = v356 * 4134; + let v364 = v344 + v363; + let v365 = v344 - v363; + let v366 = v358 * 722; + let v367 = v346 + v366; + let v368 = v346 - v366; + let v369 = v359 * 1305; + let v370 = v347 + v369; + let v371 = v347 - v369; + let v372 = f312 * 1479; + let v373 = f56 + v372; + let v374 = f56 - v372; + let v375 = f440 * 1479; + let v376 = f184 + v375; + let v377 = f184 - v375; + let v378 = v376 * 4043; + let v379 = v373 + v378; + let v380 = v373 - v378; + let v381 = v377 * 5146; + let v382 = v374 + v381; + let v383 = v374 - v381; + let v384 = f376 * 1479; + let v385 = f120 + v384; + let v386 = f120 - v384; + let v387 = f504 * 1479; + let v388 = f248 + v387; + let v389 = f248 - v387; + let v390 = v388 * 4043; + let v391 = v385 + v390; + let v392 = v385 - v390; + let v393 = v389 * 5146; + let v394 = v386 + v393; + let v395 = v386 - v393; + let v396 = v391 * 5736; + let v397 = v379 + v396; + let v398 = v379 - v396; + let v399 = v392 * 4134; + let v400 = v380 + v399; + let v401 = v380 - v399; + let v402 = v394 * 722; + let v403 = v382 + v402; + let v404 = v382 - v402; + let v405 = v395 * 1305; + let v406 = v383 + v405; + let v407 = v383 - v405; + let v408 = v397 * 1646; + let v409 = v361 + v408; + let v410 = v361 - v408; + let v411 = v398 * 1212; + let v412 = v362 + v411; + let v413 = v362 - v411; + let v414 = v400 * 5860; + let v415 = v364 + v414; + let v416 = v364 - v414; + let v417 = v401 * 3195; + let v418 = v365 + v417; + let v419 = v365 - v417; + let v420 = v403 * 2545; + let v421 = v367 + v420; + let v422 = v367 - v420; + let v423 = v404 * 3621; + let v424 = v368 + v423; + let v425 = v368 - v423; + let v426 = v406 * 3504; + let v427 = v370 + v426; + let v428 = v370 - v426; + let v429 = v407 * 3542; + let v430 = v371 + v429; + let v431 = v371 - v429; + let v432 = v409 * 4591; + let v433 = v313 + v432; + let v434 = v313 - v432; + let v435 = v410 * 5728; + let v436 = v314 + v435; + let v437 = v314 - v435; + let v438 = v412 * 5023; + let v439 = v316 + v438; + let v440 = v316 - v438; + let v441 = v413 * 5828; + let v442 = v317 + v441; + let v443 = v317 - v441; + let v444 = v415 * 4978; + let v445 = v319 + v444; + let v446 = v319 - v444; + let v447 = v416 * 1351; + let v448 = v320 + v447; + let v449 = v320 - v447; + let v450 = v418 * 3328; + let v451 = v322 + v450; + let v452 = v322 - v450; + let v453 = v419 * 5777; + let v454 = v323 + v453; + let v455 = v323 - v453; + let v456 = v421 * 2975; + let v457 = v325 + v456; + let v458 = v325 - v456; + let v459 = v422 * 563; + let v460 = v326 + v459; + let v461 = v326 - v459; + let v462 = v424 * 3006; + let v463 = v328 + v462; + let v464 = v328 - v462; + let v465 = v425 * 2744; + let v466 = v329 + v465; + let v467 = v329 - v465; + let v468 = v427 * 949; + let v469 = v331 + v468; + let v470 = v331 - v468; + let v471 = v428 * 2625; + let v472 = v332 + v471; + let v473 = v332 - v471; + let v474 = v430 * 4821; + let v475 = v334 + v474; + let v476 = v334 - v474; + let v477 = v431 * 2639; + let v478 = v335 + v477; + let v479 = v335 - v477; + let v480 = v433 * 1000; + let v481 = v193 + v480; + let v482 = v193 - v480; + let v483 = v434 * 4320; + let v484 = v194 + v483; + let v485 = v194 - v483; + let v486 = v436 * 3091; + let v487 = v196 + v486; + let v488 = v196 - v486; + let v489 = v437 * 81; + let v490 = v197 + v489; + let v491 = v197 - v489; + let v492 = v439 * 2963; + let v493 = v199 + v492; + let v494 = v199 - v492; + let v495 = v440 * 4896; + let v496 = v200 + v495; + let v497 = v200 - v495; + let v498 = v442 * 3051; + let v499 = v202 + v498; + let v500 = v202 - v498; + let v501 = v443 * 2366; + let v502 = v203 + v501; + let v503 = v203 - v501; + let v504 = v445 * 1853; + let v505 = v205 + v504; + let v506 = v205 - v504; + let v507 = v446 * 140; + let v508 = v206 + v507; + let v509 = v206 - v507; + let v510 = v448 * 4611; + let v511 = v208 + v510; + let v512 = v208 - v510; + let v513 = v449 * 726; + let v514 = v209 + v513; + let v515 = v209 - v513; + let v516 = v451 * 4255; + let v517 = v211 + v516; + let v518 = v211 - v516; + let v519 = v452 * 1177; + let v520 = v212 + v519; + let v521 = v212 - v519; + let v522 = v454 * 2768; + let v523 = v214 + v522; + let v524 = v214 - v522; + let v525 = v455 * 1635; + let v526 = v215 + v525; + let v527 = v215 - v525; + let v528 = v457 * 3712; + let v529 = v217 + v528; + let v530 = v217 - v528; + let v531 = v458 * 3135; + let v532 = v218 + v531; + let v533 = v218 - v531; + let v534 = v460 * 2747; + let v535 = v220 + v534; + let v536 = v220 - v534; + let v537 = v461 * 4846; + let v538 = v221 + v537; + let v539 = v221 - v537; + let v540 = v463 * 3553; + let v541 = v223 + v540; + let v542 = v223 - v540; + let v543 = v464 * 4805; + let v544 = v224 + v543; + let v545 = v224 - v543; + let v546 = v466 * 2294; + let v547 = v226 + v546; + let v548 = v226 - v546; + let v549 = v467 * 1062; + let v550 = v227 + v549; + let v551 = v227 - v549; + let v552 = v469 * 1326; + let v553 = v229 + v552; + let v554 = v229 - v552; + let v555 = v470 * 5086; + let v556 = v230 + v555; + let v557 = v230 - v555; + let v558 = v472 * 3014; + let v559 = v232 + v558; + let v560 = v232 - v558; + let v561 = v473 * 3201; + let v562 = v233 + v561; + let v563 = v233 - v561; + let v564 = v475 * 1170; + let v565 = v235 + v564; + let v566 = v235 - v564; + let v567 = v476 * 2319; + let v568 = v236 + v567; + let v569 = v236 - v567; + let v570 = v478 * 955; + let v571 = v238 + v570; + let v572 = v238 - v570; + let v573 = v479 * 790; + let v574 = v239 + v573; + let v575 = v239 - v573; + let v576 = f260 * 1479; + let v577 = f4 + v576; + let v578 = f4 - v576; + let v579 = f388 * 1479; + let v580 = f132 + v579; + let v581 = f132 - v579; + let v582 = v580 * 4043; + let v583 = v577 + v582; + let v584 = v577 - v582; + let v585 = v581 * 5146; + let v586 = v578 + v585; + let v587 = v578 - v585; + let v588 = f324 * 1479; + let v589 = f68 + v588; + let v590 = f68 - v588; + let v591 = f452 * 1479; + let v592 = f196 + v591; + let v593 = f196 - v591; + let v594 = v592 * 4043; + let v595 = v589 + v594; + let v596 = v589 - v594; + let v597 = v593 * 5146; + let v598 = v590 + v597; + let v599 = v590 - v597; + let v600 = v595 * 5736; + let v601 = v583 + v600; + let v602 = v583 - v600; + let v603 = v596 * 4134; + let v604 = v584 + v603; + let v605 = v584 - v603; + let v606 = v598 * 722; + let v607 = v586 + v606; + let v608 = v586 - v606; + let v609 = v599 * 1305; + let v610 = v587 + v609; + let v611 = v587 - v609; + let v612 = f292 * 1479; + let v613 = f36 + v612; + let v614 = f36 - v612; + let v615 = f420 * 1479; + let v616 = f164 + v615; + let v617 = f164 - v615; + let v618 = v616 * 4043; + let v619 = v613 + v618; + let v620 = v613 - v618; + let v621 = v617 * 5146; + let v622 = v614 + v621; + let v623 = v614 - v621; + let v624 = f356 * 1479; + let v625 = f100 + v624; + let v626 = f100 - v624; + let v627 = f484 * 1479; + let v628 = f228 + v627; + let v629 = f228 - v627; + let v630 = v628 * 4043; + let v631 = v625 + v630; + let v632 = v625 - v630; + let v633 = v629 * 5146; + let v634 = v626 + v633; + let v635 = v626 - v633; + let v636 = v631 * 5736; + let v637 = v619 + v636; + let v638 = v619 - v636; + let v639 = v632 * 4134; + let v640 = v620 + v639; + let v641 = v620 - v639; + let v642 = v634 * 722; + let v643 = v622 + v642; + let v644 = v622 - v642; + let v645 = v635 * 1305; + let v646 = v623 + v645; + let v647 = v623 - v645; + let v648 = v637 * 1646; + let v649 = v601 + v648; + let v650 = v601 - v648; + let v651 = v638 * 1212; + let v652 = v602 + v651; + let v653 = v602 - v651; + let v654 = v640 * 5860; + let v655 = v604 + v654; + let v656 = v604 - v654; + let v657 = v641 * 3195; + let v658 = v605 + v657; + let v659 = v605 - v657; + let v660 = v643 * 2545; + let v661 = v607 + v660; + let v662 = v607 - v660; + let v663 = v644 * 3621; + let v664 = v608 + v663; + let v665 = v608 - v663; + let v666 = v646 * 3504; + let v667 = v610 + v666; + let v668 = v610 - v666; + let v669 = v647 * 3542; + let v670 = v611 + v669; + let v671 = v611 - v669; + let v672 = f276 * 1479; + let v673 = f20 + v672; + let v674 = f20 - v672; + let v675 = f404 * 1479; + let v676 = f148 + v675; + let v677 = f148 - v675; + let v678 = v676 * 4043; + let v679 = v673 + v678; + let v680 = v673 - v678; + let v681 = v677 * 5146; + let v682 = v674 + v681; + let v683 = v674 - v681; + let v684 = f340 * 1479; + let v685 = f84 + v684; + let v686 = f84 - v684; + let v687 = f468 * 1479; + let v688 = f212 + v687; + let v689 = f212 - v687; + let v690 = v688 * 4043; + let v691 = v685 + v690; + let v692 = v685 - v690; + let v693 = v689 * 5146; + let v694 = v686 + v693; + let v695 = v686 - v693; + let v696 = v691 * 5736; + let v697 = v679 + v696; + let v698 = v679 - v696; + let v699 = v692 * 4134; + let v700 = v680 + v699; + let v701 = v680 - v699; + let v702 = v694 * 722; + let v703 = v682 + v702; + let v704 = v682 - v702; + let v705 = v695 * 1305; + let v706 = v683 + v705; + let v707 = v683 - v705; + let v708 = f308 * 1479; + let v709 = f52 + v708; + let v710 = f52 - v708; + let v711 = f436 * 1479; + let v712 = f180 + v711; + let v713 = f180 - v711; + let v714 = v712 * 4043; + let v715 = v709 + v714; + let v716 = v709 - v714; + let v717 = v713 * 5146; + let v718 = v710 + v717; + let v719 = v710 - v717; + let v720 = f372 * 1479; + let v721 = f116 + v720; + let v722 = f116 - v720; + let v723 = f500 * 1479; + let v724 = f244 + v723; + let v725 = f244 - v723; + let v726 = v724 * 4043; + let v727 = v721 + v726; + let v728 = v721 - v726; + let v729 = v725 * 5146; + let v730 = v722 + v729; + let v731 = v722 - v729; + let v732 = v727 * 5736; + let v733 = v715 + v732; + let v734 = v715 - v732; + let v735 = v728 * 4134; + let v736 = v716 + v735; + let v737 = v716 - v735; + let v738 = v730 * 722; + let v739 = v718 + v738; + let v740 = v718 - v738; + let v741 = v731 * 1305; + let v742 = v719 + v741; + let v743 = v719 - v741; + let v744 = v733 * 1646; + let v745 = v697 + v744; + let v746 = v697 - v744; + let v747 = v734 * 1212; + let v748 = v698 + v747; + let v749 = v698 - v747; + let v750 = v736 * 5860; + let v751 = v700 + v750; + let v752 = v700 - v750; + let v753 = v737 * 3195; + let v754 = v701 + v753; + let v755 = v701 - v753; + let v756 = v739 * 2545; + let v757 = v703 + v756; + let v758 = v703 - v756; + let v759 = v740 * 3621; + let v760 = v704 + v759; + let v761 = v704 - v759; + let v762 = v742 * 3504; + let v763 = v706 + v762; + let v764 = v706 - v762; + let v765 = v743 * 3542; + let v766 = v707 + v765; + let v767 = v707 - v765; + let v768 = v745 * 4591; + let v769 = v649 + v768; + let v770 = v649 - v768; + let v771 = v746 * 5728; + let v772 = v650 + v771; + let v773 = v650 - v771; + let v774 = v748 * 5023; + let v775 = v652 + v774; + let v776 = v652 - v774; + let v777 = v749 * 5828; + let v778 = v653 + v777; + let v779 = v653 - v777; + let v780 = v751 * 4978; + let v781 = v655 + v780; + let v782 = v655 - v780; + let v783 = v752 * 1351; + let v784 = v656 + v783; + let v785 = v656 - v783; + let v786 = v754 * 3328; + let v787 = v658 + v786; + let v788 = v658 - v786; + let v789 = v755 * 5777; + let v790 = v659 + v789; + let v791 = v659 - v789; + let v792 = v757 * 2975; + let v793 = v661 + v792; + let v794 = v661 - v792; + let v795 = v758 * 563; + let v796 = v662 + v795; + let v797 = v662 - v795; + let v798 = v760 * 3006; + let v799 = v664 + v798; + let v800 = v664 - v798; + let v801 = v761 * 2744; + let v802 = v665 + v801; + let v803 = v665 - v801; + let v804 = v763 * 949; + let v805 = v667 + v804; + let v806 = v667 - v804; + let v807 = v764 * 2625; + let v808 = v668 + v807; + let v809 = v668 - v807; + let v810 = v766 * 4821; + let v811 = v670 + v810; + let v812 = v670 - v810; + let v813 = v767 * 2639; + let v814 = v671 + v813; + let v815 = v671 - v813; + let v816 = f268 * 1479; + let v817 = f12 + v816; + let v818 = f12 - v816; + let v819 = f396 * 1479; + let v820 = f140 + v819; + let v821 = f140 - v819; + let v822 = v820 * 4043; + let v823 = v817 + v822; + let v824 = v817 - v822; + let v825 = v821 * 5146; + let v826 = v818 + v825; + let v827 = v818 - v825; + let v828 = f332 * 1479; + let v829 = f76 + v828; + let v830 = f76 - v828; + let v831 = f460 * 1479; + let v832 = f204 + v831; + let v833 = f204 - v831; + let v834 = v832 * 4043; + let v835 = v829 + v834; + let v836 = v829 - v834; + let v837 = v833 * 5146; + let v838 = v830 + v837; + let v839 = v830 - v837; + let v840 = v835 * 5736; + let v841 = v823 + v840; + let v842 = v823 - v840; + let v843 = v836 * 4134; + let v844 = v824 + v843; + let v845 = v824 - v843; + let v846 = v838 * 722; + let v847 = v826 + v846; + let v848 = v826 - v846; + let v849 = v839 * 1305; + let v850 = v827 + v849; + let v851 = v827 - v849; + let v852 = f300 * 1479; + let v853 = f44 + v852; + let v854 = f44 - v852; + let v855 = f428 * 1479; + let v856 = f172 + v855; + let v857 = f172 - v855; + let v858 = v856 * 4043; + let v859 = v853 + v858; + let v860 = v853 - v858; + let v861 = v857 * 5146; + let v862 = v854 + v861; + let v863 = v854 - v861; + let v864 = f364 * 1479; + let v865 = f108 + v864; + let v866 = f108 - v864; + let v867 = f492 * 1479; + let v868 = f236 + v867; + let v869 = f236 - v867; + let v870 = v868 * 4043; + let v871 = v865 + v870; + let v872 = v865 - v870; + let v873 = v869 * 5146; + let v874 = v866 + v873; + let v875 = v866 - v873; + let v876 = v871 * 5736; + let v877 = v859 + v876; + let v878 = v859 - v876; + let v879 = v872 * 4134; + let v880 = v860 + v879; + let v881 = v860 - v879; + let v882 = v874 * 722; + let v883 = v862 + v882; + let v884 = v862 - v882; + let v885 = v875 * 1305; + let v886 = v863 + v885; + let v887 = v863 - v885; + let v888 = v877 * 1646; + let v889 = v841 + v888; + let v890 = v841 - v888; + let v891 = v878 * 1212; + let v892 = v842 + v891; + let v893 = v842 - v891; + let v894 = v880 * 5860; + let v895 = v844 + v894; + let v896 = v844 - v894; + let v897 = v881 * 3195; + let v898 = v845 + v897; + let v899 = v845 - v897; + let v900 = v883 * 2545; + let v901 = v847 + v900; + let v902 = v847 - v900; + let v903 = v884 * 3621; + let v904 = v848 + v903; + let v905 = v848 - v903; + let v906 = v886 * 3504; + let v907 = v850 + v906; + let v908 = v850 - v906; + let v909 = v887 * 3542; + let v910 = v851 + v909; + let v911 = v851 - v909; + let v912 = f284 * 1479; + let v913 = f28 + v912; + let v914 = f28 - v912; + let v915 = f412 * 1479; + let v916 = f156 + v915; + let v917 = f156 - v915; + let v918 = v916 * 4043; + let v919 = v913 + v918; + let v920 = v913 - v918; + let v921 = v917 * 5146; + let v922 = v914 + v921; + let v923 = v914 - v921; + let v924 = f348 * 1479; + let v925 = f92 + v924; + let v926 = f92 - v924; + let v927 = f476 * 1479; + let v928 = f220 + v927; + let v929 = f220 - v927; + let v930 = v928 * 4043; + let v931 = v925 + v930; + let v932 = v925 - v930; + let v933 = v929 * 5146; + let v934 = v926 + v933; + let v935 = v926 - v933; + let v936 = v931 * 5736; + let v937 = v919 + v936; + let v938 = v919 - v936; + let v939 = v932 * 4134; + let v940 = v920 + v939; + let v941 = v920 - v939; + let v942 = v934 * 722; + let v943 = v922 + v942; + let v944 = v922 - v942; + let v945 = v935 * 1305; + let v946 = v923 + v945; + let v947 = v923 - v945; + let v948 = f316 * 1479; + let v949 = f60 + v948; + let v950 = f60 - v948; + let v951 = f444 * 1479; + let v952 = f188 + v951; + let v953 = f188 - v951; + let v954 = v952 * 4043; + let v955 = v949 + v954; + let v956 = v949 - v954; + let v957 = v953 * 5146; + let v958 = v950 + v957; + let v959 = v950 - v957; + let v960 = f380 * 1479; + let v961 = f124 + v960; + let v962 = f124 - v960; + let v963 = f508 * 1479; + let v964 = f252 + v963; + let v965 = f252 - v963; + let v966 = v964 * 4043; + let v967 = v961 + v966; + let v968 = v961 - v966; + let v969 = v965 * 5146; + let v970 = v962 + v969; + let v971 = v962 - v969; + let v972 = v967 * 5736; + let v973 = v955 + v972; + let v974 = v955 - v972; + let v975 = v968 * 4134; + let v976 = v956 + v975; + let v977 = v956 - v975; + let v978 = v970 * 722; + let v979 = v958 + v978; + let v980 = v958 - v978; + let v981 = v971 * 1305; + let v982 = v959 + v981; + let v983 = v959 - v981; + let v984 = v973 * 1646; + let v985 = v937 + v984; + let v986 = v937 - v984; + let v987 = v974 * 1212; + let v988 = v938 + v987; + let v989 = v938 - v987; + let v990 = v976 * 5860; + let v991 = v940 + v990; + let v992 = v940 - v990; + let v993 = v977 * 3195; + let v994 = v941 + v993; + let v995 = v941 - v993; + let v996 = v979 * 2545; + let v997 = v943 + v996; + let v998 = v943 - v996; + let v999 = v980 * 3621; + let v1000 = v944 + v999; + let v1001 = v944 - v999; + let v1002 = v982 * 3504; + let v1003 = v946 + v1002; + let v1004 = v946 - v1002; + let v1005 = v983 * 3542; + let v1006 = v947 + v1005; + let v1007 = v947 - v1005; + let v1008 = v985 * 4591; + let v1009 = v889 + v1008; + let v1010 = v889 - v1008; + let v1011 = v986 * 5728; + let v1012 = v890 + v1011; + let v1013 = v890 - v1011; + let v1014 = v988 * 5023; + let v1015 = v892 + v1014; + let v1016 = v892 - v1014; + let v1017 = v989 * 5828; + let v1018 = v893 + v1017; + let v1019 = v893 - v1017; + let v1020 = v991 * 4978; + let v1021 = v895 + v1020; + let v1022 = v895 - v1020; + let v1023 = v992 * 1351; + let v1024 = v896 + v1023; + let v1025 = v896 - v1023; + let v1026 = v994 * 3328; + let v1027 = v898 + v1026; + let v1028 = v898 - v1026; + let v1029 = v995 * 5777; + let v1030 = v899 + v1029; + let v1031 = v899 - v1029; + let v1032 = v997 * 2975; + let v1033 = v901 + v1032; + let v1034 = v901 - v1032; + let v1035 = v998 * 563; + let v1036 = v902 + v1035; + let v1037 = v902 - v1035; + let v1038 = v1000 * 3006; + let v1039 = v904 + v1038; + let v1040 = v904 - v1038; + let v1041 = v1001 * 2744; + let v1042 = v905 + v1041; + let v1043 = v905 - v1041; + let v1044 = v1003 * 949; + let v1045 = v907 + v1044; + let v1046 = v907 - v1044; + let v1047 = v1004 * 2625; + let v1048 = v908 + v1047; + let v1049 = v908 - v1047; + let v1050 = v1006 * 4821; + let v1051 = v910 + v1050; + let v1052 = v910 - v1050; + let v1053 = v1007 * 2639; + let v1054 = v911 + v1053; + let v1055 = v911 - v1053; + let v1056 = v1009 * 1000; + let v1057 = v769 + v1056; + let v1058 = v769 - v1056; + let v1059 = v1010 * 4320; + let v1060 = v770 + v1059; + let v1061 = v770 - v1059; + let v1062 = v1012 * 3091; + let v1063 = v772 + v1062; + let v1064 = v772 - v1062; + let v1065 = v1013 * 81; + let v1066 = v773 + v1065; + let v1067 = v773 - v1065; + let v1068 = v1015 * 2963; + let v1069 = v775 + v1068; + let v1070 = v775 - v1068; + let v1071 = v1016 * 4896; + let v1072 = v776 + v1071; + let v1073 = v776 - v1071; + let v1074 = v1018 * 3051; + let v1075 = v778 + v1074; + let v1076 = v778 - v1074; + let v1077 = v1019 * 2366; + let v1078 = v779 + v1077; + let v1079 = v779 - v1077; + let v1080 = v1021 * 1853; + let v1081 = v781 + v1080; + let v1082 = v781 - v1080; + let v1083 = v1022 * 140; + let v1084 = v782 + v1083; + let v1085 = v782 - v1083; + let v1086 = v1024 * 4611; + let v1087 = v784 + v1086; + let v1088 = v784 - v1086; + let v1089 = v1025 * 726; + let v1090 = v785 + v1089; + let v1091 = v785 - v1089; + let v1092 = v1027 * 4255; + let v1093 = v787 + v1092; + let v1094 = v787 - v1092; + let v1095 = v1028 * 1177; + let v1096 = v788 + v1095; + let v1097 = v788 - v1095; + let v1098 = v1030 * 2768; + let v1099 = v790 + v1098; + let v1100 = v790 - v1098; + let v1101 = v1031 * 1635; + let v1102 = v791 + v1101; + let v1103 = v791 - v1101; + let v1104 = v1033 * 3712; + let v1105 = v793 + v1104; + let v1106 = v793 - v1104; + let v1107 = v1034 * 3135; + let v1108 = v794 + v1107; + let v1109 = v794 - v1107; + let v1110 = v1036 * 2747; + let v1111 = v796 + v1110; + let v1112 = v796 - v1110; + let v1113 = v1037 * 4846; + let v1114 = v797 + v1113; + let v1115 = v797 - v1113; + let v1116 = v1039 * 3553; + let v1117 = v799 + v1116; + let v1118 = v799 - v1116; + let v1119 = v1040 * 4805; + let v1120 = v800 + v1119; + let v1121 = v800 - v1119; + let v1122 = v1042 * 2294; + let v1123 = v802 + v1122; + let v1124 = v802 - v1122; + let v1125 = v1043 * 1062; + let v1126 = v803 + v1125; + let v1127 = v803 - v1125; + let v1128 = v1045 * 1326; + let v1129 = v805 + v1128; + let v1130 = v805 - v1128; + let v1131 = v1046 * 5086; + let v1132 = v806 + v1131; + let v1133 = v806 - v1131; + let v1134 = v1048 * 3014; + let v1135 = v808 + v1134; + let v1136 = v808 - v1134; + let v1137 = v1049 * 3201; + let v1138 = v809 + v1137; + let v1139 = v809 - v1137; + let v1140 = v1051 * 1170; + let v1141 = v811 + v1140; + let v1142 = v811 - v1140; + let v1143 = v1052 * 2319; + let v1144 = v812 + v1143; + let v1145 = v812 - v1143; + let v1146 = v1054 * 955; + let v1147 = v814 + v1146; + let v1148 = v814 - v1146; + let v1149 = v1055 * 790; + let v1150 = v815 + v1149; + let v1151 = v815 - v1149; + let v1152 = v1057 * 544; + let v1153 = v481 + v1152; + let v1154 = v481 - v1152; + let v1155 = v1058 * 5791; + let v1156 = v482 + v1155; + let v1157 = v482 - v1155; + let v1158 = v1060 * 339; + let v1159 = v484 + v1158; + let v1160 = v484 - v1158; + let v1161 = v1061 * 2468; + let v1162 = v485 + v1161; + let v1163 = v485 - v1161; + let v1164 = v1063 * 2842; + let v1165 = v487 + v1164; + let v1166 = v487 - v1164; + let v1167 = v1064 * 480; + let v1168 = v488 + v1167; + let v1169 = v488 - v1167; + let v1170 = v1066 * 9; + let v1171 = v490 + v1170; + let v1172 = v490 - v1170; + let v1173 = v1067 * 1022; + let v1174 = v491 + v1173; + let v1175 = v491 - v1173; + let v1176 = v1069 * 4278; + let v1177 = v493 + v1176; + let v1178 = v493 - v1176; + let v1179 = v1070 * 1673; + let v1180 = v494 + v1179; + let v1181 = v494 - v1179; + let v1182 = v1072 * 4989; + let v1183 = v496 + v1182; + let v1184 = v496 - v1182; + let v1185 = v1073 * 5331; + let v1186 = v497 + v1185; + let v1187 = v497 - v1185; + let v1188 = v1075 * 3584; + let v1189 = v499 + v1188; + let v1190 = v499 - v1188; + let v1191 = v1076 * 4177; + let v1192 = v500 + v1191; + let v1193 = v500 - v1191; + let v1194 = v1078 * 1381; + let v1195 = v502 + v1194; + let v1196 = v502 - v1194; + let v1197 = v1079 * 2525; + let v1198 = v503 + v1197; + let v1199 = v503 - v1197; + let v1200 = v1081 * 2396; + let v1201 = v505 + v1200; + let v1202 = v505 - v1200; + let v1203 = v1082 * 4452; + let v1204 = v506 + v1203; + let v1205 = v506 - v1203; + let v1206 = v1084 * 3296; + let v1207 = v508 + v1206; + let v1208 = v508 - v1206; + let v1209 = v1085 * 3949; + let v1210 = v509 + v1209; + let v1211 = v509 - v1209; + let v1212 = v1087 * 130; + let v1213 = v511 + v1212; + let v1214 = v511 - v1212; + let v1215 = v1088 * 4354; + let v1216 = v512 + v1215; + let v1217 = v512 - v1215; + let v1218 = v1090 * 5374; + let v1219 = v514 + v1218; + let v1220 = v514 - v1218; + let v1221 = v1091 * 2837; + let v1222 = v515 + v1221; + let v1223 = v515 - v1221; + let v1224 = v1093 * 5767; + let v1225 = v517 + v1224; + let v1226 = v517 - v1224; + let v1227 = v1094 * 827; + let v1228 = v518 + v1227; + let v1229 = v518 - v1227; + let v1230 = v1096 * 3748; + let v1231 = v520 + v1230; + let v1232 = v520 - v1230; + let v1233 = v1097 * 953; + let v1234 = v521 + v1233; + let v1235 = v521 - v1233; + let v1236 = v1099 * 5067; + let v1237 = v523 + v1236; + let v1238 = v523 - v1236; + let v1239 = v1100 * 2197; + let v1240 = v524 + v1239; + let v1241 = v524 - v1239; + let v1242 = v1102 * 118; + let v1243 = v526 + v1242; + let v1244 = v526 - v1242; + let v1245 = v1103 * 2476; + let v1246 = v527 + v1245; + let v1247 = v527 - v1245; + let v1248 = v1105 * 2548; + let v1249 = v529 + v1248; + let v1250 = v529 - v1248; + let v1251 = v1106 * 4231; + let v1252 = v530 + v1251; + let v1253 = v530 - v1251; + let v1254 = v1108 * 355; + let v1255 = v532 + v1254; + let v1256 = v532 - v1254; + let v1257 = v1109 * 3382; + let v1258 = v533 + v1257; + let v1259 = v533 - v1257; + let v1260 = v1111 * 3707; + let v1261 = v535 + v1260; + let v1262 = v535 - v1260; + let v1263 = v1112 * 1759; + let v1264 = v536 + v1263; + let v1265 = v536 - v1263; + let v1266 = v1114 * 3694; + let v1267 = v538 + v1266; + let v1268 = v538 - v1266; + let v1269 = v1115 * 5179; + let v1270 = v539 + v1269; + let v1271 = v539 - v1269; + let v1272 = v1117 * 5542; + let v1273 = v541 + v1272; + let v1274 = v541 - v1272; + let v1275 = v1118 * 145; + let v1276 = v542 + v1275; + let v1277 = v542 - v1275; + let v1278 = v1120 * 3637; + let v1279 = v544 + v1278; + let v1280 = v544 - v1278; + let v1281 = v1121 * 3459; + let v1282 = v545 + v1281; + let v1283 = v545 - v1281; + let v1284 = v1123 * 5911; + let v1285 = v547 + v1284; + let v1286 = v547 - v1284; + let v1287 = v1124 * 4890; + let v1288 = v548 + v1287; + let v1289 = v548 - v1287; + let v1290 = v1126 * 3932; + let v1291 = v550 + v1290; + let v1292 = v550 - v1290; + let v1293 = v1127 * 2731; + let v1294 = v551 + v1293; + let v1295 = v551 - v1293; + let v1296 = v1129 * 2089; + let v1297 = v553 + v1296; + let v1298 = v553 - v1296; + let v1299 = v1130 * 5092; + let v1300 = v554 + v1299; + let v1301 = v554 - v1299; + let v1302 = v1132 * 2881; + let v1303 = v556 + v1302; + let v1304 = v556 - v1302; + let v1305 = v1133 * 3284; + let v1306 = v557 + v1305; + let v1307 = v557 - v1305; + let v1308 = v1135 * 729; + let v1309 = v559 + v1308; + let v1310 = v559 - v1308; + let v1311 = v1136 * 3241; + let v1312 = v560 + v1311; + let v1313 = v560 - v1311; + let v1314 = v1138 * 3289; + let v1315 = v562 + v1314; + let v1316 = v562 - v1314; + let v1317 = v1139 * 2013; + let v1318 = v563 + v1317; + let v1319 = v563 - v1317; + let v1320 = v1141 * 5755; + let v1321 = v565 + v1320; + let v1322 = v565 - v1320; + let v1323 = v1142 * 4632; + let v1324 = v566 + v1323; + let v1325 = v566 - v1323; + let v1326 = v1144 * 1260; + let v1327 = v568 + v1326; + let v1328 = v568 - v1326; + let v1329 = v1145 * 4388; + let v1330 = v569 + v1329; + let v1331 = v569 - v1329; + let v1332 = v1147 * 334; + let v1333 = v571 + v1332; + let v1334 = v571 - v1332; + let v1335 = v1148 * 2426; + let v1336 = v572 + v1335; + let v1337 = v572 - v1335; + let v1338 = v1150 * 1696; + let v1339 = v574 + v1338; + let v1340 = v574 - v1338; + let v1341 = v1151 * 1428; + let v1342 = v575 + v1341; + let v1343 = v575 - v1341; + let v1344 = f258 * 1479; + let v1345 = f2 + v1344; + let v1346 = f2 - v1344; + let v1347 = f386 * 1479; + let v1348 = f130 + v1347; + let v1349 = f130 - v1347; + let v1350 = v1348 * 4043; + let v1351 = v1345 + v1350; + let v1352 = v1345 - v1350; + let v1353 = v1349 * 5146; + let v1354 = v1346 + v1353; + let v1355 = v1346 - v1353; + let v1356 = f322 * 1479; + let v1357 = f66 + v1356; + let v1358 = f66 - v1356; + let v1359 = f450 * 1479; + let v1360 = f194 + v1359; + let v1361 = f194 - v1359; + let v1362 = v1360 * 4043; + let v1363 = v1357 + v1362; + let v1364 = v1357 - v1362; + let v1365 = v1361 * 5146; + let v1366 = v1358 + v1365; + let v1367 = v1358 - v1365; + let v1368 = v1363 * 5736; + let v1369 = v1351 + v1368; + let v1370 = v1351 - v1368; + let v1371 = v1364 * 4134; + let v1372 = v1352 + v1371; + let v1373 = v1352 - v1371; + let v1374 = v1366 * 722; + let v1375 = v1354 + v1374; + let v1376 = v1354 - v1374; + let v1377 = v1367 * 1305; + let v1378 = v1355 + v1377; + let v1379 = v1355 - v1377; + let v1380 = f290 * 1479; + let v1381 = f34 + v1380; + let v1382 = f34 - v1380; + let v1383 = f418 * 1479; + let v1384 = f162 + v1383; + let v1385 = f162 - v1383; + let v1386 = v1384 * 4043; + let v1387 = v1381 + v1386; + let v1388 = v1381 - v1386; + let v1389 = v1385 * 5146; + let v1390 = v1382 + v1389; + let v1391 = v1382 - v1389; + let v1392 = f354 * 1479; + let v1393 = f98 + v1392; + let v1394 = f98 - v1392; + let v1395 = f482 * 1479; + let v1396 = f226 + v1395; + let v1397 = f226 - v1395; + let v1398 = v1396 * 4043; + let v1399 = v1393 + v1398; + let v1400 = v1393 - v1398; + let v1401 = v1397 * 5146; + let v1402 = v1394 + v1401; + let v1403 = v1394 - v1401; + let v1404 = v1399 * 5736; + let v1405 = v1387 + v1404; + let v1406 = v1387 - v1404; + let v1407 = v1400 * 4134; + let v1408 = v1388 + v1407; + let v1409 = v1388 - v1407; + let v1410 = v1402 * 722; + let v1411 = v1390 + v1410; + let v1412 = v1390 - v1410; + let v1413 = v1403 * 1305; + let v1414 = v1391 + v1413; + let v1415 = v1391 - v1413; + let v1416 = v1405 * 1646; + let v1417 = v1369 + v1416; + let v1418 = v1369 - v1416; + let v1419 = v1406 * 1212; + let v1420 = v1370 + v1419; + let v1421 = v1370 - v1419; + let v1422 = v1408 * 5860; + let v1423 = v1372 + v1422; + let v1424 = v1372 - v1422; + let v1425 = v1409 * 3195; + let v1426 = v1373 + v1425; + let v1427 = v1373 - v1425; + let v1428 = v1411 * 2545; + let v1429 = v1375 + v1428; + let v1430 = v1375 - v1428; + let v1431 = v1412 * 3621; + let v1432 = v1376 + v1431; + let v1433 = v1376 - v1431; + let v1434 = v1414 * 3504; + let v1435 = v1378 + v1434; + let v1436 = v1378 - v1434; + let v1437 = v1415 * 3542; + let v1438 = v1379 + v1437; + let v1439 = v1379 - v1437; + let v1440 = f274 * 1479; + let v1441 = f18 + v1440; + let v1442 = f18 - v1440; + let v1443 = f402 * 1479; + let v1444 = f146 + v1443; + let v1445 = f146 - v1443; + let v1446 = v1444 * 4043; + let v1447 = v1441 + v1446; + let v1448 = v1441 - v1446; + let v1449 = v1445 * 5146; + let v1450 = v1442 + v1449; + let v1451 = v1442 - v1449; + let v1452 = f338 * 1479; + let v1453 = f82 + v1452; + let v1454 = f82 - v1452; + let v1455 = f466 * 1479; + let v1456 = f210 + v1455; + let v1457 = f210 - v1455; + let v1458 = v1456 * 4043; + let v1459 = v1453 + v1458; + let v1460 = v1453 - v1458; + let v1461 = v1457 * 5146; + let v1462 = v1454 + v1461; + let v1463 = v1454 - v1461; + let v1464 = v1459 * 5736; + let v1465 = v1447 + v1464; + let v1466 = v1447 - v1464; + let v1467 = v1460 * 4134; + let v1468 = v1448 + v1467; + let v1469 = v1448 - v1467; + let v1470 = v1462 * 722; + let v1471 = v1450 + v1470; + let v1472 = v1450 - v1470; + let v1473 = v1463 * 1305; + let v1474 = v1451 + v1473; + let v1475 = v1451 - v1473; + let v1476 = f306 * 1479; + let v1477 = f50 + v1476; + let v1478 = f50 - v1476; + let v1479 = f434 * 1479; + let v1480 = f178 + v1479; + let v1481 = f178 - v1479; + let v1482 = v1480 * 4043; + let v1483 = v1477 + v1482; + let v1484 = v1477 - v1482; + let v1485 = v1481 * 5146; + let v1486 = v1478 + v1485; + let v1487 = v1478 - v1485; + let v1488 = f370 * 1479; + let v1489 = f114 + v1488; + let v1490 = f114 - v1488; + let v1491 = f498 * 1479; + let v1492 = f242 + v1491; + let v1493 = f242 - v1491; + let v1494 = v1492 * 4043; + let v1495 = v1489 + v1494; + let v1496 = v1489 - v1494; + let v1497 = v1493 * 5146; + let v1498 = v1490 + v1497; + let v1499 = v1490 - v1497; + let v1500 = v1495 * 5736; + let v1501 = v1483 + v1500; + let v1502 = v1483 - v1500; + let v1503 = v1496 * 4134; + let v1504 = v1484 + v1503; + let v1505 = v1484 - v1503; + let v1506 = v1498 * 722; + let v1507 = v1486 + v1506; + let v1508 = v1486 - v1506; + let v1509 = v1499 * 1305; + let v1510 = v1487 + v1509; + let v1511 = v1487 - v1509; + let v1512 = v1501 * 1646; + let v1513 = v1465 + v1512; + let v1514 = v1465 - v1512; + let v1515 = v1502 * 1212; + let v1516 = v1466 + v1515; + let v1517 = v1466 - v1515; + let v1518 = v1504 * 5860; + let v1519 = v1468 + v1518; + let v1520 = v1468 - v1518; + let v1521 = v1505 * 3195; + let v1522 = v1469 + v1521; + let v1523 = v1469 - v1521; + let v1524 = v1507 * 2545; + let v1525 = v1471 + v1524; + let v1526 = v1471 - v1524; + let v1527 = v1508 * 3621; + let v1528 = v1472 + v1527; + let v1529 = v1472 - v1527; + let v1530 = v1510 * 3504; + let v1531 = v1474 + v1530; + let v1532 = v1474 - v1530; + let v1533 = v1511 * 3542; + let v1534 = v1475 + v1533; + let v1535 = v1475 - v1533; + let v1536 = v1513 * 4591; + let v1537 = v1417 + v1536; + let v1538 = v1417 - v1536; + let v1539 = v1514 * 5728; + let v1540 = v1418 + v1539; + let v1541 = v1418 - v1539; + let v1542 = v1516 * 5023; + let v1543 = v1420 + v1542; + let v1544 = v1420 - v1542; + let v1545 = v1517 * 5828; + let v1546 = v1421 + v1545; + let v1547 = v1421 - v1545; + let v1548 = v1519 * 4978; + let v1549 = v1423 + v1548; + let v1550 = v1423 - v1548; + let v1551 = v1520 * 1351; + let v1552 = v1424 + v1551; + let v1553 = v1424 - v1551; + let v1554 = v1522 * 3328; + let v1555 = v1426 + v1554; + let v1556 = v1426 - v1554; + let v1557 = v1523 * 5777; + let v1558 = v1427 + v1557; + let v1559 = v1427 - v1557; + let v1560 = v1525 * 2975; + let v1561 = v1429 + v1560; + let v1562 = v1429 - v1560; + let v1563 = v1526 * 563; + let v1564 = v1430 + v1563; + let v1565 = v1430 - v1563; + let v1566 = v1528 * 3006; + let v1567 = v1432 + v1566; + let v1568 = v1432 - v1566; + let v1569 = v1529 * 2744; + let v1570 = v1433 + v1569; + let v1571 = v1433 - v1569; + let v1572 = v1531 * 949; + let v1573 = v1435 + v1572; + let v1574 = v1435 - v1572; + let v1575 = v1532 * 2625; + let v1576 = v1436 + v1575; + let v1577 = v1436 - v1575; + let v1578 = v1534 * 4821; + let v1579 = v1438 + v1578; + let v1580 = v1438 - v1578; + let v1581 = v1535 * 2639; + let v1582 = v1439 + v1581; + let v1583 = v1439 - v1581; + let v1584 = f266 * 1479; + let v1585 = f10 + v1584; + let v1586 = f10 - v1584; + let v1587 = f394 * 1479; + let v1588 = f138 + v1587; + let v1589 = f138 - v1587; + let v1590 = v1588 * 4043; + let v1591 = v1585 + v1590; + let v1592 = v1585 - v1590; + let v1593 = v1589 * 5146; + let v1594 = v1586 + v1593; + let v1595 = v1586 - v1593; + let v1596 = f330 * 1479; + let v1597 = f74 + v1596; + let v1598 = f74 - v1596; + let v1599 = f458 * 1479; + let v1600 = f202 + v1599; + let v1601 = f202 - v1599; + let v1602 = v1600 * 4043; + let v1603 = v1597 + v1602; + let v1604 = v1597 - v1602; + let v1605 = v1601 * 5146; + let v1606 = v1598 + v1605; + let v1607 = v1598 - v1605; + let v1608 = v1603 * 5736; + let v1609 = v1591 + v1608; + let v1610 = v1591 - v1608; + let v1611 = v1604 * 4134; + let v1612 = v1592 + v1611; + let v1613 = v1592 - v1611; + let v1614 = v1606 * 722; + let v1615 = v1594 + v1614; + let v1616 = v1594 - v1614; + let v1617 = v1607 * 1305; + let v1618 = v1595 + v1617; + let v1619 = v1595 - v1617; + let v1620 = f298 * 1479; + let v1621 = f42 + v1620; + let v1622 = f42 - v1620; + let v1623 = f426 * 1479; + let v1624 = f170 + v1623; + let v1625 = f170 - v1623; + let v1626 = v1624 * 4043; + let v1627 = v1621 + v1626; + let v1628 = v1621 - v1626; + let v1629 = v1625 * 5146; + let v1630 = v1622 + v1629; + let v1631 = v1622 - v1629; + let v1632 = f362 * 1479; + let v1633 = f106 + v1632; + let v1634 = f106 - v1632; + let v1635 = f490 * 1479; + let v1636 = f234 + v1635; + let v1637 = f234 - v1635; + let v1638 = v1636 * 4043; + let v1639 = v1633 + v1638; + let v1640 = v1633 - v1638; + let v1641 = v1637 * 5146; + let v1642 = v1634 + v1641; + let v1643 = v1634 - v1641; + let v1644 = v1639 * 5736; + let v1645 = v1627 + v1644; + let v1646 = v1627 - v1644; + let v1647 = v1640 * 4134; + let v1648 = v1628 + v1647; + let v1649 = v1628 - v1647; + let v1650 = v1642 * 722; + let v1651 = v1630 + v1650; + let v1652 = v1630 - v1650; + let v1653 = v1643 * 1305; + let v1654 = v1631 + v1653; + let v1655 = v1631 - v1653; + let v1656 = v1645 * 1646; + let v1657 = v1609 + v1656; + let v1658 = v1609 - v1656; + let v1659 = v1646 * 1212; + let v1660 = v1610 + v1659; + let v1661 = v1610 - v1659; + let v1662 = v1648 * 5860; + let v1663 = v1612 + v1662; + let v1664 = v1612 - v1662; + let v1665 = v1649 * 3195; + let v1666 = v1613 + v1665; + let v1667 = v1613 - v1665; + let v1668 = v1651 * 2545; + let v1669 = v1615 + v1668; + let v1670 = v1615 - v1668; + let v1671 = v1652 * 3621; + let v1672 = v1616 + v1671; + let v1673 = v1616 - v1671; + let v1674 = v1654 * 3504; + let v1675 = v1618 + v1674; + let v1676 = v1618 - v1674; + let v1677 = v1655 * 3542; + let v1678 = v1619 + v1677; + let v1679 = v1619 - v1677; + let v1680 = f282 * 1479; + let v1681 = f26 + v1680; + let v1682 = f26 - v1680; + let v1683 = f410 * 1479; + let v1684 = f154 + v1683; + let v1685 = f154 - v1683; + let v1686 = v1684 * 4043; + let v1687 = v1681 + v1686; + let v1688 = v1681 - v1686; + let v1689 = v1685 * 5146; + let v1690 = v1682 + v1689; + let v1691 = v1682 - v1689; + let v1692 = f346 * 1479; + let v1693 = f90 + v1692; + let v1694 = f90 - v1692; + let v1695 = f474 * 1479; + let v1696 = f218 + v1695; + let v1697 = f218 - v1695; + let v1698 = v1696 * 4043; + let v1699 = v1693 + v1698; + let v1700 = v1693 - v1698; + let v1701 = v1697 * 5146; + let v1702 = v1694 + v1701; + let v1703 = v1694 - v1701; + let v1704 = v1699 * 5736; + let v1705 = v1687 + v1704; + let v1706 = v1687 - v1704; + let v1707 = v1700 * 4134; + let v1708 = v1688 + v1707; + let v1709 = v1688 - v1707; + let v1710 = v1702 * 722; + let v1711 = v1690 + v1710; + let v1712 = v1690 - v1710; + let v1713 = v1703 * 1305; + let v1714 = v1691 + v1713; + let v1715 = v1691 - v1713; + let v1716 = f314 * 1479; + let v1717 = f58 + v1716; + let v1718 = f58 - v1716; + let v1719 = f442 * 1479; + let v1720 = f186 + v1719; + let v1721 = f186 - v1719; + let v1722 = v1720 * 4043; + let v1723 = v1717 + v1722; + let v1724 = v1717 - v1722; + let v1725 = v1721 * 5146; + let v1726 = v1718 + v1725; + let v1727 = v1718 - v1725; + let v1728 = f378 * 1479; + let v1729 = f122 + v1728; + let v1730 = f122 - v1728; + let v1731 = f506 * 1479; + let v1732 = f250 + v1731; + let v1733 = f250 - v1731; + let v1734 = v1732 * 4043; + let v1735 = v1729 + v1734; + let v1736 = v1729 - v1734; + let v1737 = v1733 * 5146; + let v1738 = v1730 + v1737; + let v1739 = v1730 - v1737; + let v1740 = v1735 * 5736; + let v1741 = v1723 + v1740; + let v1742 = v1723 - v1740; + let v1743 = v1736 * 4134; + let v1744 = v1724 + v1743; + let v1745 = v1724 - v1743; + let v1746 = v1738 * 722; + let v1747 = v1726 + v1746; + let v1748 = v1726 - v1746; + let v1749 = v1739 * 1305; + let v1750 = v1727 + v1749; + let v1751 = v1727 - v1749; + let v1752 = v1741 * 1646; + let v1753 = v1705 + v1752; + let v1754 = v1705 - v1752; + let v1755 = v1742 * 1212; + let v1756 = v1706 + v1755; + let v1757 = v1706 - v1755; + let v1758 = v1744 * 5860; + let v1759 = v1708 + v1758; + let v1760 = v1708 - v1758; + let v1761 = v1745 * 3195; + let v1762 = v1709 + v1761; + let v1763 = v1709 - v1761; + let v1764 = v1747 * 2545; + let v1765 = v1711 + v1764; + let v1766 = v1711 - v1764; + let v1767 = v1748 * 3621; + let v1768 = v1712 + v1767; + let v1769 = v1712 - v1767; + let v1770 = v1750 * 3504; + let v1771 = v1714 + v1770; + let v1772 = v1714 - v1770; + let v1773 = v1751 * 3542; + let v1774 = v1715 + v1773; + let v1775 = v1715 - v1773; + let v1776 = v1753 * 4591; + let v1777 = v1657 + v1776; + let v1778 = v1657 - v1776; + let v1779 = v1754 * 5728; + let v1780 = v1658 + v1779; + let v1781 = v1658 - v1779; + let v1782 = v1756 * 5023; + let v1783 = v1660 + v1782; + let v1784 = v1660 - v1782; + let v1785 = v1757 * 5828; + let v1786 = v1661 + v1785; + let v1787 = v1661 - v1785; + let v1788 = v1759 * 4978; + let v1789 = v1663 + v1788; + let v1790 = v1663 - v1788; + let v1791 = v1760 * 1351; + let v1792 = v1664 + v1791; + let v1793 = v1664 - v1791; + let v1794 = v1762 * 3328; + let v1795 = v1666 + v1794; + let v1796 = v1666 - v1794; + let v1797 = v1763 * 5777; + let v1798 = v1667 + v1797; + let v1799 = v1667 - v1797; + let v1800 = v1765 * 2975; + let v1801 = v1669 + v1800; + let v1802 = v1669 - v1800; + let v1803 = v1766 * 563; + let v1804 = v1670 + v1803; + let v1805 = v1670 - v1803; + let v1806 = v1768 * 3006; + let v1807 = v1672 + v1806; + let v1808 = v1672 - v1806; + let v1809 = v1769 * 2744; + let v1810 = v1673 + v1809; + let v1811 = v1673 - v1809; + let v1812 = v1771 * 949; + let v1813 = v1675 + v1812; + let v1814 = v1675 - v1812; + let v1815 = v1772 * 2625; + let v1816 = v1676 + v1815; + let v1817 = v1676 - v1815; + let v1818 = v1774 * 4821; + let v1819 = v1678 + v1818; + let v1820 = v1678 - v1818; + let v1821 = v1775 * 2639; + let v1822 = v1679 + v1821; + let v1823 = v1679 - v1821; + let v1824 = v1777 * 1000; + let v1825 = v1537 + v1824; + let v1826 = v1537 - v1824; + let v1827 = v1778 * 4320; + let v1828 = v1538 + v1827; + let v1829 = v1538 - v1827; + let v1830 = v1780 * 3091; + let v1831 = v1540 + v1830; + let v1832 = v1540 - v1830; + let v1833 = v1781 * 81; + let v1834 = v1541 + v1833; + let v1835 = v1541 - v1833; + let v1836 = v1783 * 2963; + let v1837 = v1543 + v1836; + let v1838 = v1543 - v1836; + let v1839 = v1784 * 4896; + let v1840 = v1544 + v1839; + let v1841 = v1544 - v1839; + let v1842 = v1786 * 3051; + let v1843 = v1546 + v1842; + let v1844 = v1546 - v1842; + let v1845 = v1787 * 2366; + let v1846 = v1547 + v1845; + let v1847 = v1547 - v1845; + let v1848 = v1789 * 1853; + let v1849 = v1549 + v1848; + let v1850 = v1549 - v1848; + let v1851 = v1790 * 140; + let v1852 = v1550 + v1851; + let v1853 = v1550 - v1851; + let v1854 = v1792 * 4611; + let v1855 = v1552 + v1854; + let v1856 = v1552 - v1854; + let v1857 = v1793 * 726; + let v1858 = v1553 + v1857; + let v1859 = v1553 - v1857; + let v1860 = v1795 * 4255; + let v1861 = v1555 + v1860; + let v1862 = v1555 - v1860; + let v1863 = v1796 * 1177; + let v1864 = v1556 + v1863; + let v1865 = v1556 - v1863; + let v1866 = v1798 * 2768; + let v1867 = v1558 + v1866; + let v1868 = v1558 - v1866; + let v1869 = v1799 * 1635; + let v1870 = v1559 + v1869; + let v1871 = v1559 - v1869; + let v1872 = v1801 * 3712; + let v1873 = v1561 + v1872; + let v1874 = v1561 - v1872; + let v1875 = v1802 * 3135; + let v1876 = v1562 + v1875; + let v1877 = v1562 - v1875; + let v1878 = v1804 * 2747; + let v1879 = v1564 + v1878; + let v1880 = v1564 - v1878; + let v1881 = v1805 * 4846; + let v1882 = v1565 + v1881; + let v1883 = v1565 - v1881; + let v1884 = v1807 * 3553; + let v1885 = v1567 + v1884; + let v1886 = v1567 - v1884; + let v1887 = v1808 * 4805; + let v1888 = v1568 + v1887; + let v1889 = v1568 - v1887; + let v1890 = v1810 * 2294; + let v1891 = v1570 + v1890; + let v1892 = v1570 - v1890; + let v1893 = v1811 * 1062; + let v1894 = v1571 + v1893; + let v1895 = v1571 - v1893; + let v1896 = v1813 * 1326; + let v1897 = v1573 + v1896; + let v1898 = v1573 - v1896; + let v1899 = v1814 * 5086; + let v1900 = v1574 + v1899; + let v1901 = v1574 - v1899; + let v1902 = v1816 * 3014; + let v1903 = v1576 + v1902; + let v1904 = v1576 - v1902; + let v1905 = v1817 * 3201; + let v1906 = v1577 + v1905; + let v1907 = v1577 - v1905; + let v1908 = v1819 * 1170; + let v1909 = v1579 + v1908; + let v1910 = v1579 - v1908; + let v1911 = v1820 * 2319; + let v1912 = v1580 + v1911; + let v1913 = v1580 - v1911; + let v1914 = v1822 * 955; + let v1915 = v1582 + v1914; + let v1916 = v1582 - v1914; + let v1917 = v1823 * 790; + let v1918 = v1583 + v1917; + let v1919 = v1583 - v1917; + let v1920 = f262 * 1479; + let v1921 = f6 + v1920; + let v1922 = f6 - v1920; + let v1923 = f390 * 1479; + let v1924 = f134 + v1923; + let v1925 = f134 - v1923; + let v1926 = v1924 * 4043; + let v1927 = v1921 + v1926; + let v1928 = v1921 - v1926; + let v1929 = v1925 * 5146; + let v1930 = v1922 + v1929; + let v1931 = v1922 - v1929; + let v1932 = f326 * 1479; + let v1933 = f70 + v1932; + let v1934 = f70 - v1932; + let v1935 = f454 * 1479; + let v1936 = f198 + v1935; + let v1937 = f198 - v1935; + let v1938 = v1936 * 4043; + let v1939 = v1933 + v1938; + let v1940 = v1933 - v1938; + let v1941 = v1937 * 5146; + let v1942 = v1934 + v1941; + let v1943 = v1934 - v1941; + let v1944 = v1939 * 5736; + let v1945 = v1927 + v1944; + let v1946 = v1927 - v1944; + let v1947 = v1940 * 4134; + let v1948 = v1928 + v1947; + let v1949 = v1928 - v1947; + let v1950 = v1942 * 722; + let v1951 = v1930 + v1950; + let v1952 = v1930 - v1950; + let v1953 = v1943 * 1305; + let v1954 = v1931 + v1953; + let v1955 = v1931 - v1953; + let v1956 = f294 * 1479; + let v1957 = f38 + v1956; + let v1958 = f38 - v1956; + let v1959 = f422 * 1479; + let v1960 = f166 + v1959; + let v1961 = f166 - v1959; + let v1962 = v1960 * 4043; + let v1963 = v1957 + v1962; + let v1964 = v1957 - v1962; + let v1965 = v1961 * 5146; + let v1966 = v1958 + v1965; + let v1967 = v1958 - v1965; + let v1968 = f358 * 1479; + let v1969 = f102 + v1968; + let v1970 = f102 - v1968; + let v1971 = f486 * 1479; + let v1972 = f230 + v1971; + let v1973 = f230 - v1971; + let v1974 = v1972 * 4043; + let v1975 = v1969 + v1974; + let v1976 = v1969 - v1974; + let v1977 = v1973 * 5146; + let v1978 = v1970 + v1977; + let v1979 = v1970 - v1977; + let v1980 = v1975 * 5736; + let v1981 = v1963 + v1980; + let v1982 = v1963 - v1980; + let v1983 = v1976 * 4134; + let v1984 = v1964 + v1983; + let v1985 = v1964 - v1983; + let v1986 = v1978 * 722; + let v1987 = v1966 + v1986; + let v1988 = v1966 - v1986; + let v1989 = v1979 * 1305; + let v1990 = v1967 + v1989; + let v1991 = v1967 - v1989; + let v1992 = v1981 * 1646; + let v1993 = v1945 + v1992; + let v1994 = v1945 - v1992; + let v1995 = v1982 * 1212; + let v1996 = v1946 + v1995; + let v1997 = v1946 - v1995; + let v1998 = v1984 * 5860; + let v1999 = v1948 + v1998; + let v2000 = v1948 - v1998; + let v2001 = v1985 * 3195; + let v2002 = v1949 + v2001; + let v2003 = v1949 - v2001; + let v2004 = v1987 * 2545; + let v2005 = v1951 + v2004; + let v2006 = v1951 - v2004; + let v2007 = v1988 * 3621; + let v2008 = v1952 + v2007; + let v2009 = v1952 - v2007; + let v2010 = v1990 * 3504; + let v2011 = v1954 + v2010; + let v2012 = v1954 - v2010; + let v2013 = v1991 * 3542; + let v2014 = v1955 + v2013; + let v2015 = v1955 - v2013; + let v2016 = f278 * 1479; + let v2017 = f22 + v2016; + let v2018 = f22 - v2016; + let v2019 = f406 * 1479; + let v2020 = f150 + v2019; + let v2021 = f150 - v2019; + let v2022 = v2020 * 4043; + let v2023 = v2017 + v2022; + let v2024 = v2017 - v2022; + let v2025 = v2021 * 5146; + let v2026 = v2018 + v2025; + let v2027 = v2018 - v2025; + let v2028 = f342 * 1479; + let v2029 = f86 + v2028; + let v2030 = f86 - v2028; + let v2031 = f470 * 1479; + let v2032 = f214 + v2031; + let v2033 = f214 - v2031; + let v2034 = v2032 * 4043; + let v2035 = v2029 + v2034; + let v2036 = v2029 - v2034; + let v2037 = v2033 * 5146; + let v2038 = v2030 + v2037; + let v2039 = v2030 - v2037; + let v2040 = v2035 * 5736; + let v2041 = v2023 + v2040; + let v2042 = v2023 - v2040; + let v2043 = v2036 * 4134; + let v2044 = v2024 + v2043; + let v2045 = v2024 - v2043; + let v2046 = v2038 * 722; + let v2047 = v2026 + v2046; + let v2048 = v2026 - v2046; + let v2049 = v2039 * 1305; + let v2050 = v2027 + v2049; + let v2051 = v2027 - v2049; + let v2052 = f310 * 1479; + let v2053 = f54 + v2052; + let v2054 = f54 - v2052; + let v2055 = f438 * 1479; + let v2056 = f182 + v2055; + let v2057 = f182 - v2055; + let v2058 = v2056 * 4043; + let v2059 = v2053 + v2058; + let v2060 = v2053 - v2058; + let v2061 = v2057 * 5146; + let v2062 = v2054 + v2061; + let v2063 = v2054 - v2061; + let v2064 = f374 * 1479; + let v2065 = f118 + v2064; + let v2066 = f118 - v2064; + let v2067 = f502 * 1479; + let v2068 = f246 + v2067; + let v2069 = f246 - v2067; + let v2070 = v2068 * 4043; + let v2071 = v2065 + v2070; + let v2072 = v2065 - v2070; + let v2073 = v2069 * 5146; + let v2074 = v2066 + v2073; + let v2075 = v2066 - v2073; + let v2076 = v2071 * 5736; + let v2077 = v2059 + v2076; + let v2078 = v2059 - v2076; + let v2079 = v2072 * 4134; + let v2080 = v2060 + v2079; + let v2081 = v2060 - v2079; + let v2082 = v2074 * 722; + let v2083 = v2062 + v2082; + let v2084 = v2062 - v2082; + let v2085 = v2075 * 1305; + let v2086 = v2063 + v2085; + let v2087 = v2063 - v2085; + let v2088 = v2077 * 1646; + let v2089 = v2041 + v2088; + let v2090 = v2041 - v2088; + let v2091 = v2078 * 1212; + let v2092 = v2042 + v2091; + let v2093 = v2042 - v2091; + let v2094 = v2080 * 5860; + let v2095 = v2044 + v2094; + let v2096 = v2044 - v2094; + let v2097 = v2081 * 3195; + let v2098 = v2045 + v2097; + let v2099 = v2045 - v2097; + let v2100 = v2083 * 2545; + let v2101 = v2047 + v2100; + let v2102 = v2047 - v2100; + let v2103 = v2084 * 3621; + let v2104 = v2048 + v2103; + let v2105 = v2048 - v2103; + let v2106 = v2086 * 3504; + let v2107 = v2050 + v2106; + let v2108 = v2050 - v2106; + let v2109 = v2087 * 3542; + let v2110 = v2051 + v2109; + let v2111 = v2051 - v2109; + let v2112 = v2089 * 4591; + let v2113 = v1993 + v2112; + let v2114 = v1993 - v2112; + let v2115 = v2090 * 5728; + let v2116 = v1994 + v2115; + let v2117 = v1994 - v2115; + let v2118 = v2092 * 5023; + let v2119 = v1996 + v2118; + let v2120 = v1996 - v2118; + let v2121 = v2093 * 5828; + let v2122 = v1997 + v2121; + let v2123 = v1997 - v2121; + let v2124 = v2095 * 4978; + let v2125 = v1999 + v2124; + let v2126 = v1999 - v2124; + let v2127 = v2096 * 1351; + let v2128 = v2000 + v2127; + let v2129 = v2000 - v2127; + let v2130 = v2098 * 3328; + let v2131 = v2002 + v2130; + let v2132 = v2002 - v2130; + let v2133 = v2099 * 5777; + let v2134 = v2003 + v2133; + let v2135 = v2003 - v2133; + let v2136 = v2101 * 2975; + let v2137 = v2005 + v2136; + let v2138 = v2005 - v2136; + let v2139 = v2102 * 563; + let v2140 = v2006 + v2139; + let v2141 = v2006 - v2139; + let v2142 = v2104 * 3006; + let v2143 = v2008 + v2142; + let v2144 = v2008 - v2142; + let v2145 = v2105 * 2744; + let v2146 = v2009 + v2145; + let v2147 = v2009 - v2145; + let v2148 = v2107 * 949; + let v2149 = v2011 + v2148; + let v2150 = v2011 - v2148; + let v2151 = v2108 * 2625; + let v2152 = v2012 + v2151; + let v2153 = v2012 - v2151; + let v2154 = v2110 * 4821; + let v2155 = v2014 + v2154; + let v2156 = v2014 - v2154; + let v2157 = v2111 * 2639; + let v2158 = v2015 + v2157; + let v2159 = v2015 - v2157; + let v2160 = f270 * 1479; + let v2161 = f14 + v2160; + let v2162 = f14 - v2160; + let v2163 = f398 * 1479; + let v2164 = f142 + v2163; + let v2165 = f142 - v2163; + let v2166 = v2164 * 4043; + let v2167 = v2161 + v2166; + let v2168 = v2161 - v2166; + let v2169 = v2165 * 5146; + let v2170 = v2162 + v2169; + let v2171 = v2162 - v2169; + let v2172 = f334 * 1479; + let v2173 = f78 + v2172; + let v2174 = f78 - v2172; + let v2175 = f462 * 1479; + let v2176 = f206 + v2175; + let v2177 = f206 - v2175; + let v2178 = v2176 * 4043; + let v2179 = v2173 + v2178; + let v2180 = v2173 - v2178; + let v2181 = v2177 * 5146; + let v2182 = v2174 + v2181; + let v2183 = v2174 - v2181; + let v2184 = v2179 * 5736; + let v2185 = v2167 + v2184; + let v2186 = v2167 - v2184; + let v2187 = v2180 * 4134; + let v2188 = v2168 + v2187; + let v2189 = v2168 - v2187; + let v2190 = v2182 * 722; + let v2191 = v2170 + v2190; + let v2192 = v2170 - v2190; + let v2193 = v2183 * 1305; + let v2194 = v2171 + v2193; + let v2195 = v2171 - v2193; + let v2196 = f302 * 1479; + let v2197 = f46 + v2196; + let v2198 = f46 - v2196; + let v2199 = f430 * 1479; + let v2200 = f174 + v2199; + let v2201 = f174 - v2199; + let v2202 = v2200 * 4043; + let v2203 = v2197 + v2202; + let v2204 = v2197 - v2202; + let v2205 = v2201 * 5146; + let v2206 = v2198 + v2205; + let v2207 = v2198 - v2205; + let v2208 = f366 * 1479; + let v2209 = f110 + v2208; + let v2210 = f110 - v2208; + let v2211 = f494 * 1479; + let v2212 = f238 + v2211; + let v2213 = f238 - v2211; + let v2214 = v2212 * 4043; + let v2215 = v2209 + v2214; + let v2216 = v2209 - v2214; + let v2217 = v2213 * 5146; + let v2218 = v2210 + v2217; + let v2219 = v2210 - v2217; + let v2220 = v2215 * 5736; + let v2221 = v2203 + v2220; + let v2222 = v2203 - v2220; + let v2223 = v2216 * 4134; + let v2224 = v2204 + v2223; + let v2225 = v2204 - v2223; + let v2226 = v2218 * 722; + let v2227 = v2206 + v2226; + let v2228 = v2206 - v2226; + let v2229 = v2219 * 1305; + let v2230 = v2207 + v2229; + let v2231 = v2207 - v2229; + let v2232 = v2221 * 1646; + let v2233 = v2185 + v2232; + let v2234 = v2185 - v2232; + let v2235 = v2222 * 1212; + let v2236 = v2186 + v2235; + let v2237 = v2186 - v2235; + let v2238 = v2224 * 5860; + let v2239 = v2188 + v2238; + let v2240 = v2188 - v2238; + let v2241 = v2225 * 3195; + let v2242 = v2189 + v2241; + let v2243 = v2189 - v2241; + let v2244 = v2227 * 2545; + let v2245 = v2191 + v2244; + let v2246 = v2191 - v2244; + let v2247 = v2228 * 3621; + let v2248 = v2192 + v2247; + let v2249 = v2192 - v2247; + let v2250 = v2230 * 3504; + let v2251 = v2194 + v2250; + let v2252 = v2194 - v2250; + let v2253 = v2231 * 3542; + let v2254 = v2195 + v2253; + let v2255 = v2195 - v2253; + let v2256 = f286 * 1479; + let v2257 = f30 + v2256; + let v2258 = f30 - v2256; + let v2259 = f414 * 1479; + let v2260 = f158 + v2259; + let v2261 = f158 - v2259; + let v2262 = v2260 * 4043; + let v2263 = v2257 + v2262; + let v2264 = v2257 - v2262; + let v2265 = v2261 * 5146; + let v2266 = v2258 + v2265; + let v2267 = v2258 - v2265; + let v2268 = f350 * 1479; + let v2269 = f94 + v2268; + let v2270 = f94 - v2268; + let v2271 = f478 * 1479; + let v2272 = f222 + v2271; + let v2273 = f222 - v2271; + let v2274 = v2272 * 4043; + let v2275 = v2269 + v2274; + let v2276 = v2269 - v2274; + let v2277 = v2273 * 5146; + let v2278 = v2270 + v2277; + let v2279 = v2270 - v2277; + let v2280 = v2275 * 5736; + let v2281 = v2263 + v2280; + let v2282 = v2263 - v2280; + let v2283 = v2276 * 4134; + let v2284 = v2264 + v2283; + let v2285 = v2264 - v2283; + let v2286 = v2278 * 722; + let v2287 = v2266 + v2286; + let v2288 = v2266 - v2286; + let v2289 = v2279 * 1305; + let v2290 = v2267 + v2289; + let v2291 = v2267 - v2289; + let v2292 = f318 * 1479; + let v2293 = f62 + v2292; + let v2294 = f62 - v2292; + let v2295 = f446 * 1479; + let v2296 = f190 + v2295; + let v2297 = f190 - v2295; + let v2298 = v2296 * 4043; + let v2299 = v2293 + v2298; + let v2300 = v2293 - v2298; + let v2301 = v2297 * 5146; + let v2302 = v2294 + v2301; + let v2303 = v2294 - v2301; + let v2304 = f382 * 1479; + let v2305 = f126 + v2304; + let v2306 = f126 - v2304; + let v2307 = f510 * 1479; + let v2308 = f254 + v2307; + let v2309 = f254 - v2307; + let v2310 = v2308 * 4043; + let v2311 = v2305 + v2310; + let v2312 = v2305 - v2310; + let v2313 = v2309 * 5146; + let v2314 = v2306 + v2313; + let v2315 = v2306 - v2313; + let v2316 = v2311 * 5736; + let v2317 = v2299 + v2316; + let v2318 = v2299 - v2316; + let v2319 = v2312 * 4134; + let v2320 = v2300 + v2319; + let v2321 = v2300 - v2319; + let v2322 = v2314 * 722; + let v2323 = v2302 + v2322; + let v2324 = v2302 - v2322; + let v2325 = v2315 * 1305; + let v2326 = v2303 + v2325; + let v2327 = v2303 - v2325; + let v2328 = v2317 * 1646; + let v2329 = v2281 + v2328; + let v2330 = v2281 - v2328; + let v2331 = v2318 * 1212; + let v2332 = v2282 + v2331; + let v2333 = v2282 - v2331; + let v2334 = v2320 * 5860; + let v2335 = v2284 + v2334; + let v2336 = v2284 - v2334; + let v2337 = v2321 * 3195; + let v2338 = v2285 + v2337; + let v2339 = v2285 - v2337; + let v2340 = v2323 * 2545; + let v2341 = v2287 + v2340; + let v2342 = v2287 - v2340; + let v2343 = v2324 * 3621; + let v2344 = v2288 + v2343; + let v2345 = v2288 - v2343; + let v2346 = v2326 * 3504; + let v2347 = v2290 + v2346; + let v2348 = v2290 - v2346; + let v2349 = v2327 * 3542; + let v2350 = v2291 + v2349; + let v2351 = v2291 - v2349; + let v2352 = v2329 * 4591; + let v2353 = v2233 + v2352; + let v2354 = v2233 - v2352; + let v2355 = v2330 * 5728; + let v2356 = v2234 + v2355; + let v2357 = v2234 - v2355; + let v2358 = v2332 * 5023; + let v2359 = v2236 + v2358; + let v2360 = v2236 - v2358; + let v2361 = v2333 * 5828; + let v2362 = v2237 + v2361; + let v2363 = v2237 - v2361; + let v2364 = v2335 * 4978; + let v2365 = v2239 + v2364; + let v2366 = v2239 - v2364; + let v2367 = v2336 * 1351; + let v2368 = v2240 + v2367; + let v2369 = v2240 - v2367; + let v2370 = v2338 * 3328; + let v2371 = v2242 + v2370; + let v2372 = v2242 - v2370; + let v2373 = v2339 * 5777; + let v2374 = v2243 + v2373; + let v2375 = v2243 - v2373; + let v2376 = v2341 * 2975; + let v2377 = v2245 + v2376; + let v2378 = v2245 - v2376; + let v2379 = v2342 * 563; + let v2380 = v2246 + v2379; + let v2381 = v2246 - v2379; + let v2382 = v2344 * 3006; + let v2383 = v2248 + v2382; + let v2384 = v2248 - v2382; + let v2385 = v2345 * 2744; + let v2386 = v2249 + v2385; + let v2387 = v2249 - v2385; + let v2388 = v2347 * 949; + let v2389 = v2251 + v2388; + let v2390 = v2251 - v2388; + let v2391 = v2348 * 2625; + let v2392 = v2252 + v2391; + let v2393 = v2252 - v2391; + let v2394 = v2350 * 4821; + let v2395 = v2254 + v2394; + let v2396 = v2254 - v2394; + let v2397 = v2351 * 2639; + let v2398 = v2255 + v2397; + let v2399 = v2255 - v2397; + let v2400 = v2353 * 1000; + let v2401 = v2113 + v2400; + let v2402 = v2113 - v2400; + let v2403 = v2354 * 4320; + let v2404 = v2114 + v2403; + let v2405 = v2114 - v2403; + let v2406 = v2356 * 3091; + let v2407 = v2116 + v2406; + let v2408 = v2116 - v2406; + let v2409 = v2357 * 81; + let v2410 = v2117 + v2409; + let v2411 = v2117 - v2409; + let v2412 = v2359 * 2963; + let v2413 = v2119 + v2412; + let v2414 = v2119 - v2412; + let v2415 = v2360 * 4896; + let v2416 = v2120 + v2415; + let v2417 = v2120 - v2415; + let v2418 = v2362 * 3051; + let v2419 = v2122 + v2418; + let v2420 = v2122 - v2418; + let v2421 = v2363 * 2366; + let v2422 = v2123 + v2421; + let v2423 = v2123 - v2421; + let v2424 = v2365 * 1853; + let v2425 = v2125 + v2424; + let v2426 = v2125 - v2424; + let v2427 = v2366 * 140; + let v2428 = v2126 + v2427; + let v2429 = v2126 - v2427; + let v2430 = v2368 * 4611; + let v2431 = v2128 + v2430; + let v2432 = v2128 - v2430; + let v2433 = v2369 * 726; + let v2434 = v2129 + v2433; + let v2435 = v2129 - v2433; + let v2436 = v2371 * 4255; + let v2437 = v2131 + v2436; + let v2438 = v2131 - v2436; + let v2439 = v2372 * 1177; + let v2440 = v2132 + v2439; + let v2441 = v2132 - v2439; + let v2442 = v2374 * 2768; + let v2443 = v2134 + v2442; + let v2444 = v2134 - v2442; + let v2445 = v2375 * 1635; + let v2446 = v2135 + v2445; + let v2447 = v2135 - v2445; + let v2448 = v2377 * 3712; + let v2449 = v2137 + v2448; + let v2450 = v2137 - v2448; + let v2451 = v2378 * 3135; + let v2452 = v2138 + v2451; + let v2453 = v2138 - v2451; + let v2454 = v2380 * 2747; + let v2455 = v2140 + v2454; + let v2456 = v2140 - v2454; + let v2457 = v2381 * 4846; + let v2458 = v2141 + v2457; + let v2459 = v2141 - v2457; + let v2460 = v2383 * 3553; + let v2461 = v2143 + v2460; + let v2462 = v2143 - v2460; + let v2463 = v2384 * 4805; + let v2464 = v2144 + v2463; + let v2465 = v2144 - v2463; + let v2466 = v2386 * 2294; + let v2467 = v2146 + v2466; + let v2468 = v2146 - v2466; + let v2469 = v2387 * 1062; + let v2470 = v2147 + v2469; + let v2471 = v2147 - v2469; + let v2472 = v2389 * 1326; + let v2473 = v2149 + v2472; + let v2474 = v2149 - v2472; + let v2475 = v2390 * 5086; + let v2476 = v2150 + v2475; + let v2477 = v2150 - v2475; + let v2478 = v2392 * 3014; + let v2479 = v2152 + v2478; + let v2480 = v2152 - v2478; + let v2481 = v2393 * 3201; + let v2482 = v2153 + v2481; + let v2483 = v2153 - v2481; + let v2484 = v2395 * 1170; + let v2485 = v2155 + v2484; + let v2486 = v2155 - v2484; + let v2487 = v2396 * 2319; + let v2488 = v2156 + v2487; + let v2489 = v2156 - v2487; + let v2490 = v2398 * 955; + let v2491 = v2158 + v2490; + let v2492 = v2158 - v2490; + let v2493 = v2399 * 790; + let v2494 = v2159 + v2493; + let v2495 = v2159 - v2493; + let v2496 = v2401 * 544; + let v2497 = v1825 + v2496; + let v2498 = v1825 - v2496; + let v2499 = v2402 * 5791; + let v2500 = v1826 + v2499; + let v2501 = v1826 - v2499; + let v2502 = v2404 * 339; + let v2503 = v1828 + v2502; + let v2504 = v1828 - v2502; + let v2505 = v2405 * 2468; + let v2506 = v1829 + v2505; + let v2507 = v1829 - v2505; + let v2508 = v2407 * 2842; + let v2509 = v1831 + v2508; + let v2510 = v1831 - v2508; + let v2511 = v2408 * 480; + let v2512 = v1832 + v2511; + let v2513 = v1832 - v2511; + let v2514 = v2410 * 9; + let v2515 = v1834 + v2514; + let v2516 = v1834 - v2514; + let v2517 = v2411 * 1022; + let v2518 = v1835 + v2517; + let v2519 = v1835 - v2517; + let v2520 = v2413 * 4278; + let v2521 = v1837 + v2520; + let v2522 = v1837 - v2520; + let v2523 = v2414 * 1673; + let v2524 = v1838 + v2523; + let v2525 = v1838 - v2523; + let v2526 = v2416 * 4989; + let v2527 = v1840 + v2526; + let v2528 = v1840 - v2526; + let v2529 = v2417 * 5331; + let v2530 = v1841 + v2529; + let v2531 = v1841 - v2529; + let v2532 = v2419 * 3584; + let v2533 = v1843 + v2532; + let v2534 = v1843 - v2532; + let v2535 = v2420 * 4177; + let v2536 = v1844 + v2535; + let v2537 = v1844 - v2535; + let v2538 = v2422 * 1381; + let v2539 = v1846 + v2538; + let v2540 = v1846 - v2538; + let v2541 = v2423 * 2525; + let v2542 = v1847 + v2541; + let v2543 = v1847 - v2541; + let v2544 = v2425 * 2396; + let v2545 = v1849 + v2544; + let v2546 = v1849 - v2544; + let v2547 = v2426 * 4452; + let v2548 = v1850 + v2547; + let v2549 = v1850 - v2547; + let v2550 = v2428 * 3296; + let v2551 = v1852 + v2550; + let v2552 = v1852 - v2550; + let v2553 = v2429 * 3949; + let v2554 = v1853 + v2553; + let v2555 = v1853 - v2553; + let v2556 = v2431 * 130; + let v2557 = v1855 + v2556; + let v2558 = v1855 - v2556; + let v2559 = v2432 * 4354; + let v2560 = v1856 + v2559; + let v2561 = v1856 - v2559; + let v2562 = v2434 * 5374; + let v2563 = v1858 + v2562; + let v2564 = v1858 - v2562; + let v2565 = v2435 * 2837; + let v2566 = v1859 + v2565; + let v2567 = v1859 - v2565; + let v2568 = v2437 * 5767; + let v2569 = v1861 + v2568; + let v2570 = v1861 - v2568; + let v2571 = v2438 * 827; + let v2572 = v1862 + v2571; + let v2573 = v1862 - v2571; + let v2574 = v2440 * 3748; + let v2575 = v1864 + v2574; + let v2576 = v1864 - v2574; + let v2577 = v2441 * 953; + let v2578 = v1865 + v2577; + let v2579 = v1865 - v2577; + let v2580 = v2443 * 5067; + let v2581 = v1867 + v2580; + let v2582 = v1867 - v2580; + let v2583 = v2444 * 2197; + let v2584 = v1868 + v2583; + let v2585 = v1868 - v2583; + let v2586 = v2446 * 118; + let v2587 = v1870 + v2586; + let v2588 = v1870 - v2586; + let v2589 = v2447 * 2476; + let v2590 = v1871 + v2589; + let v2591 = v1871 - v2589; + let v2592 = v2449 * 2548; + let v2593 = v1873 + v2592; + let v2594 = v1873 - v2592; + let v2595 = v2450 * 4231; + let v2596 = v1874 + v2595; + let v2597 = v1874 - v2595; + let v2598 = v2452 * 355; + let v2599 = v1876 + v2598; + let v2600 = v1876 - v2598; + let v2601 = v2453 * 3382; + let v2602 = v1877 + v2601; + let v2603 = v1877 - v2601; + let v2604 = v2455 * 3707; + let v2605 = v1879 + v2604; + let v2606 = v1879 - v2604; + let v2607 = v2456 * 1759; + let v2608 = v1880 + v2607; + let v2609 = v1880 - v2607; + let v2610 = v2458 * 3694; + let v2611 = v1882 + v2610; + let v2612 = v1882 - v2610; + let v2613 = v2459 * 5179; + let v2614 = v1883 + v2613; + let v2615 = v1883 - v2613; + let v2616 = v2461 * 5542; + let v2617 = v1885 + v2616; + let v2618 = v1885 - v2616; + let v2619 = v2462 * 145; + let v2620 = v1886 + v2619; + let v2621 = v1886 - v2619; + let v2622 = v2464 * 3637; + let v2623 = v1888 + v2622; + let v2624 = v1888 - v2622; + let v2625 = v2465 * 3459; + let v2626 = v1889 + v2625; + let v2627 = v1889 - v2625; + let v2628 = v2467 * 5911; + let v2629 = v1891 + v2628; + let v2630 = v1891 - v2628; + let v2631 = v2468 * 4890; + let v2632 = v1892 + v2631; + let v2633 = v1892 - v2631; + let v2634 = v2470 * 3932; + let v2635 = v1894 + v2634; + let v2636 = v1894 - v2634; + let v2637 = v2471 * 2731; + let v2638 = v1895 + v2637; + let v2639 = v1895 - v2637; + let v2640 = v2473 * 2089; + let v2641 = v1897 + v2640; + let v2642 = v1897 - v2640; + let v2643 = v2474 * 5092; + let v2644 = v1898 + v2643; + let v2645 = v1898 - v2643; + let v2646 = v2476 * 2881; + let v2647 = v1900 + v2646; + let v2648 = v1900 - v2646; + let v2649 = v2477 * 3284; + let v2650 = v1901 + v2649; + let v2651 = v1901 - v2649; + let v2652 = v2479 * 729; + let v2653 = v1903 + v2652; + let v2654 = v1903 - v2652; + let v2655 = v2480 * 3241; + let v2656 = v1904 + v2655; + let v2657 = v1904 - v2655; + let v2658 = v2482 * 3289; + let v2659 = v1906 + v2658; + let v2660 = v1906 - v2658; + let v2661 = v2483 * 2013; + let v2662 = v1907 + v2661; + let v2663 = v1907 - v2661; + let v2664 = v2485 * 5755; + let v2665 = v1909 + v2664; + let v2666 = v1909 - v2664; + let v2667 = v2486 * 4632; + let v2668 = v1910 + v2667; + let v2669 = v1910 - v2667; + let v2670 = v2488 * 1260; + let v2671 = v1912 + v2670; + let v2672 = v1912 - v2670; + let v2673 = v2489 * 4388; + let v2674 = v1913 + v2673; + let v2675 = v1913 - v2673; + let v2676 = v2491 * 334; + let v2677 = v1915 + v2676; + let v2678 = v1915 - v2676; + let v2679 = v2492 * 2426; + let v2680 = v1916 + v2679; + let v2681 = v1916 - v2679; + let v2682 = v2494 * 1696; + let v2683 = v1918 + v2682; + let v2684 = v1918 - v2682; + let v2685 = v2495 * 1428; + let v2686 = v1919 + v2685; + let v2687 = v1919 - v2685; + let v2688 = v2497 * 1663; + let v2689 = v1153 + v2688; + let v2690 = v1153 - v2688; + let v2691 = v2498 * 1777; + let v2692 = v1154 + v2691; + let v2693 = v1154 - v2691; + let v2694 = v2500 * 1426; + let v2695 = v1156 + v2694; + let v2696 = v1156 - v2694; + let v2697 = v2501 * 4654; + let v2698 = v1157 + v2697; + let v2699 = v1157 - v2697; + let v2700 = v2503 * 5291; + let v2701 = v1159 + v2700; + let v2702 = v1159 - v2700; + let v2703 = v2504 * 2704; + let v2704 = v1160 + v2703; + let v2705 = v1160 - v2703; + let v2706 = v2506 * 4938; + let v2707 = v1162 + v2706; + let v2708 = v1162 - v2706; + let v2709 = v2507 * 3636; + let v2710 = v1163 + v2709; + let v2711 = v1163 - v2709; + let v2712 = v2509 * 3915; + let v2713 = v1165 + v2712; + let v2714 = v1165 - v2712; + let v2715 = v2510 * 2166; + let v2716 = v1166 + v2715; + let v2717 = v1166 - v2715; + let v2718 = v2512 * 113; + let v2719 = v1168 + v2718; + let v2720 = v1168 - v2718; + let v2721 = v2513 * 4919; + let v2722 = v1169 + v2721; + let v2723 = v1169 - v2721; + let v2724 = v2515 * 3; + let v2725 = v1171 + v2724; + let v2726 = v1171 - v2724; + let v2727 = v2516 * 4437; + let v2728 = v1172 + v2727; + let v2729 = v1172 - v2727; + let v2730 = v2518 * 160; + let v2731 = v1174 + v2730; + let v2732 = v1174 - v2730; + let v2733 = v2519 * 3149; + let v2734 = v1175 + v2733; + let v2735 = v1175 - v2733; + let v2736 = v2521 * 4057; + let v2737 = v1177 + v2736; + let v2738 = v1177 - v2736; + let v2739 = v2522 * 3271; + let v2740 = v1178 + v2739; + let v2741 = v1178 - v2739; + let v2742 = v2524 * 1689; + let v2743 = v1180 + v2742; + let v2744 = v1180 - v2742; + let v2745 = v2525 * 3364; + let v2746 = v1181 + v2745; + let v2747 = v1181 - v2745; + let v2748 = v2527 * 4372; + let v2749 = v1183 + v2748; + let v2750 = v1183 - v2748; + let v2751 = v2528 * 2174; + let v2752 = v1184 + v2751; + let v2753 = v1184 - v2751; + let v2754 = v2530 * 4414; + let v2755 = v1186 + v2754; + let v2756 = v1186 - v2754; + let v2757 = v2531 * 2847; + let v2758 = v1187 + v2757; + let v2759 = v1187 - v2757; + let v2760 = v2533 * 2645; + let v2761 = v1189 + v2760; + let v2762 = v1189 - v2760; + let v2763 = v2534 * 4053; + let v2764 = v1190 + v2763; + let v2765 = v1190 - v2763; + let v2766 = v2536 * 2305; + let v2767 = v1192 + v2766; + let v2768 = v1192 - v2766; + let v2769 = v2537 * 5042; + let v2770 = v1193 + v2769; + let v2771 = v1193 - v2769; + let v2772 = v2539 * 5195; + let v2773 = v1195 + v2772; + let v2774 = v1195 - v2772; + let v2775 = v2540 * 2780; + let v2776 = v1196 + v2775; + let v2777 = v1196 - v2775; + let v2778 = v2542 * 1484; + let v2779 = v1198 + v2778; + let v2780 = v1198 - v2778; + let v2781 = v2543 * 4895; + let v2782 = v1199 + v2781; + let v2783 = v1199 - v2781; + let v2784 = v2545 * 3016; + let v2785 = v1201 + v2784; + let v2786 = v1201 - v2784; + let v2787 = v2546 * 243; + let v2788 = v1202 + v2787; + let v2789 = v1202 - v2787; + let v2790 = v2548 * 3000; + let v2791 = v1204 + v2790; + let v2792 = v1204 - v2790; + let v2793 = v2549 * 671; + let v2794 = v1205 + v2793; + let v2795 = v1205 - v2793; + let v2796 = v2551 * 3136; + let v2797 = v1207 + v2796; + let v2798 = v1207 - v2796; + let v2799 = v2552 * 5191; + let v2800 = v1208 + v2799; + let v2801 = v1208 - v2799; + let v2802 = v2554 * 2399; + let v2803 = v1210 + v2802; + let v2804 = v1210 - v2802; + let v2805 = v2555 * 3400; + let v2806 = v1211 + v2805; + let v2807 = v1211 - v2805; + let v2808 = v2557 * 2178; + let v2809 = v1213 + v2808; + let v2810 = v1213 - v2808; + let v2811 = v2558 * 1544; + let v2812 = v1214 + v2811; + let v2813 = v1214 - v2811; + let v2814 = v2560 * 420; + let v2815 = v1216 + v2814; + let v2816 = v1216 - v2814; + let v2817 = v2561 * 5559; + let v2818 = v1217 + v2817; + let v2819 = v1217 - v2817; + let v2820 = v2563 * 476; + let v2821 = v1219 + v2820; + let v2822 = v1219 - v2820; + let v2823 = v2564 * 3531; + let v2824 = v1220 + v2823; + let v2825 = v1220 - v2823; + let v2826 = v2566 * 3985; + let v2827 = v1222 + v2826; + let v2828 = v1222 - v2826; + let v2829 = v2567 * 4905; + let v2830 = v1223 + v2829; + let v2831 = v1223 - v2829; + let v2832 = v2569 * 5332; + let v2833 = v1225 + v2832; + let v2834 = v1225 - v2832; + let v2835 = v2570 * 3510; + let v2836 = v1226 + v2835; + let v2837 = v1226 - v2835; + let v2838 = v2572 * 2370; + let v2839 = v1228 + v2838; + let v2840 = v1228 - v2838; + let v2841 = v2573 * 2865; + let v2842 = v1229 + v2841; + let v2843 = v1229 - v2841; + let v2844 = v2575 * 2969; + let v2845 = v1231 + v2844; + let v2846 = v1231 - v2844; + let v2847 = v2576 * 3978; + let v2848 = v1232 + v2847; + let v2849 = v1232 - v2847; + let v2850 = v2578 * 2686; + let v2851 = v1234 + v2850; + let v2852 = v1234 - v2850; + let v2853 = v2579 * 3247; + let v2854 = v1235 + v2853; + let v2855 = v1235 - v2853; + let v2856 = v2581 * 4048; + let v2857 = v1237 + v2856; + let v2858 = v1237 - v2856; + let v2859 = v2582 * 2249; + let v2860 = v1238 + v2859; + let v2861 = v1238 - v2859; + let v2862 = v2584 * 1153; + let v2863 = v1240 + v2862; + let v2864 = v1240 - v2862; + let v2865 = v2585 * 2884; + let v2866 = v1241 + v2865; + let v2867 = v1241 - v2865; + let v2868 = v2587 * 5407; + let v2869 = v1243 + v2868; + let v2870 = v1243 - v2868; + let v2871 = v2588 * 3186; + let v2872 = v1244 + v2871; + let v2873 = v1244 - v2871; + let v2874 = v2590 * 1630; + let v2875 = v1246 + v2874; + let v2876 = v1246 - v2874; + let v2877 = v2591 * 2126; + let v2878 = v1247 + v2877; + let v2879 = v1247 - v2877; + let v2880 = v2593 * 2187; + let v2881 = v1249 + v2880; + let v2882 = v1249 - v2880; + let v2883 = v2594 * 2566; + let v2884 = v1250 + v2883; + let v2885 = v1250 - v2883; + let v2886 = v2596 * 2422; + let v2887 = v1252 + v2886; + let v2888 = v1252 - v2886; + let v2889 = v2597 * 6039; + let v2890 = v1253 + v2889; + let v2891 = v1253 - v2889; + let v2892 = v2599 * 2987; + let v2893 = v1255 + v2892; + let v2894 = v1255 - v2892; + let v2895 = v2600 * 6022; + let v2896 = v1256 + v2895; + let v2897 = v1256 - v2895; + let v2898 = v2602 * 2437; + let v2899 = v1258 + v2898; + let v2900 = v1258 - v2898; + let v2901 = v2603 * 3646; + let v2902 = v1259 + v2901; + let v2903 = v1259 - v2901; + let v2904 = v2605 * 875; + let v2905 = v1261 + v2904; + let v2906 = v1261 - v2904; + let v2907 = v2606 * 3780; + let v2908 = v1262 + v2907; + let v2909 = v1262 - v2907; + let v2910 = v2608 * 1607; + let v2911 = v1264 + v2910; + let v2912 = v1264 - v2910; + let v2913 = v2609 * 4976; + let v2914 = v1265 + v2913; + let v2915 = v1265 - v2913; + let v2916 = v2611 * 5011; + let v2917 = v1267 + v2916; + let v2918 = v1267 - v2916; + let v2919 = v2612 * 1002; + let v2920 = v1268 + v2919; + let v2921 = v1268 - v2919; + let v2922 = v2614 * 4284; + let v2923 = v1270 + v2922; + let v2924 = v1270 - v2922; + let v2925 = v2615 * 5088; + let v2926 = v1271 + v2925; + let v2927 = v1271 - v2925; + let v2928 = v2617 * 3248; + let v2929 = v1273 + v2928; + let v2930 = v1273 - v2928; + let v2931 = v2618 * 1207; + let v2932 = v1274 + v2931; + let v2933 = v1274 - v2931; + let v2934 = v2620 * 1168; + let v2935 = v1276 + v2934; + let v2936 = v1276 - v2934; + let v2937 = v2621 * 5277; + let v2938 = v1277 + v2937; + let v2939 = v1277 - v2937; + let v2940 = v2623 * 1065; + let v2941 = v1279 + v2940; + let v2942 = v1279 - v2940; + let v2943 = v2624 * 2143; + let v2944 = v1280 + v2943; + let v2945 = v1280 - v2943; + let v2946 = v2626 * 404; + let v2947 = v1282 + v2946; + let v2948 = v1282 - v2946; + let v2949 = v2627 * 4645; + let v2950 = v1283 + v2949; + let v2951 = v1283 - v2949; + let v2952 = v2629 * 1912; + let v2953 = v1285 + v2952; + let v2954 = v1285 - v2952; + let v2955 = v2630 * 1378; + let v2956 = v1286 + v2955; + let v2957 = v1286 - v2955; + let v2958 = v2632 * 435; + let v2959 = v1288 + v2958; + let v2960 = v1288 - v2958; + let v2961 = v2633 * 4337; + let v2962 = v1289 + v2961; + let v2963 = v1289 - v2961; + let v2964 = v2635 * 2381; + let v2965 = v1291 + v2964; + let v2966 = v1291 - v2964; + let v2967 = v2636 * 5444; + let v2968 = v1292 + v2967; + let v2969 = v1292 - v2967; + let v2970 = v2638 * 4096; + let v2971 = v1294 + v2970; + let v2972 = v1294 - v2970; + let v2973 = v2639 * 493; + let v2974 = v1295 + v2973; + let v2975 = v1295 - v2973; + let v2976 = v2641 * 545; + let v2977 = v1297 + v2976; + let v2978 = v1297 - v2976; + let v2979 = v2642 * 5019; + let v2980 = v1298 + v2979; + let v2981 = v1298 - v2979; + let v2982 = v2644 * 3704; + let v2983 = v1300 + v2982; + let v2984 = v1300 - v2982; + let v2985 = v2645 * 2678; + let v2986 = v1301 + v2985; + let v2987 = v1301 - v2985; + let v2988 = v2647 * 1537; + let v2989 = v1303 + v2988; + let v2990 = v1303 - v2988; + let v2991 = v2648 * 242; + let v2992 = v1304 + v2991; + let v2993 = v1304 - v2991; + let v2994 = v2650 * 4714; + let v2995 = v1306 + v2994; + let v2996 = v1306 - v2994; + let v2997 = v2651 * 4143; + let v2998 = v1307 + v2997; + let v2999 = v1307 - v2997; + let v3000 = v2653 * 27; + let v3001 = v1309 + v3000; + let v3002 = v1309 - v3000; + let v3003 = v2654 * 3066; + let v3004 = v1310 + v3003; + let v3005 = v1310 - v3003; + let v3006 = v2656 * 3763; + let v3007 = v1312 + v3006; + let v3008 = v1312 - v3006; + let v3009 = v2657 * 1440; + let v3010 = v1313 + v3009; + let v3011 = v1313 - v3009; + let v3012 = v2659 * 5084; + let v3013 = v1315 + v3012; + let v3014 = v1315 - v3012; + let v3015 = v2660 * 1632; + let v3016 = v1316 + v3015; + let v3017 = v1316 - v3015; + let v3018 = v2662 * 1017; + let v3019 = v1318 + v3018; + let v3020 = v1318 - v3018; + let v3021 = v2663 * 4885; + let v3022 = v1319 + v3021; + let v3023 = v1319 - v3021; + let v3024 = v2665 * 3778; + let v3025 = v1321 + v3024; + let v3026 = v1321 - v3024; + let v3027 = v2666 * 3833; + let v3028 = v1322 + v3027; + let v3029 = v1322 - v3027; + let v3030 = v2668 * 390; + let v3031 = v1324 + v3030; + let v3032 = v1324 - v3030; + let v3033 = v2669 * 773; + let v3034 = v1325 + v3033; + let v3035 = v1325 - v3033; + let v3036 = v2671 * 2401; + let v3037 = v1327 + v3036; + let v3038 = v1327 - v3036; + let v3039 = v2672 * 442; + let v3040 = v1328 + v3039; + let v3041 = v1328 - v3039; + let v3042 = v2674 * 5101; + let v3043 = v1330 + v3042; + let v3044 = v1330 - v3042; + let v3045 = v2675 * 1067; + let v3046 = v1331 + v3045; + let v3047 = v1331 - v3045; + let v3048 = v2677 * 2912; + let v3049 = v1333 + v3048; + let v3050 = v1333 - v3048; + let v3051 = v2678 * 5698; + let v3052 = v1334 + v3051; + let v3053 = v1334 - v3051; + let v3054 = v2680 * 354; + let v3055 = v1336 + v3054; + let v3056 = v1336 - v3054; + let v3057 = v2681 * 4861; + let v3058 = v1337 + v3057; + let v3059 = v1337 - v3057; + let v3060 = v2683 * 2859; + let v3061 = v1339 + v3060; + let v3062 = v1339 - v3060; + let v3063 = v2684 * 1045; + let v3064 = v1340 + v3063; + let v3065 = v1340 - v3063; + let v3066 = v2686 * 5012; + let v3067 = v1342 + v3066; + let v3068 = v1342 - v3066; + let v3069 = v2687 * 2481; + let v3070 = v1343 + v3069; + let v3071 = v1343 - v3069; + let v3072 = f257 * 1479; + let v3073 = f1 + v3072; + let v3074 = f1 - v3072; + let v3075 = f385 * 1479; + let v3076 = f129 + v3075; + let v3077 = f129 - v3075; + let v3078 = v3076 * 4043; + let v3079 = v3073 + v3078; + let v3080 = v3073 - v3078; + let v3081 = v3077 * 5146; + let v3082 = v3074 + v3081; + let v3083 = v3074 - v3081; + let v3084 = f321 * 1479; + let v3085 = f65 + v3084; + let v3086 = f65 - v3084; + let v3087 = f449 * 1479; + let v3088 = f193 + v3087; + let v3089 = f193 - v3087; + let v3090 = v3088 * 4043; + let v3091 = v3085 + v3090; + let v3092 = v3085 - v3090; + let v3093 = v3089 * 5146; + let v3094 = v3086 + v3093; + let v3095 = v3086 - v3093; + let v3096 = v3091 * 5736; + let v3097 = v3079 + v3096; + let v3098 = v3079 - v3096; + let v3099 = v3092 * 4134; + let v3100 = v3080 + v3099; + let v3101 = v3080 - v3099; + let v3102 = v3094 * 722; + let v3103 = v3082 + v3102; + let v3104 = v3082 - v3102; + let v3105 = v3095 * 1305; + let v3106 = v3083 + v3105; + let v3107 = v3083 - v3105; + let v3108 = f289 * 1479; + let v3109 = f33 + v3108; + let v3110 = f33 - v3108; + let v3111 = f417 * 1479; + let v3112 = f161 + v3111; + let v3113 = f161 - v3111; + let v3114 = v3112 * 4043; + let v3115 = v3109 + v3114; + let v3116 = v3109 - v3114; + let v3117 = v3113 * 5146; + let v3118 = v3110 + v3117; + let v3119 = v3110 - v3117; + let v3120 = f353 * 1479; + let v3121 = f97 + v3120; + let v3122 = f97 - v3120; + let v3123 = f481 * 1479; + let v3124 = f225 + v3123; + let v3125 = f225 - v3123; + let v3126 = v3124 * 4043; + let v3127 = v3121 + v3126; + let v3128 = v3121 - v3126; + let v3129 = v3125 * 5146; + let v3130 = v3122 + v3129; + let v3131 = v3122 - v3129; + let v3132 = v3127 * 5736; + let v3133 = v3115 + v3132; + let v3134 = v3115 - v3132; + let v3135 = v3128 * 4134; + let v3136 = v3116 + v3135; + let v3137 = v3116 - v3135; + let v3138 = v3130 * 722; + let v3139 = v3118 + v3138; + let v3140 = v3118 - v3138; + let v3141 = v3131 * 1305; + let v3142 = v3119 + v3141; + let v3143 = v3119 - v3141; + let v3144 = v3133 * 1646; + let v3145 = v3097 + v3144; + let v3146 = v3097 - v3144; + let v3147 = v3134 * 1212; + let v3148 = v3098 + v3147; + let v3149 = v3098 - v3147; + let v3150 = v3136 * 5860; + let v3151 = v3100 + v3150; + let v3152 = v3100 - v3150; + let v3153 = v3137 * 3195; + let v3154 = v3101 + v3153; + let v3155 = v3101 - v3153; + let v3156 = v3139 * 2545; + let v3157 = v3103 + v3156; + let v3158 = v3103 - v3156; + let v3159 = v3140 * 3621; + let v3160 = v3104 + v3159; + let v3161 = v3104 - v3159; + let v3162 = v3142 * 3504; + let v3163 = v3106 + v3162; + let v3164 = v3106 - v3162; + let v3165 = v3143 * 3542; + let v3166 = v3107 + v3165; + let v3167 = v3107 - v3165; + let v3168 = f273 * 1479; + let v3169 = f17 + v3168; + let v3170 = f17 - v3168; + let v3171 = f401 * 1479; + let v3172 = f145 + v3171; + let v3173 = f145 - v3171; + let v3174 = v3172 * 4043; + let v3175 = v3169 + v3174; + let v3176 = v3169 - v3174; + let v3177 = v3173 * 5146; + let v3178 = v3170 + v3177; + let v3179 = v3170 - v3177; + let v3180 = f337 * 1479; + let v3181 = f81 + v3180; + let v3182 = f81 - v3180; + let v3183 = f465 * 1479; + let v3184 = f209 + v3183; + let v3185 = f209 - v3183; + let v3186 = v3184 * 4043; + let v3187 = v3181 + v3186; + let v3188 = v3181 - v3186; + let v3189 = v3185 * 5146; + let v3190 = v3182 + v3189; + let v3191 = v3182 - v3189; + let v3192 = v3187 * 5736; + let v3193 = v3175 + v3192; + let v3194 = v3175 - v3192; + let v3195 = v3188 * 4134; + let v3196 = v3176 + v3195; + let v3197 = v3176 - v3195; + let v3198 = v3190 * 722; + let v3199 = v3178 + v3198; + let v3200 = v3178 - v3198; + let v3201 = v3191 * 1305; + let v3202 = v3179 + v3201; + let v3203 = v3179 - v3201; + let v3204 = f305 * 1479; + let v3205 = f49 + v3204; + let v3206 = f49 - v3204; + let v3207 = f433 * 1479; + let v3208 = f177 + v3207; + let v3209 = f177 - v3207; + let v3210 = v3208 * 4043; + let v3211 = v3205 + v3210; + let v3212 = v3205 - v3210; + let v3213 = v3209 * 5146; + let v3214 = v3206 + v3213; + let v3215 = v3206 - v3213; + let v3216 = f369 * 1479; + let v3217 = f113 + v3216; + let v3218 = f113 - v3216; + let v3219 = f497 * 1479; + let v3220 = f241 + v3219; + let v3221 = f241 - v3219; + let v3222 = v3220 * 4043; + let v3223 = v3217 + v3222; + let v3224 = v3217 - v3222; + let v3225 = v3221 * 5146; + let v3226 = v3218 + v3225; + let v3227 = v3218 - v3225; + let v3228 = v3223 * 5736; + let v3229 = v3211 + v3228; + let v3230 = v3211 - v3228; + let v3231 = v3224 * 4134; + let v3232 = v3212 + v3231; + let v3233 = v3212 - v3231; + let v3234 = v3226 * 722; + let v3235 = v3214 + v3234; + let v3236 = v3214 - v3234; + let v3237 = v3227 * 1305; + let v3238 = v3215 + v3237; + let v3239 = v3215 - v3237; + let v3240 = v3229 * 1646; + let v3241 = v3193 + v3240; + let v3242 = v3193 - v3240; + let v3243 = v3230 * 1212; + let v3244 = v3194 + v3243; + let v3245 = v3194 - v3243; + let v3246 = v3232 * 5860; + let v3247 = v3196 + v3246; + let v3248 = v3196 - v3246; + let v3249 = v3233 * 3195; + let v3250 = v3197 + v3249; + let v3251 = v3197 - v3249; + let v3252 = v3235 * 2545; + let v3253 = v3199 + v3252; + let v3254 = v3199 - v3252; + let v3255 = v3236 * 3621; + let v3256 = v3200 + v3255; + let v3257 = v3200 - v3255; + let v3258 = v3238 * 3504; + let v3259 = v3202 + v3258; + let v3260 = v3202 - v3258; + let v3261 = v3239 * 3542; + let v3262 = v3203 + v3261; + let v3263 = v3203 - v3261; + let v3264 = v3241 * 4591; + let v3265 = v3145 + v3264; + let v3266 = v3145 - v3264; + let v3267 = v3242 * 5728; + let v3268 = v3146 + v3267; + let v3269 = v3146 - v3267; + let v3270 = v3244 * 5023; + let v3271 = v3148 + v3270; + let v3272 = v3148 - v3270; + let v3273 = v3245 * 5828; + let v3274 = v3149 + v3273; + let v3275 = v3149 - v3273; + let v3276 = v3247 * 4978; + let v3277 = v3151 + v3276; + let v3278 = v3151 - v3276; + let v3279 = v3248 * 1351; + let v3280 = v3152 + v3279; + let v3281 = v3152 - v3279; + let v3282 = v3250 * 3328; + let v3283 = v3154 + v3282; + let v3284 = v3154 - v3282; + let v3285 = v3251 * 5777; + let v3286 = v3155 + v3285; + let v3287 = v3155 - v3285; + let v3288 = v3253 * 2975; + let v3289 = v3157 + v3288; + let v3290 = v3157 - v3288; + let v3291 = v3254 * 563; + let v3292 = v3158 + v3291; + let v3293 = v3158 - v3291; + let v3294 = v3256 * 3006; + let v3295 = v3160 + v3294; + let v3296 = v3160 - v3294; + let v3297 = v3257 * 2744; + let v3298 = v3161 + v3297; + let v3299 = v3161 - v3297; + let v3300 = v3259 * 949; + let v3301 = v3163 + v3300; + let v3302 = v3163 - v3300; + let v3303 = v3260 * 2625; + let v3304 = v3164 + v3303; + let v3305 = v3164 - v3303; + let v3306 = v3262 * 4821; + let v3307 = v3166 + v3306; + let v3308 = v3166 - v3306; + let v3309 = v3263 * 2639; + let v3310 = v3167 + v3309; + let v3311 = v3167 - v3309; + let v3312 = f265 * 1479; + let v3313 = f9 + v3312; + let v3314 = f9 - v3312; + let v3315 = f393 * 1479; + let v3316 = f137 + v3315; + let v3317 = f137 - v3315; + let v3318 = v3316 * 4043; + let v3319 = v3313 + v3318; + let v3320 = v3313 - v3318; + let v3321 = v3317 * 5146; + let v3322 = v3314 + v3321; + let v3323 = v3314 - v3321; + let v3324 = f329 * 1479; + let v3325 = f73 + v3324; + let v3326 = f73 - v3324; + let v3327 = f457 * 1479; + let v3328 = f201 + v3327; + let v3329 = f201 - v3327; + let v3330 = v3328 * 4043; + let v3331 = v3325 + v3330; + let v3332 = v3325 - v3330; + let v3333 = v3329 * 5146; + let v3334 = v3326 + v3333; + let v3335 = v3326 - v3333; + let v3336 = v3331 * 5736; + let v3337 = v3319 + v3336; + let v3338 = v3319 - v3336; + let v3339 = v3332 * 4134; + let v3340 = v3320 + v3339; + let v3341 = v3320 - v3339; + let v3342 = v3334 * 722; + let v3343 = v3322 + v3342; + let v3344 = v3322 - v3342; + let v3345 = v3335 * 1305; + let v3346 = v3323 + v3345; + let v3347 = v3323 - v3345; + let v3348 = f297 * 1479; + let v3349 = f41 + v3348; + let v3350 = f41 - v3348; + let v3351 = f425 * 1479; + let v3352 = f169 + v3351; + let v3353 = f169 - v3351; + let v3354 = v3352 * 4043; + let v3355 = v3349 + v3354; + let v3356 = v3349 - v3354; + let v3357 = v3353 * 5146; + let v3358 = v3350 + v3357; + let v3359 = v3350 - v3357; + let v3360 = f361 * 1479; + let v3361 = f105 + v3360; + let v3362 = f105 - v3360; + let v3363 = f489 * 1479; + let v3364 = f233 + v3363; + let v3365 = f233 - v3363; + let v3366 = v3364 * 4043; + let v3367 = v3361 + v3366; + let v3368 = v3361 - v3366; + let v3369 = v3365 * 5146; + let v3370 = v3362 + v3369; + let v3371 = v3362 - v3369; + let v3372 = v3367 * 5736; + let v3373 = v3355 + v3372; + let v3374 = v3355 - v3372; + let v3375 = v3368 * 4134; + let v3376 = v3356 + v3375; + let v3377 = v3356 - v3375; + let v3378 = v3370 * 722; + let v3379 = v3358 + v3378; + let v3380 = v3358 - v3378; + let v3381 = v3371 * 1305; + let v3382 = v3359 + v3381; + let v3383 = v3359 - v3381; + let v3384 = v3373 * 1646; + let v3385 = v3337 + v3384; + let v3386 = v3337 - v3384; + let v3387 = v3374 * 1212; + let v3388 = v3338 + v3387; + let v3389 = v3338 - v3387; + let v3390 = v3376 * 5860; + let v3391 = v3340 + v3390; + let v3392 = v3340 - v3390; + let v3393 = v3377 * 3195; + let v3394 = v3341 + v3393; + let v3395 = v3341 - v3393; + let v3396 = v3379 * 2545; + let v3397 = v3343 + v3396; + let v3398 = v3343 - v3396; + let v3399 = v3380 * 3621; + let v3400 = v3344 + v3399; + let v3401 = v3344 - v3399; + let v3402 = v3382 * 3504; + let v3403 = v3346 + v3402; + let v3404 = v3346 - v3402; + let v3405 = v3383 * 3542; + let v3406 = v3347 + v3405; + let v3407 = v3347 - v3405; + let v3408 = f281 * 1479; + let v3409 = f25 + v3408; + let v3410 = f25 - v3408; + let v3411 = f409 * 1479; + let v3412 = f153 + v3411; + let v3413 = f153 - v3411; + let v3414 = v3412 * 4043; + let v3415 = v3409 + v3414; + let v3416 = v3409 - v3414; + let v3417 = v3413 * 5146; + let v3418 = v3410 + v3417; + let v3419 = v3410 - v3417; + let v3420 = f345 * 1479; + let v3421 = f89 + v3420; + let v3422 = f89 - v3420; + let v3423 = f473 * 1479; + let v3424 = f217 + v3423; + let v3425 = f217 - v3423; + let v3426 = v3424 * 4043; + let v3427 = v3421 + v3426; + let v3428 = v3421 - v3426; + let v3429 = v3425 * 5146; + let v3430 = v3422 + v3429; + let v3431 = v3422 - v3429; + let v3432 = v3427 * 5736; + let v3433 = v3415 + v3432; + let v3434 = v3415 - v3432; + let v3435 = v3428 * 4134; + let v3436 = v3416 + v3435; + let v3437 = v3416 - v3435; + let v3438 = v3430 * 722; + let v3439 = v3418 + v3438; + let v3440 = v3418 - v3438; + let v3441 = v3431 * 1305; + let v3442 = v3419 + v3441; + let v3443 = v3419 - v3441; + let v3444 = f313 * 1479; + let v3445 = f57 + v3444; + let v3446 = f57 - v3444; + let v3447 = f441 * 1479; + let v3448 = f185 + v3447; + let v3449 = f185 - v3447; + let v3450 = v3448 * 4043; + let v3451 = v3445 + v3450; + let v3452 = v3445 - v3450; + let v3453 = v3449 * 5146; + let v3454 = v3446 + v3453; + let v3455 = v3446 - v3453; + let v3456 = f377 * 1479; + let v3457 = f121 + v3456; + let v3458 = f121 - v3456; + let v3459 = f505 * 1479; + let v3460 = f249 + v3459; + let v3461 = f249 - v3459; + let v3462 = v3460 * 4043; + let v3463 = v3457 + v3462; + let v3464 = v3457 - v3462; + let v3465 = v3461 * 5146; + let v3466 = v3458 + v3465; + let v3467 = v3458 - v3465; + let v3468 = v3463 * 5736; + let v3469 = v3451 + v3468; + let v3470 = v3451 - v3468; + let v3471 = v3464 * 4134; + let v3472 = v3452 + v3471; + let v3473 = v3452 - v3471; + let v3474 = v3466 * 722; + let v3475 = v3454 + v3474; + let v3476 = v3454 - v3474; + let v3477 = v3467 * 1305; + let v3478 = v3455 + v3477; + let v3479 = v3455 - v3477; + let v3480 = v3469 * 1646; + let v3481 = v3433 + v3480; + let v3482 = v3433 - v3480; + let v3483 = v3470 * 1212; + let v3484 = v3434 + v3483; + let v3485 = v3434 - v3483; + let v3486 = v3472 * 5860; + let v3487 = v3436 + v3486; + let v3488 = v3436 - v3486; + let v3489 = v3473 * 3195; + let v3490 = v3437 + v3489; + let v3491 = v3437 - v3489; + let v3492 = v3475 * 2545; + let v3493 = v3439 + v3492; + let v3494 = v3439 - v3492; + let v3495 = v3476 * 3621; + let v3496 = v3440 + v3495; + let v3497 = v3440 - v3495; + let v3498 = v3478 * 3504; + let v3499 = v3442 + v3498; + let v3500 = v3442 - v3498; + let v3501 = v3479 * 3542; + let v3502 = v3443 + v3501; + let v3503 = v3443 - v3501; + let v3504 = v3481 * 4591; + let v3505 = v3385 + v3504; + let v3506 = v3385 - v3504; + let v3507 = v3482 * 5728; + let v3508 = v3386 + v3507; + let v3509 = v3386 - v3507; + let v3510 = v3484 * 5023; + let v3511 = v3388 + v3510; + let v3512 = v3388 - v3510; + let v3513 = v3485 * 5828; + let v3514 = v3389 + v3513; + let v3515 = v3389 - v3513; + let v3516 = v3487 * 4978; + let v3517 = v3391 + v3516; + let v3518 = v3391 - v3516; + let v3519 = v3488 * 1351; + let v3520 = v3392 + v3519; + let v3521 = v3392 - v3519; + let v3522 = v3490 * 3328; + let v3523 = v3394 + v3522; + let v3524 = v3394 - v3522; + let v3525 = v3491 * 5777; + let v3526 = v3395 + v3525; + let v3527 = v3395 - v3525; + let v3528 = v3493 * 2975; + let v3529 = v3397 + v3528; + let v3530 = v3397 - v3528; + let v3531 = v3494 * 563; + let v3532 = v3398 + v3531; + let v3533 = v3398 - v3531; + let v3534 = v3496 * 3006; + let v3535 = v3400 + v3534; + let v3536 = v3400 - v3534; + let v3537 = v3497 * 2744; + let v3538 = v3401 + v3537; + let v3539 = v3401 - v3537; + let v3540 = v3499 * 949; + let v3541 = v3403 + v3540; + let v3542 = v3403 - v3540; + let v3543 = v3500 * 2625; + let v3544 = v3404 + v3543; + let v3545 = v3404 - v3543; + let v3546 = v3502 * 4821; + let v3547 = v3406 + v3546; + let v3548 = v3406 - v3546; + let v3549 = v3503 * 2639; + let v3550 = v3407 + v3549; + let v3551 = v3407 - v3549; + let v3552 = v3505 * 1000; + let v3553 = v3265 + v3552; + let v3554 = v3265 - v3552; + let v3555 = v3506 * 4320; + let v3556 = v3266 + v3555; + let v3557 = v3266 - v3555; + let v3558 = v3508 * 3091; + let v3559 = v3268 + v3558; + let v3560 = v3268 - v3558; + let v3561 = v3509 * 81; + let v3562 = v3269 + v3561; + let v3563 = v3269 - v3561; + let v3564 = v3511 * 2963; + let v3565 = v3271 + v3564; + let v3566 = v3271 - v3564; + let v3567 = v3512 * 4896; + let v3568 = v3272 + v3567; + let v3569 = v3272 - v3567; + let v3570 = v3514 * 3051; + let v3571 = v3274 + v3570; + let v3572 = v3274 - v3570; + let v3573 = v3515 * 2366; + let v3574 = v3275 + v3573; + let v3575 = v3275 - v3573; + let v3576 = v3517 * 1853; + let v3577 = v3277 + v3576; + let v3578 = v3277 - v3576; + let v3579 = v3518 * 140; + let v3580 = v3278 + v3579; + let v3581 = v3278 - v3579; + let v3582 = v3520 * 4611; + let v3583 = v3280 + v3582; + let v3584 = v3280 - v3582; + let v3585 = v3521 * 726; + let v3586 = v3281 + v3585; + let v3587 = v3281 - v3585; + let v3588 = v3523 * 4255; + let v3589 = v3283 + v3588; + let v3590 = v3283 - v3588; + let v3591 = v3524 * 1177; + let v3592 = v3284 + v3591; + let v3593 = v3284 - v3591; + let v3594 = v3526 * 2768; + let v3595 = v3286 + v3594; + let v3596 = v3286 - v3594; + let v3597 = v3527 * 1635; + let v3598 = v3287 + v3597; + let v3599 = v3287 - v3597; + let v3600 = v3529 * 3712; + let v3601 = v3289 + v3600; + let v3602 = v3289 - v3600; + let v3603 = v3530 * 3135; + let v3604 = v3290 + v3603; + let v3605 = v3290 - v3603; + let v3606 = v3532 * 2747; + let v3607 = v3292 + v3606; + let v3608 = v3292 - v3606; + let v3609 = v3533 * 4846; + let v3610 = v3293 + v3609; + let v3611 = v3293 - v3609; + let v3612 = v3535 * 3553; + let v3613 = v3295 + v3612; + let v3614 = v3295 - v3612; + let v3615 = v3536 * 4805; + let v3616 = v3296 + v3615; + let v3617 = v3296 - v3615; + let v3618 = v3538 * 2294; + let v3619 = v3298 + v3618; + let v3620 = v3298 - v3618; + let v3621 = v3539 * 1062; + let v3622 = v3299 + v3621; + let v3623 = v3299 - v3621; + let v3624 = v3541 * 1326; + let v3625 = v3301 + v3624; + let v3626 = v3301 - v3624; + let v3627 = v3542 * 5086; + let v3628 = v3302 + v3627; + let v3629 = v3302 - v3627; + let v3630 = v3544 * 3014; + let v3631 = v3304 + v3630; + let v3632 = v3304 - v3630; + let v3633 = v3545 * 3201; + let v3634 = v3305 + v3633; + let v3635 = v3305 - v3633; + let v3636 = v3547 * 1170; + let v3637 = v3307 + v3636; + let v3638 = v3307 - v3636; + let v3639 = v3548 * 2319; + let v3640 = v3308 + v3639; + let v3641 = v3308 - v3639; + let v3642 = v3550 * 955; + let v3643 = v3310 + v3642; + let v3644 = v3310 - v3642; + let v3645 = v3551 * 790; + let v3646 = v3311 + v3645; + let v3647 = v3311 - v3645; + let v3648 = f261 * 1479; + let v3649 = f5 + v3648; + let v3650 = f5 - v3648; + let v3651 = f389 * 1479; + let v3652 = f133 + v3651; + let v3653 = f133 - v3651; + let v3654 = v3652 * 4043; + let v3655 = v3649 + v3654; + let v3656 = v3649 - v3654; + let v3657 = v3653 * 5146; + let v3658 = v3650 + v3657; + let v3659 = v3650 - v3657; + let v3660 = f325 * 1479; + let v3661 = f69 + v3660; + let v3662 = f69 - v3660; + let v3663 = f453 * 1479; + let v3664 = f197 + v3663; + let v3665 = f197 - v3663; + let v3666 = v3664 * 4043; + let v3667 = v3661 + v3666; + let v3668 = v3661 - v3666; + let v3669 = v3665 * 5146; + let v3670 = v3662 + v3669; + let v3671 = v3662 - v3669; + let v3672 = v3667 * 5736; + let v3673 = v3655 + v3672; + let v3674 = v3655 - v3672; + let v3675 = v3668 * 4134; + let v3676 = v3656 + v3675; + let v3677 = v3656 - v3675; + let v3678 = v3670 * 722; + let v3679 = v3658 + v3678; + let v3680 = v3658 - v3678; + let v3681 = v3671 * 1305; + let v3682 = v3659 + v3681; + let v3683 = v3659 - v3681; + let v3684 = f293 * 1479; + let v3685 = f37 + v3684; + let v3686 = f37 - v3684; + let v3687 = f421 * 1479; + let v3688 = f165 + v3687; + let v3689 = f165 - v3687; + let v3690 = v3688 * 4043; + let v3691 = v3685 + v3690; + let v3692 = v3685 - v3690; + let v3693 = v3689 * 5146; + let v3694 = v3686 + v3693; + let v3695 = v3686 - v3693; + let v3696 = f357 * 1479; + let v3697 = f101 + v3696; + let v3698 = f101 - v3696; + let v3699 = f485 * 1479; + let v3700 = f229 + v3699; + let v3701 = f229 - v3699; + let v3702 = v3700 * 4043; + let v3703 = v3697 + v3702; + let v3704 = v3697 - v3702; + let v3705 = v3701 * 5146; + let v3706 = v3698 + v3705; + let v3707 = v3698 - v3705; + let v3708 = v3703 * 5736; + let v3709 = v3691 + v3708; + let v3710 = v3691 - v3708; + let v3711 = v3704 * 4134; + let v3712 = v3692 + v3711; + let v3713 = v3692 - v3711; + let v3714 = v3706 * 722; + let v3715 = v3694 + v3714; + let v3716 = v3694 - v3714; + let v3717 = v3707 * 1305; + let v3718 = v3695 + v3717; + let v3719 = v3695 - v3717; + let v3720 = v3709 * 1646; + let v3721 = v3673 + v3720; + let v3722 = v3673 - v3720; + let v3723 = v3710 * 1212; + let v3724 = v3674 + v3723; + let v3725 = v3674 - v3723; + let v3726 = v3712 * 5860; + let v3727 = v3676 + v3726; + let v3728 = v3676 - v3726; + let v3729 = v3713 * 3195; + let v3730 = v3677 + v3729; + let v3731 = v3677 - v3729; + let v3732 = v3715 * 2545; + let v3733 = v3679 + v3732; + let v3734 = v3679 - v3732; + let v3735 = v3716 * 3621; + let v3736 = v3680 + v3735; + let v3737 = v3680 - v3735; + let v3738 = v3718 * 3504; + let v3739 = v3682 + v3738; + let v3740 = v3682 - v3738; + let v3741 = v3719 * 3542; + let v3742 = v3683 + v3741; + let v3743 = v3683 - v3741; + let v3744 = f277 * 1479; + let v3745 = f21 + v3744; + let v3746 = f21 - v3744; + let v3747 = f405 * 1479; + let v3748 = f149 + v3747; + let v3749 = f149 - v3747; + let v3750 = v3748 * 4043; + let v3751 = v3745 + v3750; + let v3752 = v3745 - v3750; + let v3753 = v3749 * 5146; + let v3754 = v3746 + v3753; + let v3755 = v3746 - v3753; + let v3756 = f341 * 1479; + let v3757 = f85 + v3756; + let v3758 = f85 - v3756; + let v3759 = f469 * 1479; + let v3760 = f213 + v3759; + let v3761 = f213 - v3759; + let v3762 = v3760 * 4043; + let v3763 = v3757 + v3762; + let v3764 = v3757 - v3762; + let v3765 = v3761 * 5146; + let v3766 = v3758 + v3765; + let v3767 = v3758 - v3765; + let v3768 = v3763 * 5736; + let v3769 = v3751 + v3768; + let v3770 = v3751 - v3768; + let v3771 = v3764 * 4134; + let v3772 = v3752 + v3771; + let v3773 = v3752 - v3771; + let v3774 = v3766 * 722; + let v3775 = v3754 + v3774; + let v3776 = v3754 - v3774; + let v3777 = v3767 * 1305; + let v3778 = v3755 + v3777; + let v3779 = v3755 - v3777; + let v3780 = f309 * 1479; + let v3781 = f53 + v3780; + let v3782 = f53 - v3780; + let v3783 = f437 * 1479; + let v3784 = f181 + v3783; + let v3785 = f181 - v3783; + let v3786 = v3784 * 4043; + let v3787 = v3781 + v3786; + let v3788 = v3781 - v3786; + let v3789 = v3785 * 5146; + let v3790 = v3782 + v3789; + let v3791 = v3782 - v3789; + let v3792 = f373 * 1479; + let v3793 = f117 + v3792; + let v3794 = f117 - v3792; + let v3795 = f501 * 1479; + let v3796 = f245 + v3795; + let v3797 = f245 - v3795; + let v3798 = v3796 * 4043; + let v3799 = v3793 + v3798; + let v3800 = v3793 - v3798; + let v3801 = v3797 * 5146; + let v3802 = v3794 + v3801; + let v3803 = v3794 - v3801; + let v3804 = v3799 * 5736; + let v3805 = v3787 + v3804; + let v3806 = v3787 - v3804; + let v3807 = v3800 * 4134; + let v3808 = v3788 + v3807; + let v3809 = v3788 - v3807; + let v3810 = v3802 * 722; + let v3811 = v3790 + v3810; + let v3812 = v3790 - v3810; + let v3813 = v3803 * 1305; + let v3814 = v3791 + v3813; + let v3815 = v3791 - v3813; + let v3816 = v3805 * 1646; + let v3817 = v3769 + v3816; + let v3818 = v3769 - v3816; + let v3819 = v3806 * 1212; + let v3820 = v3770 + v3819; + let v3821 = v3770 - v3819; + let v3822 = v3808 * 5860; + let v3823 = v3772 + v3822; + let v3824 = v3772 - v3822; + let v3825 = v3809 * 3195; + let v3826 = v3773 + v3825; + let v3827 = v3773 - v3825; + let v3828 = v3811 * 2545; + let v3829 = v3775 + v3828; + let v3830 = v3775 - v3828; + let v3831 = v3812 * 3621; + let v3832 = v3776 + v3831; + let v3833 = v3776 - v3831; + let v3834 = v3814 * 3504; + let v3835 = v3778 + v3834; + let v3836 = v3778 - v3834; + let v3837 = v3815 * 3542; + let v3838 = v3779 + v3837; + let v3839 = v3779 - v3837; + let v3840 = v3817 * 4591; + let v3841 = v3721 + v3840; + let v3842 = v3721 - v3840; + let v3843 = v3818 * 5728; + let v3844 = v3722 + v3843; + let v3845 = v3722 - v3843; + let v3846 = v3820 * 5023; + let v3847 = v3724 + v3846; + let v3848 = v3724 - v3846; + let v3849 = v3821 * 5828; + let v3850 = v3725 + v3849; + let v3851 = v3725 - v3849; + let v3852 = v3823 * 4978; + let v3853 = v3727 + v3852; + let v3854 = v3727 - v3852; + let v3855 = v3824 * 1351; + let v3856 = v3728 + v3855; + let v3857 = v3728 - v3855; + let v3858 = v3826 * 3328; + let v3859 = v3730 + v3858; + let v3860 = v3730 - v3858; + let v3861 = v3827 * 5777; + let v3862 = v3731 + v3861; + let v3863 = v3731 - v3861; + let v3864 = v3829 * 2975; + let v3865 = v3733 + v3864; + let v3866 = v3733 - v3864; + let v3867 = v3830 * 563; + let v3868 = v3734 + v3867; + let v3869 = v3734 - v3867; + let v3870 = v3832 * 3006; + let v3871 = v3736 + v3870; + let v3872 = v3736 - v3870; + let v3873 = v3833 * 2744; + let v3874 = v3737 + v3873; + let v3875 = v3737 - v3873; + let v3876 = v3835 * 949; + let v3877 = v3739 + v3876; + let v3878 = v3739 - v3876; + let v3879 = v3836 * 2625; + let v3880 = v3740 + v3879; + let v3881 = v3740 - v3879; + let v3882 = v3838 * 4821; + let v3883 = v3742 + v3882; + let v3884 = v3742 - v3882; + let v3885 = v3839 * 2639; + let v3886 = v3743 + v3885; + let v3887 = v3743 - v3885; + let v3888 = f269 * 1479; + let v3889 = f13 + v3888; + let v3890 = f13 - v3888; + let v3891 = f397 * 1479; + let v3892 = f141 + v3891; + let v3893 = f141 - v3891; + let v3894 = v3892 * 4043; + let v3895 = v3889 + v3894; + let v3896 = v3889 - v3894; + let v3897 = v3893 * 5146; + let v3898 = v3890 + v3897; + let v3899 = v3890 - v3897; + let v3900 = f333 * 1479; + let v3901 = f77 + v3900; + let v3902 = f77 - v3900; + let v3903 = f461 * 1479; + let v3904 = f205 + v3903; + let v3905 = f205 - v3903; + let v3906 = v3904 * 4043; + let v3907 = v3901 + v3906; + let v3908 = v3901 - v3906; + let v3909 = v3905 * 5146; + let v3910 = v3902 + v3909; + let v3911 = v3902 - v3909; + let v3912 = v3907 * 5736; + let v3913 = v3895 + v3912; + let v3914 = v3895 - v3912; + let v3915 = v3908 * 4134; + let v3916 = v3896 + v3915; + let v3917 = v3896 - v3915; + let v3918 = v3910 * 722; + let v3919 = v3898 + v3918; + let v3920 = v3898 - v3918; + let v3921 = v3911 * 1305; + let v3922 = v3899 + v3921; + let v3923 = v3899 - v3921; + let v3924 = f301 * 1479; + let v3925 = f45 + v3924; + let v3926 = f45 - v3924; + let v3927 = f429 * 1479; + let v3928 = f173 + v3927; + let v3929 = f173 - v3927; + let v3930 = v3928 * 4043; + let v3931 = v3925 + v3930; + let v3932 = v3925 - v3930; + let v3933 = v3929 * 5146; + let v3934 = v3926 + v3933; + let v3935 = v3926 - v3933; + let v3936 = f365 * 1479; + let v3937 = f109 + v3936; + let v3938 = f109 - v3936; + let v3939 = f493 * 1479; + let v3940 = f237 + v3939; + let v3941 = f237 - v3939; + let v3942 = v3940 * 4043; + let v3943 = v3937 + v3942; + let v3944 = v3937 - v3942; + let v3945 = v3941 * 5146; + let v3946 = v3938 + v3945; + let v3947 = v3938 - v3945; + let v3948 = v3943 * 5736; + let v3949 = v3931 + v3948; + let v3950 = v3931 - v3948; + let v3951 = v3944 * 4134; + let v3952 = v3932 + v3951; + let v3953 = v3932 - v3951; + let v3954 = v3946 * 722; + let v3955 = v3934 + v3954; + let v3956 = v3934 - v3954; + let v3957 = v3947 * 1305; + let v3958 = v3935 + v3957; + let v3959 = v3935 - v3957; + let v3960 = v3949 * 1646; + let v3961 = v3913 + v3960; + let v3962 = v3913 - v3960; + let v3963 = v3950 * 1212; + let v3964 = v3914 + v3963; + let v3965 = v3914 - v3963; + let v3966 = v3952 * 5860; + let v3967 = v3916 + v3966; + let v3968 = v3916 - v3966; + let v3969 = v3953 * 3195; + let v3970 = v3917 + v3969; + let v3971 = v3917 - v3969; + let v3972 = v3955 * 2545; + let v3973 = v3919 + v3972; + let v3974 = v3919 - v3972; + let v3975 = v3956 * 3621; + let v3976 = v3920 + v3975; + let v3977 = v3920 - v3975; + let v3978 = v3958 * 3504; + let v3979 = v3922 + v3978; + let v3980 = v3922 - v3978; + let v3981 = v3959 * 3542; + let v3982 = v3923 + v3981; + let v3983 = v3923 - v3981; + let v3984 = f285 * 1479; + let v3985 = f29 + v3984; + let v3986 = f29 - v3984; + let v3987 = f413 * 1479; + let v3988 = f157 + v3987; + let v3989 = f157 - v3987; + let v3990 = v3988 * 4043; + let v3991 = v3985 + v3990; + let v3992 = v3985 - v3990; + let v3993 = v3989 * 5146; + let v3994 = v3986 + v3993; + let v3995 = v3986 - v3993; + let v3996 = f349 * 1479; + let v3997 = f93 + v3996; + let v3998 = f93 - v3996; + let v3999 = f477 * 1479; + let v4000 = f221 + v3999; + let v4001 = f221 - v3999; + let v4002 = v4000 * 4043; + let v4003 = v3997 + v4002; + let v4004 = v3997 - v4002; + let v4005 = v4001 * 5146; + let v4006 = v3998 + v4005; + let v4007 = v3998 - v4005; + let v4008 = v4003 * 5736; + let v4009 = v3991 + v4008; + let v4010 = v3991 - v4008; + let v4011 = v4004 * 4134; + let v4012 = v3992 + v4011; + let v4013 = v3992 - v4011; + let v4014 = v4006 * 722; + let v4015 = v3994 + v4014; + let v4016 = v3994 - v4014; + let v4017 = v4007 * 1305; + let v4018 = v3995 + v4017; + let v4019 = v3995 - v4017; + let v4020 = f317 * 1479; + let v4021 = f61 + v4020; + let v4022 = f61 - v4020; + let v4023 = f445 * 1479; + let v4024 = f189 + v4023; + let v4025 = f189 - v4023; + let v4026 = v4024 * 4043; + let v4027 = v4021 + v4026; + let v4028 = v4021 - v4026; + let v4029 = v4025 * 5146; + let v4030 = v4022 + v4029; + let v4031 = v4022 - v4029; + let v4032 = f381 * 1479; + let v4033 = f125 + v4032; + let v4034 = f125 - v4032; + let v4035 = f509 * 1479; + let v4036 = f253 + v4035; + let v4037 = f253 - v4035; + let v4038 = v4036 * 4043; + let v4039 = v4033 + v4038; + let v4040 = v4033 - v4038; + let v4041 = v4037 * 5146; + let v4042 = v4034 + v4041; + let v4043 = v4034 - v4041; + let v4044 = v4039 * 5736; + let v4045 = v4027 + v4044; + let v4046 = v4027 - v4044; + let v4047 = v4040 * 4134; + let v4048 = v4028 + v4047; + let v4049 = v4028 - v4047; + let v4050 = v4042 * 722; + let v4051 = v4030 + v4050; + let v4052 = v4030 - v4050; + let v4053 = v4043 * 1305; + let v4054 = v4031 + v4053; + let v4055 = v4031 - v4053; + let v4056 = v4045 * 1646; + let v4057 = v4009 + v4056; + let v4058 = v4009 - v4056; + let v4059 = v4046 * 1212; + let v4060 = v4010 + v4059; + let v4061 = v4010 - v4059; + let v4062 = v4048 * 5860; + let v4063 = v4012 + v4062; + let v4064 = v4012 - v4062; + let v4065 = v4049 * 3195; + let v4066 = v4013 + v4065; + let v4067 = v4013 - v4065; + let v4068 = v4051 * 2545; + let v4069 = v4015 + v4068; + let v4070 = v4015 - v4068; + let v4071 = v4052 * 3621; + let v4072 = v4016 + v4071; + let v4073 = v4016 - v4071; + let v4074 = v4054 * 3504; + let v4075 = v4018 + v4074; + let v4076 = v4018 - v4074; + let v4077 = v4055 * 3542; + let v4078 = v4019 + v4077; + let v4079 = v4019 - v4077; + let v4080 = v4057 * 4591; + let v4081 = v3961 + v4080; + let v4082 = v3961 - v4080; + let v4083 = v4058 * 5728; + let v4084 = v3962 + v4083; + let v4085 = v3962 - v4083; + let v4086 = v4060 * 5023; + let v4087 = v3964 + v4086; + let v4088 = v3964 - v4086; + let v4089 = v4061 * 5828; + let v4090 = v3965 + v4089; + let v4091 = v3965 - v4089; + let v4092 = v4063 * 4978; + let v4093 = v3967 + v4092; + let v4094 = v3967 - v4092; + let v4095 = v4064 * 1351; + let v4096 = v3968 + v4095; + let v4097 = v3968 - v4095; + let v4098 = v4066 * 3328; + let v4099 = v3970 + v4098; + let v4100 = v3970 - v4098; + let v4101 = v4067 * 5777; + let v4102 = v3971 + v4101; + let v4103 = v3971 - v4101; + let v4104 = v4069 * 2975; + let v4105 = v3973 + v4104; + let v4106 = v3973 - v4104; + let v4107 = v4070 * 563; + let v4108 = v3974 + v4107; + let v4109 = v3974 - v4107; + let v4110 = v4072 * 3006; + let v4111 = v3976 + v4110; + let v4112 = v3976 - v4110; + let v4113 = v4073 * 2744; + let v4114 = v3977 + v4113; + let v4115 = v3977 - v4113; + let v4116 = v4075 * 949; + let v4117 = v3979 + v4116; + let v4118 = v3979 - v4116; + let v4119 = v4076 * 2625; + let v4120 = v3980 + v4119; + let v4121 = v3980 - v4119; + let v4122 = v4078 * 4821; + let v4123 = v3982 + v4122; + let v4124 = v3982 - v4122; + let v4125 = v4079 * 2639; + let v4126 = v3983 + v4125; + let v4127 = v3983 - v4125; + let v4128 = v4081 * 1000; + let v4129 = v3841 + v4128; + let v4130 = v3841 - v4128; + let v4131 = v4082 * 4320; + let v4132 = v3842 + v4131; + let v4133 = v3842 - v4131; + let v4134 = v4084 * 3091; + let v4135 = v3844 + v4134; + let v4136 = v3844 - v4134; + let v4137 = v4085 * 81; + let v4138 = v3845 + v4137; + let v4139 = v3845 - v4137; + let v4140 = v4087 * 2963; + let v4141 = v3847 + v4140; + let v4142 = v3847 - v4140; + let v4143 = v4088 * 4896; + let v4144 = v3848 + v4143; + let v4145 = v3848 - v4143; + let v4146 = v4090 * 3051; + let v4147 = v3850 + v4146; + let v4148 = v3850 - v4146; + let v4149 = v4091 * 2366; + let v4150 = v3851 + v4149; + let v4151 = v3851 - v4149; + let v4152 = v4093 * 1853; + let v4153 = v3853 + v4152; + let v4154 = v3853 - v4152; + let v4155 = v4094 * 140; + let v4156 = v3854 + v4155; + let v4157 = v3854 - v4155; + let v4158 = v4096 * 4611; + let v4159 = v3856 + v4158; + let v4160 = v3856 - v4158; + let v4161 = v4097 * 726; + let v4162 = v3857 + v4161; + let v4163 = v3857 - v4161; + let v4164 = v4099 * 4255; + let v4165 = v3859 + v4164; + let v4166 = v3859 - v4164; + let v4167 = v4100 * 1177; + let v4168 = v3860 + v4167; + let v4169 = v3860 - v4167; + let v4170 = v4102 * 2768; + let v4171 = v3862 + v4170; + let v4172 = v3862 - v4170; + let v4173 = v4103 * 1635; + let v4174 = v3863 + v4173; + let v4175 = v3863 - v4173; + let v4176 = v4105 * 3712; + let v4177 = v3865 + v4176; + let v4178 = v3865 - v4176; + let v4179 = v4106 * 3135; + let v4180 = v3866 + v4179; + let v4181 = v3866 - v4179; + let v4182 = v4108 * 2747; + let v4183 = v3868 + v4182; + let v4184 = v3868 - v4182; + let v4185 = v4109 * 4846; + let v4186 = v3869 + v4185; + let v4187 = v3869 - v4185; + let v4188 = v4111 * 3553; + let v4189 = v3871 + v4188; + let v4190 = v3871 - v4188; + let v4191 = v4112 * 4805; + let v4192 = v3872 + v4191; + let v4193 = v3872 - v4191; + let v4194 = v4114 * 2294; + let v4195 = v3874 + v4194; + let v4196 = v3874 - v4194; + let v4197 = v4115 * 1062; + let v4198 = v3875 + v4197; + let v4199 = v3875 - v4197; + let v4200 = v4117 * 1326; + let v4201 = v3877 + v4200; + let v4202 = v3877 - v4200; + let v4203 = v4118 * 5086; + let v4204 = v3878 + v4203; + let v4205 = v3878 - v4203; + let v4206 = v4120 * 3014; + let v4207 = v3880 + v4206; + let v4208 = v3880 - v4206; + let v4209 = v4121 * 3201; + let v4210 = v3881 + v4209; + let v4211 = v3881 - v4209; + let v4212 = v4123 * 1170; + let v4213 = v3883 + v4212; + let v4214 = v3883 - v4212; + let v4215 = v4124 * 2319; + let v4216 = v3884 + v4215; + let v4217 = v3884 - v4215; + let v4218 = v4126 * 955; + let v4219 = v3886 + v4218; + let v4220 = v3886 - v4218; + let v4221 = v4127 * 790; + let v4222 = v3887 + v4221; + let v4223 = v3887 - v4221; + let v4224 = v4129 * 544; + let v4225 = v3553 + v4224; + let v4226 = v3553 - v4224; + let v4227 = v4130 * 5791; + let v4228 = v3554 + v4227; + let v4229 = v3554 - v4227; + let v4230 = v4132 * 339; + let v4231 = v3556 + v4230; + let v4232 = v3556 - v4230; + let v4233 = v4133 * 2468; + let v4234 = v3557 + v4233; + let v4235 = v3557 - v4233; + let v4236 = v4135 * 2842; + let v4237 = v3559 + v4236; + let v4238 = v3559 - v4236; + let v4239 = v4136 * 480; + let v4240 = v3560 + v4239; + let v4241 = v3560 - v4239; + let v4242 = v4138 * 9; + let v4243 = v3562 + v4242; + let v4244 = v3562 - v4242; + let v4245 = v4139 * 1022; + let v4246 = v3563 + v4245; + let v4247 = v3563 - v4245; + let v4248 = v4141 * 4278; + let v4249 = v3565 + v4248; + let v4250 = v3565 - v4248; + let v4251 = v4142 * 1673; + let v4252 = v3566 + v4251; + let v4253 = v3566 - v4251; + let v4254 = v4144 * 4989; + let v4255 = v3568 + v4254; + let v4256 = v3568 - v4254; + let v4257 = v4145 * 5331; + let v4258 = v3569 + v4257; + let v4259 = v3569 - v4257; + let v4260 = v4147 * 3584; + let v4261 = v3571 + v4260; + let v4262 = v3571 - v4260; + let v4263 = v4148 * 4177; + let v4264 = v3572 + v4263; + let v4265 = v3572 - v4263; + let v4266 = v4150 * 1381; + let v4267 = v3574 + v4266; + let v4268 = v3574 - v4266; + let v4269 = v4151 * 2525; + let v4270 = v3575 + v4269; + let v4271 = v3575 - v4269; + let v4272 = v4153 * 2396; + let v4273 = v3577 + v4272; + let v4274 = v3577 - v4272; + let v4275 = v4154 * 4452; + let v4276 = v3578 + v4275; + let v4277 = v3578 - v4275; + let v4278 = v4156 * 3296; + let v4279 = v3580 + v4278; + let v4280 = v3580 - v4278; + let v4281 = v4157 * 3949; + let v4282 = v3581 + v4281; + let v4283 = v3581 - v4281; + let v4284 = v4159 * 130; + let v4285 = v3583 + v4284; + let v4286 = v3583 - v4284; + let v4287 = v4160 * 4354; + let v4288 = v3584 + v4287; + let v4289 = v3584 - v4287; + let v4290 = v4162 * 5374; + let v4291 = v3586 + v4290; + let v4292 = v3586 - v4290; + let v4293 = v4163 * 2837; + let v4294 = v3587 + v4293; + let v4295 = v3587 - v4293; + let v4296 = v4165 * 5767; + let v4297 = v3589 + v4296; + let v4298 = v3589 - v4296; + let v4299 = v4166 * 827; + let v4300 = v3590 + v4299; + let v4301 = v3590 - v4299; + let v4302 = v4168 * 3748; + let v4303 = v3592 + v4302; + let v4304 = v3592 - v4302; + let v4305 = v4169 * 953; + let v4306 = v3593 + v4305; + let v4307 = v3593 - v4305; + let v4308 = v4171 * 5067; + let v4309 = v3595 + v4308; + let v4310 = v3595 - v4308; + let v4311 = v4172 * 2197; + let v4312 = v3596 + v4311; + let v4313 = v3596 - v4311; + let v4314 = v4174 * 118; + let v4315 = v3598 + v4314; + let v4316 = v3598 - v4314; + let v4317 = v4175 * 2476; + let v4318 = v3599 + v4317; + let v4319 = v3599 - v4317; + let v4320 = v4177 * 2548; + let v4321 = v3601 + v4320; + let v4322 = v3601 - v4320; + let v4323 = v4178 * 4231; + let v4324 = v3602 + v4323; + let v4325 = v3602 - v4323; + let v4326 = v4180 * 355; + let v4327 = v3604 + v4326; + let v4328 = v3604 - v4326; + let v4329 = v4181 * 3382; + let v4330 = v3605 + v4329; + let v4331 = v3605 - v4329; + let v4332 = v4183 * 3707; + let v4333 = v3607 + v4332; + let v4334 = v3607 - v4332; + let v4335 = v4184 * 1759; + let v4336 = v3608 + v4335; + let v4337 = v3608 - v4335; + let v4338 = v4186 * 3694; + let v4339 = v3610 + v4338; + let v4340 = v3610 - v4338; + let v4341 = v4187 * 5179; + let v4342 = v3611 + v4341; + let v4343 = v3611 - v4341; + let v4344 = v4189 * 5542; + let v4345 = v3613 + v4344; + let v4346 = v3613 - v4344; + let v4347 = v4190 * 145; + let v4348 = v3614 + v4347; + let v4349 = v3614 - v4347; + let v4350 = v4192 * 3637; + let v4351 = v3616 + v4350; + let v4352 = v3616 - v4350; + let v4353 = v4193 * 3459; + let v4354 = v3617 + v4353; + let v4355 = v3617 - v4353; + let v4356 = v4195 * 5911; + let v4357 = v3619 + v4356; + let v4358 = v3619 - v4356; + let v4359 = v4196 * 4890; + let v4360 = v3620 + v4359; + let v4361 = v3620 - v4359; + let v4362 = v4198 * 3932; + let v4363 = v3622 + v4362; + let v4364 = v3622 - v4362; + let v4365 = v4199 * 2731; + let v4366 = v3623 + v4365; + let v4367 = v3623 - v4365; + let v4368 = v4201 * 2089; + let v4369 = v3625 + v4368; + let v4370 = v3625 - v4368; + let v4371 = v4202 * 5092; + let v4372 = v3626 + v4371; + let v4373 = v3626 - v4371; + let v4374 = v4204 * 2881; + let v4375 = v3628 + v4374; + let v4376 = v3628 - v4374; + let v4377 = v4205 * 3284; + let v4378 = v3629 + v4377; + let v4379 = v3629 - v4377; + let v4380 = v4207 * 729; + let v4381 = v3631 + v4380; + let v4382 = v3631 - v4380; + let v4383 = v4208 * 3241; + let v4384 = v3632 + v4383; + let v4385 = v3632 - v4383; + let v4386 = v4210 * 3289; + let v4387 = v3634 + v4386; + let v4388 = v3634 - v4386; + let v4389 = v4211 * 2013; + let v4390 = v3635 + v4389; + let v4391 = v3635 - v4389; + let v4392 = v4213 * 5755; + let v4393 = v3637 + v4392; + let v4394 = v3637 - v4392; + let v4395 = v4214 * 4632; + let v4396 = v3638 + v4395; + let v4397 = v3638 - v4395; + let v4398 = v4216 * 1260; + let v4399 = v3640 + v4398; + let v4400 = v3640 - v4398; + let v4401 = v4217 * 4388; + let v4402 = v3641 + v4401; + let v4403 = v3641 - v4401; + let v4404 = v4219 * 334; + let v4405 = v3643 + v4404; + let v4406 = v3643 - v4404; + let v4407 = v4220 * 2426; + let v4408 = v3644 + v4407; + let v4409 = v3644 - v4407; + let v4410 = v4222 * 1696; + let v4411 = v3646 + v4410; + let v4412 = v3646 - v4410; + let v4413 = v4223 * 1428; + let v4414 = v3647 + v4413; + let v4415 = v3647 - v4413; + let v4416 = f259 * 1479; + let v4417 = f3 + v4416; + let v4418 = f3 - v4416; + let v4419 = f387 * 1479; + let v4420 = f131 + v4419; + let v4421 = f131 - v4419; + let v4422 = v4420 * 4043; + let v4423 = v4417 + v4422; + let v4424 = v4417 - v4422; + let v4425 = v4421 * 5146; + let v4426 = v4418 + v4425; + let v4427 = v4418 - v4425; + let v4428 = f323 * 1479; + let v4429 = f67 + v4428; + let v4430 = f67 - v4428; + let v4431 = f451 * 1479; + let v4432 = f195 + v4431; + let v4433 = f195 - v4431; + let v4434 = v4432 * 4043; + let v4435 = v4429 + v4434; + let v4436 = v4429 - v4434; + let v4437 = v4433 * 5146; + let v4438 = v4430 + v4437; + let v4439 = v4430 - v4437; + let v4440 = v4435 * 5736; + let v4441 = v4423 + v4440; + let v4442 = v4423 - v4440; + let v4443 = v4436 * 4134; + let v4444 = v4424 + v4443; + let v4445 = v4424 - v4443; + let v4446 = v4438 * 722; + let v4447 = v4426 + v4446; + let v4448 = v4426 - v4446; + let v4449 = v4439 * 1305; + let v4450 = v4427 + v4449; + let v4451 = v4427 - v4449; + let v4452 = f291 * 1479; + let v4453 = f35 + v4452; + let v4454 = f35 - v4452; + let v4455 = f419 * 1479; + let v4456 = f163 + v4455; + let v4457 = f163 - v4455; + let v4458 = v4456 * 4043; + let v4459 = v4453 + v4458; + let v4460 = v4453 - v4458; + let v4461 = v4457 * 5146; + let v4462 = v4454 + v4461; + let v4463 = v4454 - v4461; + let v4464 = f355 * 1479; + let v4465 = f99 + v4464; + let v4466 = f99 - v4464; + let v4467 = f483 * 1479; + let v4468 = f227 + v4467; + let v4469 = f227 - v4467; + let v4470 = v4468 * 4043; + let v4471 = v4465 + v4470; + let v4472 = v4465 - v4470; + let v4473 = v4469 * 5146; + let v4474 = v4466 + v4473; + let v4475 = v4466 - v4473; + let v4476 = v4471 * 5736; + let v4477 = v4459 + v4476; + let v4478 = v4459 - v4476; + let v4479 = v4472 * 4134; + let v4480 = v4460 + v4479; + let v4481 = v4460 - v4479; + let v4482 = v4474 * 722; + let v4483 = v4462 + v4482; + let v4484 = v4462 - v4482; + let v4485 = v4475 * 1305; + let v4486 = v4463 + v4485; + let v4487 = v4463 - v4485; + let v4488 = v4477 * 1646; + let v4489 = v4441 + v4488; + let v4490 = v4441 - v4488; + let v4491 = v4478 * 1212; + let v4492 = v4442 + v4491; + let v4493 = v4442 - v4491; + let v4494 = v4480 * 5860; + let v4495 = v4444 + v4494; + let v4496 = v4444 - v4494; + let v4497 = v4481 * 3195; + let v4498 = v4445 + v4497; + let v4499 = v4445 - v4497; + let v4500 = v4483 * 2545; + let v4501 = v4447 + v4500; + let v4502 = v4447 - v4500; + let v4503 = v4484 * 3621; + let v4504 = v4448 + v4503; + let v4505 = v4448 - v4503; + let v4506 = v4486 * 3504; + let v4507 = v4450 + v4506; + let v4508 = v4450 - v4506; + let v4509 = v4487 * 3542; + let v4510 = v4451 + v4509; + let v4511 = v4451 - v4509; + let v4512 = f275 * 1479; + let v4513 = f19 + v4512; + let v4514 = f19 - v4512; + let v4515 = f403 * 1479; + let v4516 = f147 + v4515; + let v4517 = f147 - v4515; + let v4518 = v4516 * 4043; + let v4519 = v4513 + v4518; + let v4520 = v4513 - v4518; + let v4521 = v4517 * 5146; + let v4522 = v4514 + v4521; + let v4523 = v4514 - v4521; + let v4524 = f339 * 1479; + let v4525 = f83 + v4524; + let v4526 = f83 - v4524; + let v4527 = f467 * 1479; + let v4528 = f211 + v4527; + let v4529 = f211 - v4527; + let v4530 = v4528 * 4043; + let v4531 = v4525 + v4530; + let v4532 = v4525 - v4530; + let v4533 = v4529 * 5146; + let v4534 = v4526 + v4533; + let v4535 = v4526 - v4533; + let v4536 = v4531 * 5736; + let v4537 = v4519 + v4536; + let v4538 = v4519 - v4536; + let v4539 = v4532 * 4134; + let v4540 = v4520 + v4539; + let v4541 = v4520 - v4539; + let v4542 = v4534 * 722; + let v4543 = v4522 + v4542; + let v4544 = v4522 - v4542; + let v4545 = v4535 * 1305; + let v4546 = v4523 + v4545; + let v4547 = v4523 - v4545; + let v4548 = f307 * 1479; + let v4549 = f51 + v4548; + let v4550 = f51 - v4548; + let v4551 = f435 * 1479; + let v4552 = f179 + v4551; + let v4553 = f179 - v4551; + let v4554 = v4552 * 4043; + let v4555 = v4549 + v4554; + let v4556 = v4549 - v4554; + let v4557 = v4553 * 5146; + let v4558 = v4550 + v4557; + let v4559 = v4550 - v4557; + let v4560 = f371 * 1479; + let v4561 = f115 + v4560; + let v4562 = f115 - v4560; + let v4563 = f499 * 1479; + let v4564 = f243 + v4563; + let v4565 = f243 - v4563; + let v4566 = v4564 * 4043; + let v4567 = v4561 + v4566; + let v4568 = v4561 - v4566; + let v4569 = v4565 * 5146; + let v4570 = v4562 + v4569; + let v4571 = v4562 - v4569; + let v4572 = v4567 * 5736; + let v4573 = v4555 + v4572; + let v4574 = v4555 - v4572; + let v4575 = v4568 * 4134; + let v4576 = v4556 + v4575; + let v4577 = v4556 - v4575; + let v4578 = v4570 * 722; + let v4579 = v4558 + v4578; + let v4580 = v4558 - v4578; + let v4581 = v4571 * 1305; + let v4582 = v4559 + v4581; + let v4583 = v4559 - v4581; + let v4584 = v4573 * 1646; + let v4585 = v4537 + v4584; + let v4586 = v4537 - v4584; + let v4587 = v4574 * 1212; + let v4588 = v4538 + v4587; + let v4589 = v4538 - v4587; + let v4590 = v4576 * 5860; + let v4591 = v4540 + v4590; + let v4592 = v4540 - v4590; + let v4593 = v4577 * 3195; + let v4594 = v4541 + v4593; + let v4595 = v4541 - v4593; + let v4596 = v4579 * 2545; + let v4597 = v4543 + v4596; + let v4598 = v4543 - v4596; + let v4599 = v4580 * 3621; + let v4600 = v4544 + v4599; + let v4601 = v4544 - v4599; + let v4602 = v4582 * 3504; + let v4603 = v4546 + v4602; + let v4604 = v4546 - v4602; + let v4605 = v4583 * 3542; + let v4606 = v4547 + v4605; + let v4607 = v4547 - v4605; + let v4608 = v4585 * 4591; + let v4609 = v4489 + v4608; + let v4610 = v4489 - v4608; + let v4611 = v4586 * 5728; + let v4612 = v4490 + v4611; + let v4613 = v4490 - v4611; + let v4614 = v4588 * 5023; + let v4615 = v4492 + v4614; + let v4616 = v4492 - v4614; + let v4617 = v4589 * 5828; + let v4618 = v4493 + v4617; + let v4619 = v4493 - v4617; + let v4620 = v4591 * 4978; + let v4621 = v4495 + v4620; + let v4622 = v4495 - v4620; + let v4623 = v4592 * 1351; + let v4624 = v4496 + v4623; + let v4625 = v4496 - v4623; + let v4626 = v4594 * 3328; + let v4627 = v4498 + v4626; + let v4628 = v4498 - v4626; + let v4629 = v4595 * 5777; + let v4630 = v4499 + v4629; + let v4631 = v4499 - v4629; + let v4632 = v4597 * 2975; + let v4633 = v4501 + v4632; + let v4634 = v4501 - v4632; + let v4635 = v4598 * 563; + let v4636 = v4502 + v4635; + let v4637 = v4502 - v4635; + let v4638 = v4600 * 3006; + let v4639 = v4504 + v4638; + let v4640 = v4504 - v4638; + let v4641 = v4601 * 2744; + let v4642 = v4505 + v4641; + let v4643 = v4505 - v4641; + let v4644 = v4603 * 949; + let v4645 = v4507 + v4644; + let v4646 = v4507 - v4644; + let v4647 = v4604 * 2625; + let v4648 = v4508 + v4647; + let v4649 = v4508 - v4647; + let v4650 = v4606 * 4821; + let v4651 = v4510 + v4650; + let v4652 = v4510 - v4650; + let v4653 = v4607 * 2639; + let v4654 = v4511 + v4653; + let v4655 = v4511 - v4653; + let v4656 = f267 * 1479; + let v4657 = f11 + v4656; + let v4658 = f11 - v4656; + let v4659 = f395 * 1479; + let v4660 = f139 + v4659; + let v4661 = f139 - v4659; + let v4662 = v4660 * 4043; + let v4663 = v4657 + v4662; + let v4664 = v4657 - v4662; + let v4665 = v4661 * 5146; + let v4666 = v4658 + v4665; + let v4667 = v4658 - v4665; + let v4668 = f331 * 1479; + let v4669 = f75 + v4668; + let v4670 = f75 - v4668; + let v4671 = f459 * 1479; + let v4672 = f203 + v4671; + let v4673 = f203 - v4671; + let v4674 = v4672 * 4043; + let v4675 = v4669 + v4674; + let v4676 = v4669 - v4674; + let v4677 = v4673 * 5146; + let v4678 = v4670 + v4677; + let v4679 = v4670 - v4677; + let v4680 = v4675 * 5736; + let v4681 = v4663 + v4680; + let v4682 = v4663 - v4680; + let v4683 = v4676 * 4134; + let v4684 = v4664 + v4683; + let v4685 = v4664 - v4683; + let v4686 = v4678 * 722; + let v4687 = v4666 + v4686; + let v4688 = v4666 - v4686; + let v4689 = v4679 * 1305; + let v4690 = v4667 + v4689; + let v4691 = v4667 - v4689; + let v4692 = f299 * 1479; + let v4693 = f43 + v4692; + let v4694 = f43 - v4692; + let v4695 = f427 * 1479; + let v4696 = f171 + v4695; + let v4697 = f171 - v4695; + let v4698 = v4696 * 4043; + let v4699 = v4693 + v4698; + let v4700 = v4693 - v4698; + let v4701 = v4697 * 5146; + let v4702 = v4694 + v4701; + let v4703 = v4694 - v4701; + let v4704 = f363 * 1479; + let v4705 = f107 + v4704; + let v4706 = f107 - v4704; + let v4707 = f491 * 1479; + let v4708 = f235 + v4707; + let v4709 = f235 - v4707; + let v4710 = v4708 * 4043; + let v4711 = v4705 + v4710; + let v4712 = v4705 - v4710; + let v4713 = v4709 * 5146; + let v4714 = v4706 + v4713; + let v4715 = v4706 - v4713; + let v4716 = v4711 * 5736; + let v4717 = v4699 + v4716; + let v4718 = v4699 - v4716; + let v4719 = v4712 * 4134; + let v4720 = v4700 + v4719; + let v4721 = v4700 - v4719; + let v4722 = v4714 * 722; + let v4723 = v4702 + v4722; + let v4724 = v4702 - v4722; + let v4725 = v4715 * 1305; + let v4726 = v4703 + v4725; + let v4727 = v4703 - v4725; + let v4728 = v4717 * 1646; + let v4729 = v4681 + v4728; + let v4730 = v4681 - v4728; + let v4731 = v4718 * 1212; + let v4732 = v4682 + v4731; + let v4733 = v4682 - v4731; + let v4734 = v4720 * 5860; + let v4735 = v4684 + v4734; + let v4736 = v4684 - v4734; + let v4737 = v4721 * 3195; + let v4738 = v4685 + v4737; + let v4739 = v4685 - v4737; + let v4740 = v4723 * 2545; + let v4741 = v4687 + v4740; + let v4742 = v4687 - v4740; + let v4743 = v4724 * 3621; + let v4744 = v4688 + v4743; + let v4745 = v4688 - v4743; + let v4746 = v4726 * 3504; + let v4747 = v4690 + v4746; + let v4748 = v4690 - v4746; + let v4749 = v4727 * 3542; + let v4750 = v4691 + v4749; + let v4751 = v4691 - v4749; + let v4752 = f283 * 1479; + let v4753 = f27 + v4752; + let v4754 = f27 - v4752; + let v4755 = f411 * 1479; + let v4756 = f155 + v4755; + let v4757 = f155 - v4755; + let v4758 = v4756 * 4043; + let v4759 = v4753 + v4758; + let v4760 = v4753 - v4758; + let v4761 = v4757 * 5146; + let v4762 = v4754 + v4761; + let v4763 = v4754 - v4761; + let v4764 = f347 * 1479; + let v4765 = f91 + v4764; + let v4766 = f91 - v4764; + let v4767 = f475 * 1479; + let v4768 = f219 + v4767; + let v4769 = f219 - v4767; + let v4770 = v4768 * 4043; + let v4771 = v4765 + v4770; + let v4772 = v4765 - v4770; + let v4773 = v4769 * 5146; + let v4774 = v4766 + v4773; + let v4775 = v4766 - v4773; + let v4776 = v4771 * 5736; + let v4777 = v4759 + v4776; + let v4778 = v4759 - v4776; + let v4779 = v4772 * 4134; + let v4780 = v4760 + v4779; + let v4781 = v4760 - v4779; + let v4782 = v4774 * 722; + let v4783 = v4762 + v4782; + let v4784 = v4762 - v4782; + let v4785 = v4775 * 1305; + let v4786 = v4763 + v4785; + let v4787 = v4763 - v4785; + let v4788 = f315 * 1479; + let v4789 = f59 + v4788; + let v4790 = f59 - v4788; + let v4791 = f443 * 1479; + let v4792 = f187 + v4791; + let v4793 = f187 - v4791; + let v4794 = v4792 * 4043; + let v4795 = v4789 + v4794; + let v4796 = v4789 - v4794; + let v4797 = v4793 * 5146; + let v4798 = v4790 + v4797; + let v4799 = v4790 - v4797; + let v4800 = f379 * 1479; + let v4801 = f123 + v4800; + let v4802 = f123 - v4800; + let v4803 = f507 * 1479; + let v4804 = f251 + v4803; + let v4805 = f251 - v4803; + let v4806 = v4804 * 4043; + let v4807 = v4801 + v4806; + let v4808 = v4801 - v4806; + let v4809 = v4805 * 5146; + let v4810 = v4802 + v4809; + let v4811 = v4802 - v4809; + let v4812 = v4807 * 5736; + let v4813 = v4795 + v4812; + let v4814 = v4795 - v4812; + let v4815 = v4808 * 4134; + let v4816 = v4796 + v4815; + let v4817 = v4796 - v4815; + let v4818 = v4810 * 722; + let v4819 = v4798 + v4818; + let v4820 = v4798 - v4818; + let v4821 = v4811 * 1305; + let v4822 = v4799 + v4821; + let v4823 = v4799 - v4821; + let v4824 = v4813 * 1646; + let v4825 = v4777 + v4824; + let v4826 = v4777 - v4824; + let v4827 = v4814 * 1212; + let v4828 = v4778 + v4827; + let v4829 = v4778 - v4827; + let v4830 = v4816 * 5860; + let v4831 = v4780 + v4830; + let v4832 = v4780 - v4830; + let v4833 = v4817 * 3195; + let v4834 = v4781 + v4833; + let v4835 = v4781 - v4833; + let v4836 = v4819 * 2545; + let v4837 = v4783 + v4836; + let v4838 = v4783 - v4836; + let v4839 = v4820 * 3621; + let v4840 = v4784 + v4839; + let v4841 = v4784 - v4839; + let v4842 = v4822 * 3504; + let v4843 = v4786 + v4842; + let v4844 = v4786 - v4842; + let v4845 = v4823 * 3542; + let v4846 = v4787 + v4845; + let v4847 = v4787 - v4845; + let v4848 = v4825 * 4591; + let v4849 = v4729 + v4848; + let v4850 = v4729 - v4848; + let v4851 = v4826 * 5728; + let v4852 = v4730 + v4851; + let v4853 = v4730 - v4851; + let v4854 = v4828 * 5023; + let v4855 = v4732 + v4854; + let v4856 = v4732 - v4854; + let v4857 = v4829 * 5828; + let v4858 = v4733 + v4857; + let v4859 = v4733 - v4857; + let v4860 = v4831 * 4978; + let v4861 = v4735 + v4860; + let v4862 = v4735 - v4860; + let v4863 = v4832 * 1351; + let v4864 = v4736 + v4863; + let v4865 = v4736 - v4863; + let v4866 = v4834 * 3328; + let v4867 = v4738 + v4866; + let v4868 = v4738 - v4866; + let v4869 = v4835 * 5777; + let v4870 = v4739 + v4869; + let v4871 = v4739 - v4869; + let v4872 = v4837 * 2975; + let v4873 = v4741 + v4872; + let v4874 = v4741 - v4872; + let v4875 = v4838 * 563; + let v4876 = v4742 + v4875; + let v4877 = v4742 - v4875; + let v4878 = v4840 * 3006; + let v4879 = v4744 + v4878; + let v4880 = v4744 - v4878; + let v4881 = v4841 * 2744; + let v4882 = v4745 + v4881; + let v4883 = v4745 - v4881; + let v4884 = v4843 * 949; + let v4885 = v4747 + v4884; + let v4886 = v4747 - v4884; + let v4887 = v4844 * 2625; + let v4888 = v4748 + v4887; + let v4889 = v4748 - v4887; + let v4890 = v4846 * 4821; + let v4891 = v4750 + v4890; + let v4892 = v4750 - v4890; + let v4893 = v4847 * 2639; + let v4894 = v4751 + v4893; + let v4895 = v4751 - v4893; + let v4896 = v4849 * 1000; + let v4897 = v4609 + v4896; + let v4898 = v4609 - v4896; + let v4899 = v4850 * 4320; + let v4900 = v4610 + v4899; + let v4901 = v4610 - v4899; + let v4902 = v4852 * 3091; + let v4903 = v4612 + v4902; + let v4904 = v4612 - v4902; + let v4905 = v4853 * 81; + let v4906 = v4613 + v4905; + let v4907 = v4613 - v4905; + let v4908 = v4855 * 2963; + let v4909 = v4615 + v4908; + let v4910 = v4615 - v4908; + let v4911 = v4856 * 4896; + let v4912 = v4616 + v4911; + let v4913 = v4616 - v4911; + let v4914 = v4858 * 3051; + let v4915 = v4618 + v4914; + let v4916 = v4618 - v4914; + let v4917 = v4859 * 2366; + let v4918 = v4619 + v4917; + let v4919 = v4619 - v4917; + let v4920 = v4861 * 1853; + let v4921 = v4621 + v4920; + let v4922 = v4621 - v4920; + let v4923 = v4862 * 140; + let v4924 = v4622 + v4923; + let v4925 = v4622 - v4923; + let v4926 = v4864 * 4611; + let v4927 = v4624 + v4926; + let v4928 = v4624 - v4926; + let v4929 = v4865 * 726; + let v4930 = v4625 + v4929; + let v4931 = v4625 - v4929; + let v4932 = v4867 * 4255; + let v4933 = v4627 + v4932; + let v4934 = v4627 - v4932; + let v4935 = v4868 * 1177; + let v4936 = v4628 + v4935; + let v4937 = v4628 - v4935; + let v4938 = v4870 * 2768; + let v4939 = v4630 + v4938; + let v4940 = v4630 - v4938; + let v4941 = v4871 * 1635; + let v4942 = v4631 + v4941; + let v4943 = v4631 - v4941; + let v4944 = v4873 * 3712; + let v4945 = v4633 + v4944; + let v4946 = v4633 - v4944; + let v4947 = v4874 * 3135; + let v4948 = v4634 + v4947; + let v4949 = v4634 - v4947; + let v4950 = v4876 * 2747; + let v4951 = v4636 + v4950; + let v4952 = v4636 - v4950; + let v4953 = v4877 * 4846; + let v4954 = v4637 + v4953; + let v4955 = v4637 - v4953; + let v4956 = v4879 * 3553; + let v4957 = v4639 + v4956; + let v4958 = v4639 - v4956; + let v4959 = v4880 * 4805; + let v4960 = v4640 + v4959; + let v4961 = v4640 - v4959; + let v4962 = v4882 * 2294; + let v4963 = v4642 + v4962; + let v4964 = v4642 - v4962; + let v4965 = v4883 * 1062; + let v4966 = v4643 + v4965; + let v4967 = v4643 - v4965; + let v4968 = v4885 * 1326; + let v4969 = v4645 + v4968; + let v4970 = v4645 - v4968; + let v4971 = v4886 * 5086; + let v4972 = v4646 + v4971; + let v4973 = v4646 - v4971; + let v4974 = v4888 * 3014; + let v4975 = v4648 + v4974; + let v4976 = v4648 - v4974; + let v4977 = v4889 * 3201; + let v4978 = v4649 + v4977; + let v4979 = v4649 - v4977; + let v4980 = v4891 * 1170; + let v4981 = v4651 + v4980; + let v4982 = v4651 - v4980; + let v4983 = v4892 * 2319; + let v4984 = v4652 + v4983; + let v4985 = v4652 - v4983; + let v4986 = v4894 * 955; + let v4987 = v4654 + v4986; + let v4988 = v4654 - v4986; + let v4989 = v4895 * 790; + let v4990 = v4655 + v4989; + let v4991 = v4655 - v4989; + let v4992 = f263 * 1479; + let v4993 = f7 + v4992; + let v4994 = f7 - v4992; + let v4995 = f391 * 1479; + let v4996 = f135 + v4995; + let v4997 = f135 - v4995; + let v4998 = v4996 * 4043; + let v4999 = v4993 + v4998; + let v5000 = v4993 - v4998; + let v5001 = v4997 * 5146; + let v5002 = v4994 + v5001; + let v5003 = v4994 - v5001; + let v5004 = f327 * 1479; + let v5005 = f71 + v5004; + let v5006 = f71 - v5004; + let v5007 = f455 * 1479; + let v5008 = f199 + v5007; + let v5009 = f199 - v5007; + let v5010 = v5008 * 4043; + let v5011 = v5005 + v5010; + let v5012 = v5005 - v5010; + let v5013 = v5009 * 5146; + let v5014 = v5006 + v5013; + let v5015 = v5006 - v5013; + let v5016 = v5011 * 5736; + let v5017 = v4999 + v5016; + let v5018 = v4999 - v5016; + let v5019 = v5012 * 4134; + let v5020 = v5000 + v5019; + let v5021 = v5000 - v5019; + let v5022 = v5014 * 722; + let v5023 = v5002 + v5022; + let v5024 = v5002 - v5022; + let v5025 = v5015 * 1305; + let v5026 = v5003 + v5025; + let v5027 = v5003 - v5025; + let v5028 = f295 * 1479; + let v5029 = f39 + v5028; + let v5030 = f39 - v5028; + let v5031 = f423 * 1479; + let v5032 = f167 + v5031; + let v5033 = f167 - v5031; + let v5034 = v5032 * 4043; + let v5035 = v5029 + v5034; + let v5036 = v5029 - v5034; + let v5037 = v5033 * 5146; + let v5038 = v5030 + v5037; + let v5039 = v5030 - v5037; + let v5040 = f359 * 1479; + let v5041 = f103 + v5040; + let v5042 = f103 - v5040; + let v5043 = f487 * 1479; + let v5044 = f231 + v5043; + let v5045 = f231 - v5043; + let v5046 = v5044 * 4043; + let v5047 = v5041 + v5046; + let v5048 = v5041 - v5046; + let v5049 = v5045 * 5146; + let v5050 = v5042 + v5049; + let v5051 = v5042 - v5049; + let v5052 = v5047 * 5736; + let v5053 = v5035 + v5052; + let v5054 = v5035 - v5052; + let v5055 = v5048 * 4134; + let v5056 = v5036 + v5055; + let v5057 = v5036 - v5055; + let v5058 = v5050 * 722; + let v5059 = v5038 + v5058; + let v5060 = v5038 - v5058; + let v5061 = v5051 * 1305; + let v5062 = v5039 + v5061; + let v5063 = v5039 - v5061; + let v5064 = v5053 * 1646; + let v5065 = v5017 + v5064; + let v5066 = v5017 - v5064; + let v5067 = v5054 * 1212; + let v5068 = v5018 + v5067; + let v5069 = v5018 - v5067; + let v5070 = v5056 * 5860; + let v5071 = v5020 + v5070; + let v5072 = v5020 - v5070; + let v5073 = v5057 * 3195; + let v5074 = v5021 + v5073; + let v5075 = v5021 - v5073; + let v5076 = v5059 * 2545; + let v5077 = v5023 + v5076; + let v5078 = v5023 - v5076; + let v5079 = v5060 * 3621; + let v5080 = v5024 + v5079; + let v5081 = v5024 - v5079; + let v5082 = v5062 * 3504; + let v5083 = v5026 + v5082; + let v5084 = v5026 - v5082; + let v5085 = v5063 * 3542; + let v5086 = v5027 + v5085; + let v5087 = v5027 - v5085; + let v5088 = f279 * 1479; + let v5089 = f23 + v5088; + let v5090 = f23 - v5088; + let v5091 = f407 * 1479; + let v5092 = f151 + v5091; + let v5093 = f151 - v5091; + let v5094 = v5092 * 4043; + let v5095 = v5089 + v5094; + let v5096 = v5089 - v5094; + let v5097 = v5093 * 5146; + let v5098 = v5090 + v5097; + let v5099 = v5090 - v5097; + let v5100 = f343 * 1479; + let v5101 = f87 + v5100; + let v5102 = f87 - v5100; + let v5103 = f471 * 1479; + let v5104 = f215 + v5103; + let v5105 = f215 - v5103; + let v5106 = v5104 * 4043; + let v5107 = v5101 + v5106; + let v5108 = v5101 - v5106; + let v5109 = v5105 * 5146; + let v5110 = v5102 + v5109; + let v5111 = v5102 - v5109; + let v5112 = v5107 * 5736; + let v5113 = v5095 + v5112; + let v5114 = v5095 - v5112; + let v5115 = v5108 * 4134; + let v5116 = v5096 + v5115; + let v5117 = v5096 - v5115; + let v5118 = v5110 * 722; + let v5119 = v5098 + v5118; + let v5120 = v5098 - v5118; + let v5121 = v5111 * 1305; + let v5122 = v5099 + v5121; + let v5123 = v5099 - v5121; + let v5124 = f311 * 1479; + let v5125 = f55 + v5124; + let v5126 = f55 - v5124; + let v5127 = f439 * 1479; + let v5128 = f183 + v5127; + let v5129 = f183 - v5127; + let v5130 = v5128 * 4043; + let v5131 = v5125 + v5130; + let v5132 = v5125 - v5130; + let v5133 = v5129 * 5146; + let v5134 = v5126 + v5133; + let v5135 = v5126 - v5133; + let v5136 = f375 * 1479; + let v5137 = f119 + v5136; + let v5138 = f119 - v5136; + let v5139 = f503 * 1479; + let v5140 = f247 + v5139; + let v5141 = f247 - v5139; + let v5142 = v5140 * 4043; + let v5143 = v5137 + v5142; + let v5144 = v5137 - v5142; + let v5145 = v5141 * 5146; + let v5146 = v5138 + v5145; + let v5147 = v5138 - v5145; + let v5148 = v5143 * 5736; + let v5149 = v5131 + v5148; + let v5150 = v5131 - v5148; + let v5151 = v5144 * 4134; + let v5152 = v5132 + v5151; + let v5153 = v5132 - v5151; + let v5154 = v5146 * 722; + let v5155 = v5134 + v5154; + let v5156 = v5134 - v5154; + let v5157 = v5147 * 1305; + let v5158 = v5135 + v5157; + let v5159 = v5135 - v5157; + let v5160 = v5149 * 1646; + let v5161 = v5113 + v5160; + let v5162 = v5113 - v5160; + let v5163 = v5150 * 1212; + let v5164 = v5114 + v5163; + let v5165 = v5114 - v5163; + let v5166 = v5152 * 5860; + let v5167 = v5116 + v5166; + let v5168 = v5116 - v5166; + let v5169 = v5153 * 3195; + let v5170 = v5117 + v5169; + let v5171 = v5117 - v5169; + let v5172 = v5155 * 2545; + let v5173 = v5119 + v5172; + let v5174 = v5119 - v5172; + let v5175 = v5156 * 3621; + let v5176 = v5120 + v5175; + let v5177 = v5120 - v5175; + let v5178 = v5158 * 3504; + let v5179 = v5122 + v5178; + let v5180 = v5122 - v5178; + let v5181 = v5159 * 3542; + let v5182 = v5123 + v5181; + let v5183 = v5123 - v5181; + let v5184 = v5161 * 4591; + let v5185 = v5065 + v5184; + let v5186 = v5065 - v5184; + let v5187 = v5162 * 5728; + let v5188 = v5066 + v5187; + let v5189 = v5066 - v5187; + let v5190 = v5164 * 5023; + let v5191 = v5068 + v5190; + let v5192 = v5068 - v5190; + let v5193 = v5165 * 5828; + let v5194 = v5069 + v5193; + let v5195 = v5069 - v5193; + let v5196 = v5167 * 4978; + let v5197 = v5071 + v5196; + let v5198 = v5071 - v5196; + let v5199 = v5168 * 1351; + let v5200 = v5072 + v5199; + let v5201 = v5072 - v5199; + let v5202 = v5170 * 3328; + let v5203 = v5074 + v5202; + let v5204 = v5074 - v5202; + let v5205 = v5171 * 5777; + let v5206 = v5075 + v5205; + let v5207 = v5075 - v5205; + let v5208 = v5173 * 2975; + let v5209 = v5077 + v5208; + let v5210 = v5077 - v5208; + let v5211 = v5174 * 563; + let v5212 = v5078 + v5211; + let v5213 = v5078 - v5211; + let v5214 = v5176 * 3006; + let v5215 = v5080 + v5214; + let v5216 = v5080 - v5214; + let v5217 = v5177 * 2744; + let v5218 = v5081 + v5217; + let v5219 = v5081 - v5217; + let v5220 = v5179 * 949; + let v5221 = v5083 + v5220; + let v5222 = v5083 - v5220; + let v5223 = v5180 * 2625; + let v5224 = v5084 + v5223; + let v5225 = v5084 - v5223; + let v5226 = v5182 * 4821; + let v5227 = v5086 + v5226; + let v5228 = v5086 - v5226; + let v5229 = v5183 * 2639; + let v5230 = v5087 + v5229; + let v5231 = v5087 - v5229; + let v5232 = f271 * 1479; + let v5233 = f15 + v5232; + let v5234 = f15 - v5232; + let v5235 = f399 * 1479; + let v5236 = f143 + v5235; + let v5237 = f143 - v5235; + let v5238 = v5236 * 4043; + let v5239 = v5233 + v5238; + let v5240 = v5233 - v5238; + let v5241 = v5237 * 5146; + let v5242 = v5234 + v5241; + let v5243 = v5234 - v5241; + let v5244 = f335 * 1479; + let v5245 = f79 + v5244; + let v5246 = f79 - v5244; + let v5247 = f463 * 1479; + let v5248 = f207 + v5247; + let v5249 = f207 - v5247; + let v5250 = v5248 * 4043; + let v5251 = v5245 + v5250; + let v5252 = v5245 - v5250; + let v5253 = v5249 * 5146; + let v5254 = v5246 + v5253; + let v5255 = v5246 - v5253; + let v5256 = v5251 * 5736; + let v5257 = v5239 + v5256; + let v5258 = v5239 - v5256; + let v5259 = v5252 * 4134; + let v5260 = v5240 + v5259; + let v5261 = v5240 - v5259; + let v5262 = v5254 * 722; + let v5263 = v5242 + v5262; + let v5264 = v5242 - v5262; + let v5265 = v5255 * 1305; + let v5266 = v5243 + v5265; + let v5267 = v5243 - v5265; + let v5268 = f303 * 1479; + let v5269 = f47 + v5268; + let v5270 = f47 - v5268; + let v5271 = f431 * 1479; + let v5272 = f175 + v5271; + let v5273 = f175 - v5271; + let v5274 = v5272 * 4043; + let v5275 = v5269 + v5274; + let v5276 = v5269 - v5274; + let v5277 = v5273 * 5146; + let v5278 = v5270 + v5277; + let v5279 = v5270 - v5277; + let v5280 = f367 * 1479; + let v5281 = f111 + v5280; + let v5282 = f111 - v5280; + let v5283 = f495 * 1479; + let v5284 = f239 + v5283; + let v5285 = f239 - v5283; + let v5286 = v5284 * 4043; + let v5287 = v5281 + v5286; + let v5288 = v5281 - v5286; + let v5289 = v5285 * 5146; + let v5290 = v5282 + v5289; + let v5291 = v5282 - v5289; + let v5292 = v5287 * 5736; + let v5293 = v5275 + v5292; + let v5294 = v5275 - v5292; + let v5295 = v5288 * 4134; + let v5296 = v5276 + v5295; + let v5297 = v5276 - v5295; + let v5298 = v5290 * 722; + let v5299 = v5278 + v5298; + let v5300 = v5278 - v5298; + let v5301 = v5291 * 1305; + let v5302 = v5279 + v5301; + let v5303 = v5279 - v5301; + let v5304 = v5293 * 1646; + let v5305 = v5257 + v5304; + let v5306 = v5257 - v5304; + let v5307 = v5294 * 1212; + let v5308 = v5258 + v5307; + let v5309 = v5258 - v5307; + let v5310 = v5296 * 5860; + let v5311 = v5260 + v5310; + let v5312 = v5260 - v5310; + let v5313 = v5297 * 3195; + let v5314 = v5261 + v5313; + let v5315 = v5261 - v5313; + let v5316 = v5299 * 2545; + let v5317 = v5263 + v5316; + let v5318 = v5263 - v5316; + let v5319 = v5300 * 3621; + let v5320 = v5264 + v5319; + let v5321 = v5264 - v5319; + let v5322 = v5302 * 3504; + let v5323 = v5266 + v5322; + let v5324 = v5266 - v5322; + let v5325 = v5303 * 3542; + let v5326 = v5267 + v5325; + let v5327 = v5267 - v5325; + let v5328 = f287 * 1479; + let v5329 = f31 + v5328; + let v5330 = f31 - v5328; + let v5331 = f415 * 1479; + let v5332 = f159 + v5331; + let v5333 = f159 - v5331; + let v5334 = v5332 * 4043; + let v5335 = v5329 + v5334; + let v5336 = v5329 - v5334; + let v5337 = v5333 * 5146; + let v5338 = v5330 + v5337; + let v5339 = v5330 - v5337; + let v5340 = f351 * 1479; + let v5341 = f95 + v5340; + let v5342 = f95 - v5340; + let v5343 = f479 * 1479; + let v5344 = f223 + v5343; + let v5345 = f223 - v5343; + let v5346 = v5344 * 4043; + let v5347 = v5341 + v5346; + let v5348 = v5341 - v5346; + let v5349 = v5345 * 5146; + let v5350 = v5342 + v5349; + let v5351 = v5342 - v5349; + let v5352 = v5347 * 5736; + let v5353 = v5335 + v5352; + let v5354 = v5335 - v5352; + let v5355 = v5348 * 4134; + let v5356 = v5336 + v5355; + let v5357 = v5336 - v5355; + let v5358 = v5350 * 722; + let v5359 = v5338 + v5358; + let v5360 = v5338 - v5358; + let v5361 = v5351 * 1305; + let v5362 = v5339 + v5361; + let v5363 = v5339 - v5361; + let v5364 = f319 * 1479; + let v5365 = f63 + v5364; + let v5366 = f63 - v5364; + let v5367 = f447 * 1479; + let v5368 = f191 + v5367; + let v5369 = f191 - v5367; + let v5370 = v5368 * 4043; + let v5371 = v5365 + v5370; + let v5372 = v5365 - v5370; + let v5373 = v5369 * 5146; + let v5374 = v5366 + v5373; + let v5375 = v5366 - v5373; + let v5376 = f383 * 1479; + let v5377 = f127 + v5376; + let v5378 = f127 - v5376; + let v5379 = f511 * 1479; + let v5380 = f255 + v5379; + let v5381 = f255 - v5379; + let v5382 = v5380 * 4043; + let v5383 = v5377 + v5382; + let v5384 = v5377 - v5382; + let v5385 = v5381 * 5146; + let v5386 = v5378 + v5385; + let v5387 = v5378 - v5385; + let v5388 = v5383 * 5736; + let v5389 = v5371 + v5388; + let v5390 = v5371 - v5388; + let v5391 = v5384 * 4134; + let v5392 = v5372 + v5391; + let v5393 = v5372 - v5391; + let v5394 = v5386 * 722; + let v5395 = v5374 + v5394; + let v5396 = v5374 - v5394; + let v5397 = v5387 * 1305; + let v5398 = v5375 + v5397; + let v5399 = v5375 - v5397; + let v5400 = v5389 * 1646; + let v5401 = v5353 + v5400; + let v5402 = v5353 - v5400; + let v5403 = v5390 * 1212; + let v5404 = v5354 + v5403; + let v5405 = v5354 - v5403; + let v5406 = v5392 * 5860; + let v5407 = v5356 + v5406; + let v5408 = v5356 - v5406; + let v5409 = v5393 * 3195; + let v5410 = v5357 + v5409; + let v5411 = v5357 - v5409; + let v5412 = v5395 * 2545; + let v5413 = v5359 + v5412; + let v5414 = v5359 - v5412; + let v5415 = v5396 * 3621; + let v5416 = v5360 + v5415; + let v5417 = v5360 - v5415; + let v5418 = v5398 * 3504; + let v5419 = v5362 + v5418; + let v5420 = v5362 - v5418; + let v5421 = v5399 * 3542; + let v5422 = v5363 + v5421; + let v5423 = v5363 - v5421; + let v5424 = v5401 * 4591; + let v5425 = v5305 + v5424; + let v5426 = v5305 - v5424; + let v5427 = v5402 * 5728; + let v5428 = v5306 + v5427; + let v5429 = v5306 - v5427; + let v5430 = v5404 * 5023; + let v5431 = v5308 + v5430; + let v5432 = v5308 - v5430; + let v5433 = v5405 * 5828; + let v5434 = v5309 + v5433; + let v5435 = v5309 - v5433; + let v5436 = v5407 * 4978; + let v5437 = v5311 + v5436; + let v5438 = v5311 - v5436; + let v5439 = v5408 * 1351; + let v5440 = v5312 + v5439; + let v5441 = v5312 - v5439; + let v5442 = v5410 * 3328; + let v5443 = v5314 + v5442; + let v5444 = v5314 - v5442; + let v5445 = v5411 * 5777; + let v5446 = v5315 + v5445; + let v5447 = v5315 - v5445; + let v5448 = v5413 * 2975; + let v5449 = v5317 + v5448; + let v5450 = v5317 - v5448; + let v5451 = v5414 * 563; + let v5452 = v5318 + v5451; + let v5453 = v5318 - v5451; + let v5454 = v5416 * 3006; + let v5455 = v5320 + v5454; + let v5456 = v5320 - v5454; + let v5457 = v5417 * 2744; + let v5458 = v5321 + v5457; + let v5459 = v5321 - v5457; + let v5460 = v5419 * 949; + let v5461 = v5323 + v5460; + let v5462 = v5323 - v5460; + let v5463 = v5420 * 2625; + let v5464 = v5324 + v5463; + let v5465 = v5324 - v5463; + let v5466 = v5422 * 4821; + let v5467 = v5326 + v5466; + let v5468 = v5326 - v5466; + let v5469 = v5423 * 2639; + let v5470 = v5327 + v5469; + let v5471 = v5327 - v5469; + let v5472 = v5425 * 1000; + let v5473 = v5185 + v5472; + let v5474 = v5185 - v5472; + let v5475 = v5426 * 4320; + let v5476 = v5186 + v5475; + let v5477 = v5186 - v5475; + let v5478 = v5428 * 3091; + let v5479 = v5188 + v5478; + let v5480 = v5188 - v5478; + let v5481 = v5429 * 81; + let v5482 = v5189 + v5481; + let v5483 = v5189 - v5481; + let v5484 = v5431 * 2963; + let v5485 = v5191 + v5484; + let v5486 = v5191 - v5484; + let v5487 = v5432 * 4896; + let v5488 = v5192 + v5487; + let v5489 = v5192 - v5487; + let v5490 = v5434 * 3051; + let v5491 = v5194 + v5490; + let v5492 = v5194 - v5490; + let v5493 = v5435 * 2366; + let v5494 = v5195 + v5493; + let v5495 = v5195 - v5493; + let v5496 = v5437 * 1853; + let v5497 = v5197 + v5496; + let v5498 = v5197 - v5496; + let v5499 = v5438 * 140; + let v5500 = v5198 + v5499; + let v5501 = v5198 - v5499; + let v5502 = v5440 * 4611; + let v5503 = v5200 + v5502; + let v5504 = v5200 - v5502; + let v5505 = v5441 * 726; + let v5506 = v5201 + v5505; + let v5507 = v5201 - v5505; + let v5508 = v5443 * 4255; + let v5509 = v5203 + v5508; + let v5510 = v5203 - v5508; + let v5511 = v5444 * 1177; + let v5512 = v5204 + v5511; + let v5513 = v5204 - v5511; + let v5514 = v5446 * 2768; + let v5515 = v5206 + v5514; + let v5516 = v5206 - v5514; + let v5517 = v5447 * 1635; + let v5518 = v5207 + v5517; + let v5519 = v5207 - v5517; + let v5520 = v5449 * 3712; + let v5521 = v5209 + v5520; + let v5522 = v5209 - v5520; + let v5523 = v5450 * 3135; + let v5524 = v5210 + v5523; + let v5525 = v5210 - v5523; + let v5526 = v5452 * 2747; + let v5527 = v5212 + v5526; + let v5528 = v5212 - v5526; + let v5529 = v5453 * 4846; + let v5530 = v5213 + v5529; + let v5531 = v5213 - v5529; + let v5532 = v5455 * 3553; + let v5533 = v5215 + v5532; + let v5534 = v5215 - v5532; + let v5535 = v5456 * 4805; + let v5536 = v5216 + v5535; + let v5537 = v5216 - v5535; + let v5538 = v5458 * 2294; + let v5539 = v5218 + v5538; + let v5540 = v5218 - v5538; + let v5541 = v5459 * 1062; + let v5542 = v5219 + v5541; + let v5543 = v5219 - v5541; + let v5544 = v5461 * 1326; + let v5545 = v5221 + v5544; + let v5546 = v5221 - v5544; + let v5547 = v5462 * 5086; + let v5548 = v5222 + v5547; + let v5549 = v5222 - v5547; + let v5550 = v5464 * 3014; + let v5551 = v5224 + v5550; + let v5552 = v5224 - v5550; + let v5553 = v5465 * 3201; + let v5554 = v5225 + v5553; + let v5555 = v5225 - v5553; + let v5556 = v5467 * 1170; + let v5557 = v5227 + v5556; + let v5558 = v5227 - v5556; + let v5559 = v5468 * 2319; + let v5560 = v5228 + v5559; + let v5561 = v5228 - v5559; + let v5562 = v5470 * 955; + let v5563 = v5230 + v5562; + let v5564 = v5230 - v5562; + let v5565 = v5471 * 790; + let v5566 = v5231 + v5565; + let v5567 = v5231 - v5565; + let v5568 = v5473 * 544; + let v5569 = v4897 + v5568; + let v5570 = v4897 - v5568; + let v5571 = v5474 * 5791; + let v5572 = v4898 + v5571; + let v5573 = v4898 - v5571; + let v5574 = v5476 * 339; + let v5575 = v4900 + v5574; + let v5576 = v4900 - v5574; + let v5577 = v5477 * 2468; + let v5578 = v4901 + v5577; + let v5579 = v4901 - v5577; + let v5580 = v5479 * 2842; + let v5581 = v4903 + v5580; + let v5582 = v4903 - v5580; + let v5583 = v5480 * 480; + let v5584 = v4904 + v5583; + let v5585 = v4904 - v5583; + let v5586 = v5482 * 9; + let v5587 = v4906 + v5586; + let v5588 = v4906 - v5586; + let v5589 = v5483 * 1022; + let v5590 = v4907 + v5589; + let v5591 = v4907 - v5589; + let v5592 = v5485 * 4278; + let v5593 = v4909 + v5592; + let v5594 = v4909 - v5592; + let v5595 = v5486 * 1673; + let v5596 = v4910 + v5595; + let v5597 = v4910 - v5595; + let v5598 = v5488 * 4989; + let v5599 = v4912 + v5598; + let v5600 = v4912 - v5598; + let v5601 = v5489 * 5331; + let v5602 = v4913 + v5601; + let v5603 = v4913 - v5601; + let v5604 = v5491 * 3584; + let v5605 = v4915 + v5604; + let v5606 = v4915 - v5604; + let v5607 = v5492 * 4177; + let v5608 = v4916 + v5607; + let v5609 = v4916 - v5607; + let v5610 = v5494 * 1381; + let v5611 = v4918 + v5610; + let v5612 = v4918 - v5610; + let v5613 = v5495 * 2525; + let v5614 = v4919 + v5613; + let v5615 = v4919 - v5613; + let v5616 = v5497 * 2396; + let v5617 = v4921 + v5616; + let v5618 = v4921 - v5616; + let v5619 = v5498 * 4452; + let v5620 = v4922 + v5619; + let v5621 = v4922 - v5619; + let v5622 = v5500 * 3296; + let v5623 = v4924 + v5622; + let v5624 = v4924 - v5622; + let v5625 = v5501 * 3949; + let v5626 = v4925 + v5625; + let v5627 = v4925 - v5625; + let v5628 = v5503 * 130; + let v5629 = v4927 + v5628; + let v5630 = v4927 - v5628; + let v5631 = v5504 * 4354; + let v5632 = v4928 + v5631; + let v5633 = v4928 - v5631; + let v5634 = v5506 * 5374; + let v5635 = v4930 + v5634; + let v5636 = v4930 - v5634; + let v5637 = v5507 * 2837; + let v5638 = v4931 + v5637; + let v5639 = v4931 - v5637; + let v5640 = v5509 * 5767; + let v5641 = v4933 + v5640; + let v5642 = v4933 - v5640; + let v5643 = v5510 * 827; + let v5644 = v4934 + v5643; + let v5645 = v4934 - v5643; + let v5646 = v5512 * 3748; + let v5647 = v4936 + v5646; + let v5648 = v4936 - v5646; + let v5649 = v5513 * 953; + let v5650 = v4937 + v5649; + let v5651 = v4937 - v5649; + let v5652 = v5515 * 5067; + let v5653 = v4939 + v5652; + let v5654 = v4939 - v5652; + let v5655 = v5516 * 2197; + let v5656 = v4940 + v5655; + let v5657 = v4940 - v5655; + let v5658 = v5518 * 118; + let v5659 = v4942 + v5658; + let v5660 = v4942 - v5658; + let v5661 = v5519 * 2476; + let v5662 = v4943 + v5661; + let v5663 = v4943 - v5661; + let v5664 = v5521 * 2548; + let v5665 = v4945 + v5664; + let v5666 = v4945 - v5664; + let v5667 = v5522 * 4231; + let v5668 = v4946 + v5667; + let v5669 = v4946 - v5667; + let v5670 = v5524 * 355; + let v5671 = v4948 + v5670; + let v5672 = v4948 - v5670; + let v5673 = v5525 * 3382; + let v5674 = v4949 + v5673; + let v5675 = v4949 - v5673; + let v5676 = v5527 * 3707; + let v5677 = v4951 + v5676; + let v5678 = v4951 - v5676; + let v5679 = v5528 * 1759; + let v5680 = v4952 + v5679; + let v5681 = v4952 - v5679; + let v5682 = v5530 * 3694; + let v5683 = v4954 + v5682; + let v5684 = v4954 - v5682; + let v5685 = v5531 * 5179; + let v5686 = v4955 + v5685; + let v5687 = v4955 - v5685; + let v5688 = v5533 * 5542; + let v5689 = v4957 + v5688; + let v5690 = v4957 - v5688; + let v5691 = v5534 * 145; + let v5692 = v4958 + v5691; + let v5693 = v4958 - v5691; + let v5694 = v5536 * 3637; + let v5695 = v4960 + v5694; + let v5696 = v4960 - v5694; + let v5697 = v5537 * 3459; + let v5698 = v4961 + v5697; + let v5699 = v4961 - v5697; + let v5700 = v5539 * 5911; + let v5701 = v4963 + v5700; + let v5702 = v4963 - v5700; + let v5703 = v5540 * 4890; + let v5704 = v4964 + v5703; + let v5705 = v4964 - v5703; + let v5706 = v5542 * 3932; + let v5707 = v4966 + v5706; + let v5708 = v4966 - v5706; + let v5709 = v5543 * 2731; + let v5710 = v4967 + v5709; + let v5711 = v4967 - v5709; + let v5712 = v5545 * 2089; + let v5713 = v4969 + v5712; + let v5714 = v4969 - v5712; + let v5715 = v5546 * 5092; + let v5716 = v4970 + v5715; + let v5717 = v4970 - v5715; + let v5718 = v5548 * 2881; + let v5719 = v4972 + v5718; + let v5720 = v4972 - v5718; + let v5721 = v5549 * 3284; + let v5722 = v4973 + v5721; + let v5723 = v4973 - v5721; + let v5724 = v5551 * 729; + let v5725 = v4975 + v5724; + let v5726 = v4975 - v5724; + let v5727 = v5552 * 3241; + let v5728 = v4976 + v5727; + let v5729 = v4976 - v5727; + let v5730 = v5554 * 3289; + let v5731 = v4978 + v5730; + let v5732 = v4978 - v5730; + let v5733 = v5555 * 2013; + let v5734 = v4979 + v5733; + let v5735 = v4979 - v5733; + let v5736 = v5557 * 5755; + let v5737 = v4981 + v5736; + let v5738 = v4981 - v5736; + let v5739 = v5558 * 4632; + let v5740 = v4982 + v5739; + let v5741 = v4982 - v5739; + let v5742 = v5560 * 1260; + let v5743 = v4984 + v5742; + let v5744 = v4984 - v5742; + let v5745 = v5561 * 4388; + let v5746 = v4985 + v5745; + let v5747 = v4985 - v5745; + let v5748 = v5563 * 334; + let v5749 = v4987 + v5748; + let v5750 = v4987 - v5748; + let v5751 = v5564 * 2426; + let v5752 = v4988 + v5751; + let v5753 = v4988 - v5751; + let v5754 = v5566 * 1696; + let v5755 = v4990 + v5754; + let v5756 = v4990 - v5754; + let v5757 = v5567 * 1428; + let v5758 = v4991 + v5757; + let v5759 = v4991 - v5757; + let v5760 = v5569 * 1663; + let v5761 = v4225 + v5760; + let v5762 = v4225 - v5760; + let v5763 = v5570 * 1777; + let v5764 = v4226 + v5763; + let v5765 = v4226 - v5763; + let v5766 = v5572 * 1426; + let v5767 = v4228 + v5766; + let v5768 = v4228 - v5766; + let v5769 = v5573 * 4654; + let v5770 = v4229 + v5769; + let v5771 = v4229 - v5769; + let v5772 = v5575 * 5291; + let v5773 = v4231 + v5772; + let v5774 = v4231 - v5772; + let v5775 = v5576 * 2704; + let v5776 = v4232 + v5775; + let v5777 = v4232 - v5775; + let v5778 = v5578 * 4938; + let v5779 = v4234 + v5778; + let v5780 = v4234 - v5778; + let v5781 = v5579 * 3636; + let v5782 = v4235 + v5781; + let v5783 = v4235 - v5781; + let v5784 = v5581 * 3915; + let v5785 = v4237 + v5784; + let v5786 = v4237 - v5784; + let v5787 = v5582 * 2166; + let v5788 = v4238 + v5787; + let v5789 = v4238 - v5787; + let v5790 = v5584 * 113; + let v5791 = v4240 + v5790; + let v5792 = v4240 - v5790; + let v5793 = v5585 * 4919; + let v5794 = v4241 + v5793; + let v5795 = v4241 - v5793; + let v5796 = v5587 * 3; + let v5797 = v4243 + v5796; + let v5798 = v4243 - v5796; + let v5799 = v5588 * 4437; + let v5800 = v4244 + v5799; + let v5801 = v4244 - v5799; + let v5802 = v5590 * 160; + let v5803 = v4246 + v5802; + let v5804 = v4246 - v5802; + let v5805 = v5591 * 3149; + let v5806 = v4247 + v5805; + let v5807 = v4247 - v5805; + let v5808 = v5593 * 4057; + let v5809 = v4249 + v5808; + let v5810 = v4249 - v5808; + let v5811 = v5594 * 3271; + let v5812 = v4250 + v5811; + let v5813 = v4250 - v5811; + let v5814 = v5596 * 1689; + let v5815 = v4252 + v5814; + let v5816 = v4252 - v5814; + let v5817 = v5597 * 3364; + let v5818 = v4253 + v5817; + let v5819 = v4253 - v5817; + let v5820 = v5599 * 4372; + let v5821 = v4255 + v5820; + let v5822 = v4255 - v5820; + let v5823 = v5600 * 2174; + let v5824 = v4256 + v5823; + let v5825 = v4256 - v5823; + let v5826 = v5602 * 4414; + let v5827 = v4258 + v5826; + let v5828 = v4258 - v5826; + let v5829 = v5603 * 2847; + let v5830 = v4259 + v5829; + let v5831 = v4259 - v5829; + let v5832 = v5605 * 2645; + let v5833 = v4261 + v5832; + let v5834 = v4261 - v5832; + let v5835 = v5606 * 4053; + let v5836 = v4262 + v5835; + let v5837 = v4262 - v5835; + let v5838 = v5608 * 2305; + let v5839 = v4264 + v5838; + let v5840 = v4264 - v5838; + let v5841 = v5609 * 5042; + let v5842 = v4265 + v5841; + let v5843 = v4265 - v5841; + let v5844 = v5611 * 5195; + let v5845 = v4267 + v5844; + let v5846 = v4267 - v5844; + let v5847 = v5612 * 2780; + let v5848 = v4268 + v5847; + let v5849 = v4268 - v5847; + let v5850 = v5614 * 1484; + let v5851 = v4270 + v5850; + let v5852 = v4270 - v5850; + let v5853 = v5615 * 4895; + let v5854 = v4271 + v5853; + let v5855 = v4271 - v5853; + let v5856 = v5617 * 3016; + let v5857 = v4273 + v5856; + let v5858 = v4273 - v5856; + let v5859 = v5618 * 243; + let v5860 = v4274 + v5859; + let v5861 = v4274 - v5859; + let v5862 = v5620 * 3000; + let v5863 = v4276 + v5862; + let v5864 = v4276 - v5862; + let v5865 = v5621 * 671; + let v5866 = v4277 + v5865; + let v5867 = v4277 - v5865; + let v5868 = v5623 * 3136; + let v5869 = v4279 + v5868; + let v5870 = v4279 - v5868; + let v5871 = v5624 * 5191; + let v5872 = v4280 + v5871; + let v5873 = v4280 - v5871; + let v5874 = v5626 * 2399; + let v5875 = v4282 + v5874; + let v5876 = v4282 - v5874; + let v5877 = v5627 * 3400; + let v5878 = v4283 + v5877; + let v5879 = v4283 - v5877; + let v5880 = v5629 * 2178; + let v5881 = v4285 + v5880; + let v5882 = v4285 - v5880; + let v5883 = v5630 * 1544; + let v5884 = v4286 + v5883; + let v5885 = v4286 - v5883; + let v5886 = v5632 * 420; + let v5887 = v4288 + v5886; + let v5888 = v4288 - v5886; + let v5889 = v5633 * 5559; + let v5890 = v4289 + v5889; + let v5891 = v4289 - v5889; + let v5892 = v5635 * 476; + let v5893 = v4291 + v5892; + let v5894 = v4291 - v5892; + let v5895 = v5636 * 3531; + let v5896 = v4292 + v5895; + let v5897 = v4292 - v5895; + let v5898 = v5638 * 3985; + let v5899 = v4294 + v5898; + let v5900 = v4294 - v5898; + let v5901 = v5639 * 4905; + let v5902 = v4295 + v5901; + let v5903 = v4295 - v5901; + let v5904 = v5641 * 5332; + let v5905 = v4297 + v5904; + let v5906 = v4297 - v5904; + let v5907 = v5642 * 3510; + let v5908 = v4298 + v5907; + let v5909 = v4298 - v5907; + let v5910 = v5644 * 2370; + let v5911 = v4300 + v5910; + let v5912 = v4300 - v5910; + let v5913 = v5645 * 2865; + let v5914 = v4301 + v5913; + let v5915 = v4301 - v5913; + let v5916 = v5647 * 2969; + let v5917 = v4303 + v5916; + let v5918 = v4303 - v5916; + let v5919 = v5648 * 3978; + let v5920 = v4304 + v5919; + let v5921 = v4304 - v5919; + let v5922 = v5650 * 2686; + let v5923 = v4306 + v5922; + let v5924 = v4306 - v5922; + let v5925 = v5651 * 3247; + let v5926 = v4307 + v5925; + let v5927 = v4307 - v5925; + let v5928 = v5653 * 4048; + let v5929 = v4309 + v5928; + let v5930 = v4309 - v5928; + let v5931 = v5654 * 2249; + let v5932 = v4310 + v5931; + let v5933 = v4310 - v5931; + let v5934 = v5656 * 1153; + let v5935 = v4312 + v5934; + let v5936 = v4312 - v5934; + let v5937 = v5657 * 2884; + let v5938 = v4313 + v5937; + let v5939 = v4313 - v5937; + let v5940 = v5659 * 5407; + let v5941 = v4315 + v5940; + let v5942 = v4315 - v5940; + let v5943 = v5660 * 3186; + let v5944 = v4316 + v5943; + let v5945 = v4316 - v5943; + let v5946 = v5662 * 1630; + let v5947 = v4318 + v5946; + let v5948 = v4318 - v5946; + let v5949 = v5663 * 2126; + let v5950 = v4319 + v5949; + let v5951 = v4319 - v5949; + let v5952 = v5665 * 2187; + let v5953 = v4321 + v5952; + let v5954 = v4321 - v5952; + let v5955 = v5666 * 2566; + let v5956 = v4322 + v5955; + let v5957 = v4322 - v5955; + let v5958 = v5668 * 2422; + let v5959 = v4324 + v5958; + let v5960 = v4324 - v5958; + let v5961 = v5669 * 6039; + let v5962 = v4325 + v5961; + let v5963 = v4325 - v5961; + let v5964 = v5671 * 2987; + let v5965 = v4327 + v5964; + let v5966 = v4327 - v5964; + let v5967 = v5672 * 6022; + let v5968 = v4328 + v5967; + let v5969 = v4328 - v5967; + let v5970 = v5674 * 2437; + let v5971 = v4330 + v5970; + let v5972 = v4330 - v5970; + let v5973 = v5675 * 3646; + let v5974 = v4331 + v5973; + let v5975 = v4331 - v5973; + let v5976 = v5677 * 875; + let v5977 = v4333 + v5976; + let v5978 = v4333 - v5976; + let v5979 = v5678 * 3780; + let v5980 = v4334 + v5979; + let v5981 = v4334 - v5979; + let v5982 = v5680 * 1607; + let v5983 = v4336 + v5982; + let v5984 = v4336 - v5982; + let v5985 = v5681 * 4976; + let v5986 = v4337 + v5985; + let v5987 = v4337 - v5985; + let v5988 = v5683 * 5011; + let v5989 = v4339 + v5988; + let v5990 = v4339 - v5988; + let v5991 = v5684 * 1002; + let v5992 = v4340 + v5991; + let v5993 = v4340 - v5991; + let v5994 = v5686 * 4284; + let v5995 = v4342 + v5994; + let v5996 = v4342 - v5994; + let v5997 = v5687 * 5088; + let v5998 = v4343 + v5997; + let v5999 = v4343 - v5997; + let v6000 = v5689 * 3248; + let v6001 = v4345 + v6000; + let v6002 = v4345 - v6000; + let v6003 = v5690 * 1207; + let v6004 = v4346 + v6003; + let v6005 = v4346 - v6003; + let v6006 = v5692 * 1168; + let v6007 = v4348 + v6006; + let v6008 = v4348 - v6006; + let v6009 = v5693 * 5277; + let v6010 = v4349 + v6009; + let v6011 = v4349 - v6009; + let v6012 = v5695 * 1065; + let v6013 = v4351 + v6012; + let v6014 = v4351 - v6012; + let v6015 = v5696 * 2143; + let v6016 = v4352 + v6015; + let v6017 = v4352 - v6015; + let v6018 = v5698 * 404; + let v6019 = v4354 + v6018; + let v6020 = v4354 - v6018; + let v6021 = v5699 * 4645; + let v6022 = v4355 + v6021; + let v6023 = v4355 - v6021; + let v6024 = v5701 * 1912; + let v6025 = v4357 + v6024; + let v6026 = v4357 - v6024; + let v6027 = v5702 * 1378; + let v6028 = v4358 + v6027; + let v6029 = v4358 - v6027; + let v6030 = v5704 * 435; + let v6031 = v4360 + v6030; + let v6032 = v4360 - v6030; + let v6033 = v5705 * 4337; + let v6034 = v4361 + v6033; + let v6035 = v4361 - v6033; + let v6036 = v5707 * 2381; + let v6037 = v4363 + v6036; + let v6038 = v4363 - v6036; + let v6039 = v5708 * 5444; + let v6040 = v4364 + v6039; + let v6041 = v4364 - v6039; + let v6042 = v5710 * 4096; + let v6043 = v4366 + v6042; + let v6044 = v4366 - v6042; + let v6045 = v5711 * 493; + let v6046 = v4367 + v6045; + let v6047 = v4367 - v6045; + let v6048 = v5713 * 545; + let v6049 = v4369 + v6048; + let v6050 = v4369 - v6048; + let v6051 = v5714 * 5019; + let v6052 = v4370 + v6051; + let v6053 = v4370 - v6051; + let v6054 = v5716 * 3704; + let v6055 = v4372 + v6054; + let v6056 = v4372 - v6054; + let v6057 = v5717 * 2678; + let v6058 = v4373 + v6057; + let v6059 = v4373 - v6057; + let v6060 = v5719 * 1537; + let v6061 = v4375 + v6060; + let v6062 = v4375 - v6060; + let v6063 = v5720 * 242; + let v6064 = v4376 + v6063; + let v6065 = v4376 - v6063; + let v6066 = v5722 * 4714; + let v6067 = v4378 + v6066; + let v6068 = v4378 - v6066; + let v6069 = v5723 * 4143; + let v6070 = v4379 + v6069; + let v6071 = v4379 - v6069; + let v6072 = v5725 * 27; + let v6073 = v4381 + v6072; + let v6074 = v4381 - v6072; + let v6075 = v5726 * 3066; + let v6076 = v4382 + v6075; + let v6077 = v4382 - v6075; + let v6078 = v5728 * 3763; + let v6079 = v4384 + v6078; + let v6080 = v4384 - v6078; + let v6081 = v5729 * 1440; + let v6082 = v4385 + v6081; + let v6083 = v4385 - v6081; + let v6084 = v5731 * 5084; + let v6085 = v4387 + v6084; + let v6086 = v4387 - v6084; + let v6087 = v5732 * 1632; + let v6088 = v4388 + v6087; + let v6089 = v4388 - v6087; + let v6090 = v5734 * 1017; + let v6091 = v4390 + v6090; + let v6092 = v4390 - v6090; + let v6093 = v5735 * 4885; + let v6094 = v4391 + v6093; + let v6095 = v4391 - v6093; + let v6096 = v5737 * 3778; + let v6097 = v4393 + v6096; + let v6098 = v4393 - v6096; + let v6099 = v5738 * 3833; + let v6100 = v4394 + v6099; + let v6101 = v4394 - v6099; + let v6102 = v5740 * 390; + let v6103 = v4396 + v6102; + let v6104 = v4396 - v6102; + let v6105 = v5741 * 773; + let v6106 = v4397 + v6105; + let v6107 = v4397 - v6105; + let v6108 = v5743 * 2401; + let v6109 = v4399 + v6108; + let v6110 = v4399 - v6108; + let v6111 = v5744 * 442; + let v6112 = v4400 + v6111; + let v6113 = v4400 - v6111; + let v6114 = v5746 * 5101; + let v6115 = v4402 + v6114; + let v6116 = v4402 - v6114; + let v6117 = v5747 * 1067; + let v6118 = v4403 + v6117; + let v6119 = v4403 - v6117; + let v6120 = v5749 * 2912; + let v6121 = v4405 + v6120; + let v6122 = v4405 - v6120; + let v6123 = v5750 * 5698; + let v6124 = v4406 + v6123; + let v6125 = v4406 - v6123; + let v6126 = v5752 * 354; + let v6127 = v4408 + v6126; + let v6128 = v4408 - v6126; + let v6129 = v5753 * 4861; + let v6130 = v4409 + v6129; + let v6131 = v4409 - v6129; + let v6132 = v5755 * 2859; + let v6133 = v4411 + v6132; + let v6134 = v4411 - v6132; + let v6135 = v5756 * 1045; + let v6136 = v4412 + v6135; + let v6137 = v4412 - v6135; + let v6138 = v5758 * 5012; + let v6139 = v4414 + v6138; + let v6140 = v4414 - v6138; + let v6141 = v5759 * 2481; + let v6142 = v4415 + v6141; + let v6143 = v4415 - v6141; + let v6144 = v5761 * 3957; + let v6145 = v2689 + v6144; + let v6146 = v2689 - v6144; + let v6147 = v5762 * 2839; + let v6148 = v2690 + v6147; + let v6149 = v2690 - v6147; + let v6150 = v5764 * 2127; + let v6151 = v2692 + v6150; + let v6152 = v2692 - v6150; + let v6153 = v5765 * 151; + let v6154 = v2693 + v6153; + let v6155 = v2693 - v6153; + let v6156 = v5767 * 431; + let v6157 = v2695 + v6156; + let v6158 = v2695 - v6156; + let v6159 = v5768 * 1579; + let v6160 = v2696 + v6159; + let v6161 = v2696 - v6159; + let v6162 = v5770 * 5906; + let v6163 = v2698 + v6162; + let v6164 = v2698 - v6162; + let v6165 = v5771 * 2505; + let v6166 = v2699 + v6165; + let v6167 = v2699 - v6165; + let v6168 = v5773 * 1323; + let v6169 = v2701 + v6168; + let v6170 = v2701 - v6168; + let v6171 = v5774 * 2766; + let v6172 = v2702 + v6171; + let v6173 = v2702 - v6171; + let v6174 = v5776 * 52; + let v6175 = v2704 + v6174; + let v6176 = v2704 - v6174; + let v6177 = v5777 * 3174; + let v6178 = v2705 + v6177; + let v6179 = v2705 - v6177; + let v6180 = v5779 * 6055; + let v6181 = v2707 + v6180; + let v6182 = v2707 - v6180; + let v6183 = v5780 * 3336; + let v6184 = v2708 + v6183; + let v6185 = v2708 - v6183; + let v6186 = v5782 * 677; + let v6187 = v2710 + v6186; + let v6188 = v2710 - v6186; + let v6189 = v5783 * 5874; + let v6190 = v2711 + v6189; + let v6191 = v2711 - v6189; + let v6192 = v5785 * 4169; + let v6193 = v2713 + v6192; + let v6194 = v2713 - v6192; + let v6195 = v5786 * 3127; + let v6196 = v2714 + v6195; + let v6197 = v2714 - v6195; + let v6198 = v5788 * 5241; + let v6199 = v2716 + v6198; + let v6200 = v2716 - v6198; + let v6201 = v5789 * 2920; + let v6202 = v2717 + v6201; + let v6203 = v2717 - v6201; + let v6204 = v5791 * 1010; + let v6205 = v2719 + v6204; + let v6206 = v2719 - v6204; + let v6207 = v5792 * 5468; + let v6208 = v2720 + v6207; + let v6209 = v2720 - v6207; + let v6210 = v5794 * 787; + let v6211 = v2722 + v6210; + let v6212 = v2722 - v6210; + let v6213 = v5795 * 3482; + let v6214 = v2723 + v6213; + let v6215 = v2723 - v6213; + let v6216 = v5797 * 1321; + let v6217 = v2725 + v6216; + let v6218 = v2725 - v6216; + let v6219 = v5798 * 192; + let v6220 = v2726 + v6219; + let v6221 = v2726 - v6219; + let v6222 = v5800 * 4912; + let v6223 = v2728 + v6222; + let v6224 = v2728 - v6222; + let v6225 = v5801 * 2049; + let v6226 = v2729 + v6225; + let v6227 = v2729 - v6225; + let v6228 = v5803 * 4698; + let v6229 = v2731 + v6228; + let v6230 = v2731 - v6228; + let v6231 = v5804 * 5057; + let v6232 = v2732 + v6231; + let v6233 = v2732 - v6231; + let v6234 = v5806 * 4780; + let v6235 = v2734 + v6234; + let v6236 = v2734 - v6234; + let v6237 = v5807 * 3445; + let v6238 = v2735 + v6237; + let v6239 = v2735 - v6237; + let v6240 = v5809 * 1956; + let v6241 = v2737 + v6240; + let v6242 = v2737 - v6240; + let v6243 = v5810 * 5009; + let v6244 = v2738 + v6243; + let v6245 = v2738 - v6243; + let v6246 = v5812 * 6008; + let v6247 = v2740 + v6246; + let v6248 = v2740 - v6246; + let v6249 = v5813 * 885; + let v6250 = v2741 + v6249; + let v6251 = v2741 - v6249; + let v6252 = v5815 * 3532; + let v6253 = v2743 + v6252; + let v6254 = v2743 - v6252; + let v6255 = v5816 * 1003; + let v6256 = v2744 + v6255; + let v6257 = v2744 - v6255; + let v6258 = v5818 * 58; + let v6259 = v2746 + v6258; + let v6260 = v2746 - v6258; + let v6261 = v5819 * 241; + let v6262 = v2747 + v6261; + let v6263 = v2747 - v6261; + let v6264 = v5821 * 975; + let v6265 = v2749 + v6264; + let v6266 = v2749 - v6264; + let v6267 = v5822 * 4212; + let v6268 = v2750 + v6267; + let v6269 = v2750 - v6267; + let v6270 = v5824 * 2844; + let v6271 = v2752 + v6270; + let v6272 = v2752 - v6270; + let v6273 = v5825 * 3438; + let v6274 = v2753 + v6273; + let v6275 = v2753 - v6273; + let v6276 = v5827 * 1105; + let v6277 = v2755 + v6276; + let v6278 = v2755 - v6276; + let v6279 = v5828 * 142; + let v6280 = v2756 + v6279; + let v6281 = v2756 - v6279; + let v6282 = v5830 * 5681; + let v6283 = v2758 + v6282; + let v6284 = v2758 - v6282; + let v6285 = v5831 * 3477; + let v6286 = v2759 + v6285; + let v6287 = v2759 - v6285; + let v6288 = v5833 * 2302; + let v6289 = v2761 + v6288; + let v6290 = v2761 - v6288; + let v6291 = v5834 * 605; + let v6292 = v2762 + v6291; + let v6293 = v2762 - v6291; + let v6294 = v5836 * 4213; + let v6295 = v2764 + v6294; + let v6296 = v2764 - v6294; + let v6297 = v5837 * 504; + let v6298 = v2765 + v6297; + let v6299 = v2765 - v6297; + let v6300 = v5839 * 5886; + let v6301 = v2767 + v6300; + let v6302 = v2767 - v6300; + let v6303 = v5840 * 4782; + let v6304 = v2768 + v6303; + let v6305 = v2768 - v6303; + let v6306 = v5842 * 5594; + let v6307 = v2770 + v6306; + let v6308 = v2770 - v6306; + let v6309 = v5843 * 3029; + let v6310 = v2771 + v6309; + let v6311 = v2771 - v6309; + let v6312 = v5845 * 421; + let v6313 = v2773 + v6312; + let v6314 = v2773 - v6312; + let v6315 = v5846 * 4080; + let v6316 = v2774 + v6315; + let v6317 = v2774 - v6315; + let v6318 = v5848 * 6068; + let v6319 = v2776 + v6318; + let v6320 = v2776 - v6318; + let v6321 = v5849 * 3602; + let v6322 = v2777 + v6321; + let v6323 = v2777 - v6321; + let v6324 = v5851 * 6077; + let v6325 = v2779 + v6324; + let v6326 = v2779 - v6324; + let v6327 = v5852 * 4624; + let v6328 = v2780 + v6327; + let v6329 = v2780 - v6327; + let v6330 = v5854 * 3263; + let v6331 = v2782 + v6330; + let v6332 = v2782 - v6330; + let v6333 = v5855 * 3600; + let v6334 = v2783 + v6333; + let v6335 = v2783 - v6333; + let v6336 = v5857 * 4948; + let v6337 = v2785 + v6336; + let v6338 = v2785 - v6336; + let v6339 = v5858 * 6137; + let v6340 = v2786 + v6339; + let v6341 = v2786 - v6339; + let v6342 = v5860 * 400; + let v6343 = v2788 + v6342; + let v6344 = v2788 - v6342; + let v6345 = v5861 * 1728; + let v6346 = v2789 + v6345; + let v6347 = v2789 - v6345; + let v6348 = v5863 * 5862; + let v6349 = v2791 + v6348; + let v6350 = v2791 - v6348; + let v6351 = v5864 * 6136; + let v6352 = v2792 + v6351; + let v6353 = v2792 - v6351; + let v6354 = v5866 * 5415; + let v6355 = v2794 + v6354; + let v6356 = v2794 - v6354; + let v6357 = v5867 * 3643; + let v6358 = v2795 + v6357; + let v6359 = v2795 - v6357; + let v6360 = v5869 * 56; + let v6361 = v2797 + v6360; + let v6362 = v2797 - v6360; + let v6363 = v5870 * 3199; + let v6364 = v2798 + v6363; + let v6365 = v2798 - v6363; + let v6366 = v5872 * 5206; + let v6367 = v2800 + v6366; + let v6368 = v2800 - v6366; + let v6369 = v5873 * 5529; + let v6370 = v2801 + v6369; + let v6371 = v2801 - v6369; + let v6372 = v5875 * 3565; + let v6373 = v2803 + v6372; + let v6374 = v2803 - v6372; + let v6375 = v5876 * 654; + let v6376 = v2804 + v6375; + let v6377 = v2804 - v6375; + let v6378 = v5878 * 1987; + let v6379 = v2806 + v6378; + let v6380 = v2806 - v6378; + let v6381 = v5879 * 1702; + let v6382 = v2807 + v6381; + let v6383 = v2807 - v6381; + let v6384 = v5881 * 3988; + let v6385 = v2809 + v6384; + let v6386 = v2809 - v6384; + let v6387 = v5882 * 468; + let v6388 = v2810 + v6387; + let v6389 = v2810 - v6387; + let v6390 = v5884 * 316; + let v6391 = v2812 + v6390; + let v6392 = v2812 - v6390; + let v6393 = v5885 * 382; + let v6394 = v2813 + v6393; + let v6395 = v2813 - v6393; + let v6396 = v5887 * 3710; + let v6397 = v2815 + v6396; + let v6398 = v2815 - v6396; + let v6399 = v5888 * 6093; + let v6400 = v2816 + v6399; + let v6401 = v2816 - v6399; + let v6402 = v5890 * 5446; + let v6403 = v2818 + v6402; + let v6404 = v2818 - v6402; + let v6405 = v5891 * 5339; + let v6406 = v2819 + v6405; + let v6407 = v2819 - v6405; + let v6408 = v5893 * 973; + let v6409 = v2821 + v6408; + let v6410 = v2821 - v6408; + let v6411 = v5894 * 1254; + let v6412 = v2822 + v6411; + let v6413 = v2822 - v6411; + let v6414 = v5896 * 1359; + let v6415 = v2824 + v6414; + let v6416 = v2824 - v6414; + let v6417 = v5897 * 5435; + let v6418 = v2825 + v6417; + let v6419 = v2825 - v6417; + let v6420 = v5899 * 2033; + let v6421 = v2827 + v6420; + let v6422 = v2827 - v6420; + let v6423 = v5900 * 3998; + let v6424 = v2828 + v6423; + let v6425 = v2828 - v6423; + let v6426 = v5902 * 3879; + let v6427 = v2830 + v6426; + let v6428 = v2830 - v6426; + let v6429 = v5903 * 1922; + let v6430 = v2831 + v6429; + let v6431 = v2831 - v6429; + let v6432 = v5905 * 3860; + let v6433 = v2833 + v6432; + let v6434 = v2833 - v6432; + let v6435 = v5906 * 5445; + let v6436 = v2834 + v6435; + let v6437 = v2834 - v6435; + let v6438 = v5908 * 4536; + let v6439 = v2836 + v6438; + let v6440 = v2836 - v6438; + let v6441 = v5909 * 1050; + let v6442 = v2837 + v6441; + let v6443 = v2837 - v6441; + let v6444 = v5911 * 3818; + let v6445 = v2839 + v6444; + let v6446 = v2839 - v6444; + let v6447 = v5912 * 6118; + let v6448 = v2840 + v6447; + let v6449 = v2840 - v6447; + let v6450 = v5914 * 1190; + let v6451 = v2842 + v6450; + let v6452 = v2842 - v6450; + let v6453 = v5915 * 2683; + let v6454 = v2843 + v6453; + let v6455 = v2843 - v6453; + let v6456 = v5917 * 3789; + let v6457 = v2845 + v6456; + let v6458 = v2845 - v6456; + let v6459 = v5918 * 147; + let v6460 = v2846 + v6459; + let v6461 = v2846 - v6459; + let v6462 = v5920 * 5456; + let v6463 = v2848 + v6462; + let v6464 = v2848 - v6462; + let v6465 = v5921 * 4449; + let v6466 = v2849 + v6465; + let v6467 = v2849 - v6465; + let v6468 = v5923 * 4749; + let v6469 = v2851 + v6468; + let v6470 = v2851 - v6468; + let v6471 = v5924 * 5537; + let v6472 = v2852 + v6471; + let v6473 = v2852 - v6471; + let v6474 = v5926 * 4789; + let v6475 = v2854 + v6474; + let v6476 = v2854 - v6474; + let v6477 = v5927 * 4467; + let v6478 = v2855 + v6477; + let v6479 = v2855 - v6477; + let v6480 = v5929 * 1018; + let v6481 = v2857 + v6480; + let v6482 = v2857 - v6480; + let v6483 = v5930 * 5925; + let v6484 = v2858 + v6483; + let v6485 = v2858 - v6483; + let v6486 = v5932 * 1041; + let v6487 = v2860 + v6486; + let v6488 = v2860 - v6486; + let v6489 = v5933 * 3514; + let v6490 = v2861 + v6489; + let v6491 = v2861 - v6489; + let v6492 = v5935 * 2344; + let v6493 = v2863 + v6492; + let v6494 = v2863 - v6492; + let v6495 = v5936 * 1278; + let v6496 = v2864 + v6495; + let v6497 = v2864 - v6495; + let v6498 = v5938 * 5574; + let v6499 = v2866 + v6498; + let v6500 = v2866 - v6498; + let v6501 = v5939 * 1973; + let v6502 = v2867 + v6501; + let v6503 = v2867 - v6501; + let v6504 = v5941 * 4324; + let v6505 = v2869 + v6504; + let v6506 = v2869 - v6504; + let v6507 = v5942 * 4916; + let v6508 = v2870 + v6507; + let v6509 = v2870 - v6507; + let v6510 = v5944 * 4075; + let v6511 = v2872 + v6510; + let v6512 = v2872 - v6510; + let v6513 = v5945 * 5315; + let v6514 = v2873 + v6513; + let v6515 = v2873 - v6513; + let v6516 = v5947 * 5079; + let v6517 = v2875 + v6516; + let v6518 = v2875 - v6516; + let v6519 = v5948 * 3262; + let v6520 = v2876 + v6519; + let v6521 = v2876 - v6519; + let v6522 = v5950 * 522; + let v6523 = v2878 + v6522; + let v6524 = v2878 - v6522; + let v6525 = v5951 * 2169; + let v6526 = v2879 + v6525; + let v6527 = v2879 - v6525; + let v6528 = v5953 * 1200; + let v6529 = v2881 + v6528; + let v6530 = v2881 - v6528; + let v6531 = v5954 * 5184; + let v6532 = v2882 + v6531; + let v6533 = v2882 - v6531; + let v6534 = v5956 * 2555; + let v6535 = v2884 + v6534; + let v6536 = v2884 - v6534; + let v6537 = v5957 * 6122; + let v6538 = v2885 + v6537; + let v6539 = v2885 - v6537; + let v6540 = v5959 * 5297; + let v6541 = v2887 + v6540; + let v6542 = v2887 - v6540; + let v6543 = v5960 * 6119; + let v6544 = v2888 + v6543; + let v6545 = v2888 - v6543; + let v6546 = v5962 * 3956; + let v6547 = v2890 + v6546; + let v6548 = v2890 - v6546; + let v6549 = v5963 * 1360; + let v6550 = v2891 + v6549; + let v6551 = v2891 - v6549; + let v6552 = v5965 * 1962; + let v6553 = v2893 + v6552; + let v6554 = v2893 - v6552; + let v6555 = v5966 * 1594; + let v6556 = v2894 + v6555; + let v6557 = v2894 - v6555; + let v6558 = v5968 * 5961; + let v6559 = v2896 + v6558; + let v6560 = v2896 - v6558; + let v6561 = v5969 * 5106; + let v6562 = v2897 + v6561; + let v6563 = v2897 - v6561; + let v6564 = v5971 * 4298; + let v6565 = v2899 + v6564; + let v6566 = v2899 - v6564; + let v6567 = v5972 * 3329; + let v6568 = v2900 + v6567; + let v6569 = v2900 - v6567; + let v6570 = v5974 * 168; + let v6571 = v2902 + v6570; + let v6572 = v2902 - v6570; + let v6573 = v5975 * 2692; + let v6574 = v2903 + v6573; + let v6575 = v2903 - v6573; + let v6576 = v5977 * 4049; + let v6577 = v2905 + v6576; + let v6578 = v2905 - v6576; + let v6579 = v5978 * 3728; + let v6580 = v2906 + v6579; + let v6581 = v2906 - v6579; + let v6582 = v5980 * 1159; + let v6583 = v2908 + v6582; + let v6584 = v2908 - v6582; + let v6585 = v5981 * 5990; + let v6586 = v2909 + v6585; + let v6587 = v2909 - v6585; + let v6588 = v5983 * 948; + let v6589 = v2911 + v6588; + let v6590 = v2911 - v6588; + let v6591 = v5984 * 1146; + let v6592 = v2912 + v6591; + let v6593 = v2912 - v6591; + let v6594 = v5986 * 1404; + let v6595 = v2914 + v6594; + let v6596 = v2914 - v6594; + let v6597 = v5987 * 325; + let v6598 = v2915 + v6597; + let v6599 = v2915 - v6597; + let v6600 = v5989 * 5766; + let v6601 = v2917 + v6600; + let v6602 = v2917 - v6600; + let v6603 = v5990 * 652; + let v6604 = v2918 + v6603; + let v6605 = v2918 - v6603; + let v6606 = v5992 * 295; + let v6607 = v2920 + v6606; + let v6608 = v2920 - v6606; + let v6609 = v5993 * 6099; + let v6610 = v2921 + v6609; + let v6611 = v2921 - v6609; + let v6612 = v5995 * 2919; + let v6613 = v2923 + v6612; + let v6614 = v2923 - v6612; + let v6615 = v5996 * 3762; + let v6616 = v2924 + v6615; + let v6617 = v2924 - v6615; + let v6618 = v5998 * 4016; + let v6619 = v2926 + v6618; + let v6620 = v2926 - v6618; + let v6621 = v5999 * 4077; + let v6622 = v2927 + v6621; + let v6623 = v2927 - v6621; + let v6624 = v6001 * 6065; + let v6625 = v2929 + v6624; + let v6626 = v2929 - v6624; + let v6627 = v6002 * 835; + let v6628 = v2930 + v6627; + let v6629 = v2930 - v6627; + let v6630 = v6004 * 3570; + let v6631 = v2932 + v6630; + let v6632 = v2932 - v6630; + let v6633 = v6005 * 4240; + let v6634 = v2933 + v6633; + let v6635 = v2933 - v6633; + let v6636 = v6007 * 4046; + let v6637 = v2935 + v6636; + let v6638 = v2935 - v6636; + let v6639 = v6008 * 709; + let v6640 = v2936 + v6639; + let v6641 = v2936 - v6639; + let v6642 = v6010 * 3150; + let v6643 = v2938 + v6642; + let v6644 = v2938 - v6642; + let v6645 = v6011 * 1319; + let v6646 = v2939 + v6645; + let v6647 = v2939 - v6645; + let v6648 = v6013 * 1058; + let v6649 = v2941 + v6648; + let v6650 = v2941 - v6648; + let v6651 = v6014 * 4079; + let v6652 = v2942 + v6651; + let v6653 = v2942 - v6651; + let v6654 = v6016 * 922; + let v6655 = v2944 + v6654; + let v6656 = v2944 - v6654; + let v6657 = v6017 * 441; + let v6658 = v2945 + v6657; + let v6659 = v2945 - v6657; + let v6660 = v6019 * 4322; + let v6661 = v2947 + v6660; + let v6662 = v2947 - v6660; + let v6663 = v6020 * 1958; + let v6664 = v2948 + v6663; + let v6665 = v2948 - v6663; + let v6666 = v6022 * 2078; + let v6667 = v2950 + v6666; + let v6668 = v2950 - v6666; + let v6669 = v6023 * 1112; + let v6670 = v2951 + v6669; + let v6671 = v2951 - v6669; + let v6672 = v6025 * 3834; + let v6673 = v2953 + v6672; + let v6674 = v2953 - v6672; + let v6675 = v6026 * 5257; + let v6676 = v2954 + v6675; + let v6677 = v2954 - v6675; + let v6678 = v6028 * 4433; + let v6679 = v2956 + v6678; + let v6680 = v2956 - v6678; + let v6681 = v6029 * 5919; + let v6682 = v2957 + v6681; + let v6683 = v2957 - v6681; + let v6684 = v6031 * 5486; + let v6685 = v2959 + v6684; + let v6686 = v2959 - v6684; + let v6687 = v6032 * 3054; + let v6688 = v2960 + v6687; + let v6689 = v2960 - v6687; + let v6690 = v6034 * 1747; + let v6691 = v2962 + v6690; + let v6692 = v2962 - v6690; + let v6693 = v6035 * 3123; + let v6694 = v2963 + v6693; + let v6695 = v2963 - v6693; + let v6696 = v6037 * 2948; + let v6697 = v2965 + v6696; + let v6698 = v2965 - v6696; + let v6699 = v6038 * 2503; + let v6700 = v2966 + v6699; + let v6701 = v2966 - v6699; + let v6702 = v6040 * 5782; + let v6703 = v2968 + v6702; + let v6704 = v2968 - v6702; + let v6705 = v6041 * 1566; + let v6706 = v2969 + v6705; + let v6707 = v2969 - v6705; + let v6708 = v6043 * 64; + let v6709 = v2971 + v6708; + let v6710 = v2971 - v6708; + let v6711 = v6044 * 3656; + let v6712 = v2972 + v6711; + let v6713 = v2972 - v6711; + let v6714 = v6046 * 2459; + let v6715 = v2974 + v6714; + let v6716 = v2974 - v6714; + let v6717 = v6047 * 683; + let v6718 = v2975 + v6717; + let v6719 = v2975 - v6717; + let v6720 = v6049 * 1293; + let v6721 = v2977 + v6720; + let v6722 = v2977 - v6720; + let v6723 = v6050 * 4737; + let v6724 = v2978 + v6723; + let v6725 = v2978 - v6723; + let v6726 = v6052 * 5429; + let v6727 = v2980 + v6726; + let v6728 = v2980 - v6726; + let v6729 = v6053 * 4774; + let v6730 = v2981 + v6729; + let v6731 = v2981 - v6729; + let v6732 = v6055 * 5908; + let v6733 = v2983 + v6732; + let v6734 = v2983 - v6732; + let v6735 = v6056 * 453; + let v6736 = v2984 + v6735; + let v6737 = v2984 - v6735; + let v6738 = v6058 * 418; + let v6739 = v2986 + v6738; + let v6740 = v2986 - v6738; + let v6741 = v6059 * 3772; + let v6742 = v2987 + v6741; + let v6743 = v2987 - v6741; + let v6744 = v6061 * 3991; + let v6745 = v2989 + v6744; + let v6746 = v2989 - v6744; + let v6747 = v6062 * 3969; + let v6748 = v2990 + v6747; + let v6749 = v2990 - v6747; + let v6750 = v6064 * 2767; + let v6751 = v2992 + v6750; + let v6752 = v2992 - v6750; + let v6753 = v6065 * 156; + let v6754 = v2993 + v6753; + let v6755 = v2993 - v6753; + let v6756 = v6067 * 2281; + let v6757 = v2995 + v6756; + let v6758 = v2995 - v6756; + let v6759 = v6068 * 5876; + let v6760 = v2996 + v6759; + let v6761 = v2996 - v6759; + let v6762 = v6070 * 5333; + let v6763 = v2998 + v6762; + let v6764 = v2998 - v6762; + let v6765 = v6071 * 2031; + let v6766 = v2999 + v6765; + let v6767 = v2999 - v6765; + let v6768 = v6073 * 3963; + let v6769 = v3001 + v6768; + let v6770 = v3001 - v6768; + let v6771 = v6074 * 576; + let v6772 = v3002 + v6771; + let v6773 = v3002 - v6771; + let v6774 = v6076 * 2447; + let v6775 = v3004 + v6774; + let v6776 = v3004 - v6774; + let v6777 = v6077 * 6142; + let v6778 = v3005 + v6777; + let v6779 = v3005 - v6777; + let v6780 = v6079 * 2051; + let v6781 = v3007 + v6780; + let v6782 = v3007 - v6780; + let v6783 = v6080 * 1954; + let v6784 = v3008 + v6783; + let v6785 = v3008 - v6783; + let v6786 = v6082 * 1805; + let v6787 = v3010 + v6786; + let v6788 = v3010 - v6786; + let v6789 = v6083 * 2882; + let v6790 = v3011 + v6789; + let v6791 = v3011 - v6789; + let v6792 = v6085 * 3529; + let v6793 = v3013 + v6792; + let v6794 = v3013 - v6792; + let v6795 = v6086 * 3434; + let v6796 = v3014 + v6795; + let v6797 = v3014 - v6795; + let v6798 = v6088 * 2908; + let v6799 = v3016 + v6798; + let v6800 = v3016 - v6798; + let v6801 = v6089 * 218; + let v6802 = v3017 + v6801; + let v6803 = v3017 - v6801; + let v6804 = v6091 * 3030; + let v6805 = v3019 + v6804; + let v6806 = v3019 - v6804; + let v6807 = v6092 * 4115; + let v6808 = v3020 + v6807; + let v6809 = v3020 - v6807; + let v6810 = v6094 * 1843; + let v6811 = v3022 + v6810; + let v6812 = v3022 - v6810; + let v6813 = v6095 * 2361; + let v6814 = v3023 + v6813; + let v6815 = v3023 - v6813; + let v6816 = v6097 * 3202; + let v6817 = v3025 + v6816; + let v6818 = v3025 - v6816; + let v6819 = v6098 * 4493; + let v6820 = v3026 + v6819; + let v6821 = v3026 - v6819; + let v6822 = v6100 * 2057; + let v6823 = v3028 + v6822; + let v6824 = v3028 - v6822; + let v6825 = v6101 * 5369; + let v6826 = v3029 + v6825; + let v6827 = v3029 - v6825; + let v6828 = v6103 * 1512; + let v6829 = v3031 + v6828; + let v6830 = v3031 - v6828; + let v6831 = v6104 * 350; + let v6832 = v3032 + v6831; + let v6833 = v3032 - v6831; + let v6834 = v6106 * 1815; + let v6835 = v3034 + v6834; + let v6836 = v3034 - v6834; + let v6837 = v6107 * 5383; + let v6838 = v3035 + v6837; + let v6839 = v3035 - v6837; + let v6840 = v6109 * 49; + let v6841 = v3037 + v6840; + let v6842 = v3037 - v6840; + let v6843 = v6110 * 1263; + let v6844 = v3038 + v6843; + let v6845 = v3038 - v6843; + let v6846 = v6112 * 5915; + let v6847 = v3040 + v6846; + let v6848 = v3040 - v6846; + let v6849 = v6113 * 1483; + let v6850 = v3041 + v6849; + let v6851 = v3041 - v6849; + let v6852 = v6115 * 1489; + let v6853 = v3043 + v6852; + let v6854 = v3043 - v6852; + let v6855 = v6116 * 2500; + let v6856 = v3044 + v6855; + let v6857 = v3044 - v6855; + let v6858 = v6118 * 5942; + let v6859 = v3046 + v6858; + let v6860 = v3046 - v6858; + let v6861 = v6119 * 1583; + let v6862 = v3047 + v6861; + let v6863 = v3047 - v6861; + let v6864 = v6121 * 1693; + let v6865 = v3049 + v6864; + let v6866 = v3049 - v6864; + let v6867 = v6122 * 3009; + let v6868 = v3050 + v6867; + let v6869 = v3050 - v6867; + let v6870 = v6124 * 174; + let v6871 = v3052 + v6870; + let v6872 = v3052 - v6870; + let v6873 = v6125 * 723; + let v6874 = v3053 + v6873; + let v6875 = v3053 - v6873; + let v6876 = v6127 * 2738; + let v6877 = v3055 + v6876; + let v6878 = v3055 - v6876; + let v6879 = v6128 * 5868; + let v6880 = v3056 + v6879; + let v6881 = v3056 - v6879; + let v6882 = v6130 * 5735; + let v6883 = v3058 + v6882; + let v6884 = v3058 - v6882; + let v6885 = v6131 * 2655; + let v6886 = v3059 + v6885; + let v6887 = v3059 - v6885; + let v6888 = v6133 * 3315; + let v6889 = v3061 + v6888; + let v6890 = v3061 - v6888; + let v6891 = v6134 * 426; + let v6892 = v3062 + v6891; + let v6893 = v3062 - v6891; + let v6894 = v6136 * 4754; + let v6895 = v3064 + v6894; + let v6896 = v3064 - v6894; + let v6897 = v6137 * 1858; + let v6898 = v3065 + v6897; + let v6899 = v3065 - v6897; + let v6900 = v6139 * 1975; + let v6901 = v3067 + v6900; + let v6902 = v3067 - v6900; + let v6903 = v6140 * 3757; + let v6904 = v3068 + v6903; + let v6905 = v3068 - v6903; + let v6906 = v6142 * 2925; + let v6907 = v3070 + v6906; + let v6908 = v3070 - v6906; + let v6909 = v6143 * 347; + let v6910 = v3071 + v6909; + let v6911 = v3071 - v6909; + let r0_bounded: U128AsBounded = upcast(felt252_as_u128(v6145 + SHIFT)); + let (_, r0) = bounded_int_div_rem(r0_bounded, FALCON512_Q_NZ); + let r1_bounded: U128AsBounded = upcast(felt252_as_u128(v6146 + SHIFT)); + let (_, r1) = bounded_int_div_rem(r1_bounded, FALCON512_Q_NZ); + let r2_bounded: U128AsBounded = upcast(felt252_as_u128(v6148 + SHIFT)); + let (_, r2) = bounded_int_div_rem(r2_bounded, FALCON512_Q_NZ); + let r3_bounded: U128AsBounded = upcast(felt252_as_u128(v6149 + SHIFT)); + let (_, r3) = bounded_int_div_rem(r3_bounded, FALCON512_Q_NZ); + let r4_bounded: U128AsBounded = upcast(felt252_as_u128(v6151 + SHIFT)); + let (_, r4) = bounded_int_div_rem(r4_bounded, FALCON512_Q_NZ); + let r5_bounded: U128AsBounded = upcast(felt252_as_u128(v6152 + SHIFT)); + let (_, r5) = bounded_int_div_rem(r5_bounded, FALCON512_Q_NZ); + let r6_bounded: U128AsBounded = upcast(felt252_as_u128(v6154 + SHIFT)); + let (_, r6) = bounded_int_div_rem(r6_bounded, FALCON512_Q_NZ); + let r7_bounded: U128AsBounded = upcast(felt252_as_u128(v6155 + SHIFT)); + let (_, r7) = bounded_int_div_rem(r7_bounded, FALCON512_Q_NZ); + let r8_bounded: U128AsBounded = upcast(felt252_as_u128(v6157 + SHIFT)); + let (_, r8) = bounded_int_div_rem(r8_bounded, FALCON512_Q_NZ); + let r9_bounded: U128AsBounded = upcast(felt252_as_u128(v6158 + SHIFT)); + let (_, r9) = bounded_int_div_rem(r9_bounded, FALCON512_Q_NZ); + let r10_bounded: U128AsBounded = upcast(felt252_as_u128(v6160 + SHIFT)); + let (_, r10) = bounded_int_div_rem(r10_bounded, FALCON512_Q_NZ); + let r11_bounded: U128AsBounded = upcast(felt252_as_u128(v6161 + SHIFT)); + let (_, r11) = bounded_int_div_rem(r11_bounded, FALCON512_Q_NZ); + let r12_bounded: U128AsBounded = upcast(felt252_as_u128(v6163 + SHIFT)); + let (_, r12) = bounded_int_div_rem(r12_bounded, FALCON512_Q_NZ); + let r13_bounded: U128AsBounded = upcast(felt252_as_u128(v6164 + SHIFT)); + let (_, r13) = bounded_int_div_rem(r13_bounded, FALCON512_Q_NZ); + let r14_bounded: U128AsBounded = upcast(felt252_as_u128(v6166 + SHIFT)); + let (_, r14) = bounded_int_div_rem(r14_bounded, FALCON512_Q_NZ); + let r15_bounded: U128AsBounded = upcast(felt252_as_u128(v6167 + SHIFT)); + let (_, r15) = bounded_int_div_rem(r15_bounded, FALCON512_Q_NZ); + let r16_bounded: U128AsBounded = upcast(felt252_as_u128(v6169 + SHIFT)); + let (_, r16) = bounded_int_div_rem(r16_bounded, FALCON512_Q_NZ); + let r17_bounded: U128AsBounded = upcast(felt252_as_u128(v6170 + SHIFT)); + let (_, r17) = bounded_int_div_rem(r17_bounded, FALCON512_Q_NZ); + let r18_bounded: U128AsBounded = upcast(felt252_as_u128(v6172 + SHIFT)); + let (_, r18) = bounded_int_div_rem(r18_bounded, FALCON512_Q_NZ); + let r19_bounded: U128AsBounded = upcast(felt252_as_u128(v6173 + SHIFT)); + let (_, r19) = bounded_int_div_rem(r19_bounded, FALCON512_Q_NZ); + let r20_bounded: U128AsBounded = upcast(felt252_as_u128(v6175 + SHIFT)); + let (_, r20) = bounded_int_div_rem(r20_bounded, FALCON512_Q_NZ); + let r21_bounded: U128AsBounded = upcast(felt252_as_u128(v6176 + SHIFT)); + let (_, r21) = bounded_int_div_rem(r21_bounded, FALCON512_Q_NZ); + let r22_bounded: U128AsBounded = upcast(felt252_as_u128(v6178 + SHIFT)); + let (_, r22) = bounded_int_div_rem(r22_bounded, FALCON512_Q_NZ); + let r23_bounded: U128AsBounded = upcast(felt252_as_u128(v6179 + SHIFT)); + let (_, r23) = bounded_int_div_rem(r23_bounded, FALCON512_Q_NZ); + let r24_bounded: U128AsBounded = upcast(felt252_as_u128(v6181 + SHIFT)); + let (_, r24) = bounded_int_div_rem(r24_bounded, FALCON512_Q_NZ); + let r25_bounded: U128AsBounded = upcast(felt252_as_u128(v6182 + SHIFT)); + let (_, r25) = bounded_int_div_rem(r25_bounded, FALCON512_Q_NZ); + let r26_bounded: U128AsBounded = upcast(felt252_as_u128(v6184 + SHIFT)); + let (_, r26) = bounded_int_div_rem(r26_bounded, FALCON512_Q_NZ); + let r27_bounded: U128AsBounded = upcast(felt252_as_u128(v6185 + SHIFT)); + let (_, r27) = bounded_int_div_rem(r27_bounded, FALCON512_Q_NZ); + let r28_bounded: U128AsBounded = upcast(felt252_as_u128(v6187 + SHIFT)); + let (_, r28) = bounded_int_div_rem(r28_bounded, FALCON512_Q_NZ); + let r29_bounded: U128AsBounded = upcast(felt252_as_u128(v6188 + SHIFT)); + let (_, r29) = bounded_int_div_rem(r29_bounded, FALCON512_Q_NZ); + let r30_bounded: U128AsBounded = upcast(felt252_as_u128(v6190 + SHIFT)); + let (_, r30) = bounded_int_div_rem(r30_bounded, FALCON512_Q_NZ); + let r31_bounded: U128AsBounded = upcast(felt252_as_u128(v6191 + SHIFT)); + let (_, r31) = bounded_int_div_rem(r31_bounded, FALCON512_Q_NZ); + let r32_bounded: U128AsBounded = upcast(felt252_as_u128(v6193 + SHIFT)); + let (_, r32) = bounded_int_div_rem(r32_bounded, FALCON512_Q_NZ); + let r33_bounded: U128AsBounded = upcast(felt252_as_u128(v6194 + SHIFT)); + let (_, r33) = bounded_int_div_rem(r33_bounded, FALCON512_Q_NZ); + let r34_bounded: U128AsBounded = upcast(felt252_as_u128(v6196 + SHIFT)); + let (_, r34) = bounded_int_div_rem(r34_bounded, FALCON512_Q_NZ); + let r35_bounded: U128AsBounded = upcast(felt252_as_u128(v6197 + SHIFT)); + let (_, r35) = bounded_int_div_rem(r35_bounded, FALCON512_Q_NZ); + let r36_bounded: U128AsBounded = upcast(felt252_as_u128(v6199 + SHIFT)); + let (_, r36) = bounded_int_div_rem(r36_bounded, FALCON512_Q_NZ); + let r37_bounded: U128AsBounded = upcast(felt252_as_u128(v6200 + SHIFT)); + let (_, r37) = bounded_int_div_rem(r37_bounded, FALCON512_Q_NZ); + let r38_bounded: U128AsBounded = upcast(felt252_as_u128(v6202 + SHIFT)); + let (_, r38) = bounded_int_div_rem(r38_bounded, FALCON512_Q_NZ); + let r39_bounded: U128AsBounded = upcast(felt252_as_u128(v6203 + SHIFT)); + let (_, r39) = bounded_int_div_rem(r39_bounded, FALCON512_Q_NZ); + let r40_bounded: U128AsBounded = upcast(felt252_as_u128(v6205 + SHIFT)); + let (_, r40) = bounded_int_div_rem(r40_bounded, FALCON512_Q_NZ); + let r41_bounded: U128AsBounded = upcast(felt252_as_u128(v6206 + SHIFT)); + let (_, r41) = bounded_int_div_rem(r41_bounded, FALCON512_Q_NZ); + let r42_bounded: U128AsBounded = upcast(felt252_as_u128(v6208 + SHIFT)); + let (_, r42) = bounded_int_div_rem(r42_bounded, FALCON512_Q_NZ); + let r43_bounded: U128AsBounded = upcast(felt252_as_u128(v6209 + SHIFT)); + let (_, r43) = bounded_int_div_rem(r43_bounded, FALCON512_Q_NZ); + let r44_bounded: U128AsBounded = upcast(felt252_as_u128(v6211 + SHIFT)); + let (_, r44) = bounded_int_div_rem(r44_bounded, FALCON512_Q_NZ); + let r45_bounded: U128AsBounded = upcast(felt252_as_u128(v6212 + SHIFT)); + let (_, r45) = bounded_int_div_rem(r45_bounded, FALCON512_Q_NZ); + let r46_bounded: U128AsBounded = upcast(felt252_as_u128(v6214 + SHIFT)); + let (_, r46) = bounded_int_div_rem(r46_bounded, FALCON512_Q_NZ); + let r47_bounded: U128AsBounded = upcast(felt252_as_u128(v6215 + SHIFT)); + let (_, r47) = bounded_int_div_rem(r47_bounded, FALCON512_Q_NZ); + let r48_bounded: U128AsBounded = upcast(felt252_as_u128(v6217 + SHIFT)); + let (_, r48) = bounded_int_div_rem(r48_bounded, FALCON512_Q_NZ); + let r49_bounded: U128AsBounded = upcast(felt252_as_u128(v6218 + SHIFT)); + let (_, r49) = bounded_int_div_rem(r49_bounded, FALCON512_Q_NZ); + let r50_bounded: U128AsBounded = upcast(felt252_as_u128(v6220 + SHIFT)); + let (_, r50) = bounded_int_div_rem(r50_bounded, FALCON512_Q_NZ); + let r51_bounded: U128AsBounded = upcast(felt252_as_u128(v6221 + SHIFT)); + let (_, r51) = bounded_int_div_rem(r51_bounded, FALCON512_Q_NZ); + let r52_bounded: U128AsBounded = upcast(felt252_as_u128(v6223 + SHIFT)); + let (_, r52) = bounded_int_div_rem(r52_bounded, FALCON512_Q_NZ); + let r53_bounded: U128AsBounded = upcast(felt252_as_u128(v6224 + SHIFT)); + let (_, r53) = bounded_int_div_rem(r53_bounded, FALCON512_Q_NZ); + let r54_bounded: U128AsBounded = upcast(felt252_as_u128(v6226 + SHIFT)); + let (_, r54) = bounded_int_div_rem(r54_bounded, FALCON512_Q_NZ); + let r55_bounded: U128AsBounded = upcast(felt252_as_u128(v6227 + SHIFT)); + let (_, r55) = bounded_int_div_rem(r55_bounded, FALCON512_Q_NZ); + let r56_bounded: U128AsBounded = upcast(felt252_as_u128(v6229 + SHIFT)); + let (_, r56) = bounded_int_div_rem(r56_bounded, FALCON512_Q_NZ); + let r57_bounded: U128AsBounded = upcast(felt252_as_u128(v6230 + SHIFT)); + let (_, r57) = bounded_int_div_rem(r57_bounded, FALCON512_Q_NZ); + let r58_bounded: U128AsBounded = upcast(felt252_as_u128(v6232 + SHIFT)); + let (_, r58) = bounded_int_div_rem(r58_bounded, FALCON512_Q_NZ); + let r59_bounded: U128AsBounded = upcast(felt252_as_u128(v6233 + SHIFT)); + let (_, r59) = bounded_int_div_rem(r59_bounded, FALCON512_Q_NZ); + let r60_bounded: U128AsBounded = upcast(felt252_as_u128(v6235 + SHIFT)); + let (_, r60) = bounded_int_div_rem(r60_bounded, FALCON512_Q_NZ); + let r61_bounded: U128AsBounded = upcast(felt252_as_u128(v6236 + SHIFT)); + let (_, r61) = bounded_int_div_rem(r61_bounded, FALCON512_Q_NZ); + let r62_bounded: U128AsBounded = upcast(felt252_as_u128(v6238 + SHIFT)); + let (_, r62) = bounded_int_div_rem(r62_bounded, FALCON512_Q_NZ); + let r63_bounded: U128AsBounded = upcast(felt252_as_u128(v6239 + SHIFT)); + let (_, r63) = bounded_int_div_rem(r63_bounded, FALCON512_Q_NZ); + let r64_bounded: U128AsBounded = upcast(felt252_as_u128(v6241 + SHIFT)); + let (_, r64) = bounded_int_div_rem(r64_bounded, FALCON512_Q_NZ); + let r65_bounded: U128AsBounded = upcast(felt252_as_u128(v6242 + SHIFT)); + let (_, r65) = bounded_int_div_rem(r65_bounded, FALCON512_Q_NZ); + let r66_bounded: U128AsBounded = upcast(felt252_as_u128(v6244 + SHIFT)); + let (_, r66) = bounded_int_div_rem(r66_bounded, FALCON512_Q_NZ); + let r67_bounded: U128AsBounded = upcast(felt252_as_u128(v6245 + SHIFT)); + let (_, r67) = bounded_int_div_rem(r67_bounded, FALCON512_Q_NZ); + let r68_bounded: U128AsBounded = upcast(felt252_as_u128(v6247 + SHIFT)); + let (_, r68) = bounded_int_div_rem(r68_bounded, FALCON512_Q_NZ); + let r69_bounded: U128AsBounded = upcast(felt252_as_u128(v6248 + SHIFT)); + let (_, r69) = bounded_int_div_rem(r69_bounded, FALCON512_Q_NZ); + let r70_bounded: U128AsBounded = upcast(felt252_as_u128(v6250 + SHIFT)); + let (_, r70) = bounded_int_div_rem(r70_bounded, FALCON512_Q_NZ); + let r71_bounded: U128AsBounded = upcast(felt252_as_u128(v6251 + SHIFT)); + let (_, r71) = bounded_int_div_rem(r71_bounded, FALCON512_Q_NZ); + let r72_bounded: U128AsBounded = upcast(felt252_as_u128(v6253 + SHIFT)); + let (_, r72) = bounded_int_div_rem(r72_bounded, FALCON512_Q_NZ); + let r73_bounded: U128AsBounded = upcast(felt252_as_u128(v6254 + SHIFT)); + let (_, r73) = bounded_int_div_rem(r73_bounded, FALCON512_Q_NZ); + let r74_bounded: U128AsBounded = upcast(felt252_as_u128(v6256 + SHIFT)); + let (_, r74) = bounded_int_div_rem(r74_bounded, FALCON512_Q_NZ); + let r75_bounded: U128AsBounded = upcast(felt252_as_u128(v6257 + SHIFT)); + let (_, r75) = bounded_int_div_rem(r75_bounded, FALCON512_Q_NZ); + let r76_bounded: U128AsBounded = upcast(felt252_as_u128(v6259 + SHIFT)); + let (_, r76) = bounded_int_div_rem(r76_bounded, FALCON512_Q_NZ); + let r77_bounded: U128AsBounded = upcast(felt252_as_u128(v6260 + SHIFT)); + let (_, r77) = bounded_int_div_rem(r77_bounded, FALCON512_Q_NZ); + let r78_bounded: U128AsBounded = upcast(felt252_as_u128(v6262 + SHIFT)); + let (_, r78) = bounded_int_div_rem(r78_bounded, FALCON512_Q_NZ); + let r79_bounded: U128AsBounded = upcast(felt252_as_u128(v6263 + SHIFT)); + let (_, r79) = bounded_int_div_rem(r79_bounded, FALCON512_Q_NZ); + let r80_bounded: U128AsBounded = upcast(felt252_as_u128(v6265 + SHIFT)); + let (_, r80) = bounded_int_div_rem(r80_bounded, FALCON512_Q_NZ); + let r81_bounded: U128AsBounded = upcast(felt252_as_u128(v6266 + SHIFT)); + let (_, r81) = bounded_int_div_rem(r81_bounded, FALCON512_Q_NZ); + let r82_bounded: U128AsBounded = upcast(felt252_as_u128(v6268 + SHIFT)); + let (_, r82) = bounded_int_div_rem(r82_bounded, FALCON512_Q_NZ); + let r83_bounded: U128AsBounded = upcast(felt252_as_u128(v6269 + SHIFT)); + let (_, r83) = bounded_int_div_rem(r83_bounded, FALCON512_Q_NZ); + let r84_bounded: U128AsBounded = upcast(felt252_as_u128(v6271 + SHIFT)); + let (_, r84) = bounded_int_div_rem(r84_bounded, FALCON512_Q_NZ); + let r85_bounded: U128AsBounded = upcast(felt252_as_u128(v6272 + SHIFT)); + let (_, r85) = bounded_int_div_rem(r85_bounded, FALCON512_Q_NZ); + let r86_bounded: U128AsBounded = upcast(felt252_as_u128(v6274 + SHIFT)); + let (_, r86) = bounded_int_div_rem(r86_bounded, FALCON512_Q_NZ); + let r87_bounded: U128AsBounded = upcast(felt252_as_u128(v6275 + SHIFT)); + let (_, r87) = bounded_int_div_rem(r87_bounded, FALCON512_Q_NZ); + let r88_bounded: U128AsBounded = upcast(felt252_as_u128(v6277 + SHIFT)); + let (_, r88) = bounded_int_div_rem(r88_bounded, FALCON512_Q_NZ); + let r89_bounded: U128AsBounded = upcast(felt252_as_u128(v6278 + SHIFT)); + let (_, r89) = bounded_int_div_rem(r89_bounded, FALCON512_Q_NZ); + let r90_bounded: U128AsBounded = upcast(felt252_as_u128(v6280 + SHIFT)); + let (_, r90) = bounded_int_div_rem(r90_bounded, FALCON512_Q_NZ); + let r91_bounded: U128AsBounded = upcast(felt252_as_u128(v6281 + SHIFT)); + let (_, r91) = bounded_int_div_rem(r91_bounded, FALCON512_Q_NZ); + let r92_bounded: U128AsBounded = upcast(felt252_as_u128(v6283 + SHIFT)); + let (_, r92) = bounded_int_div_rem(r92_bounded, FALCON512_Q_NZ); + let r93_bounded: U128AsBounded = upcast(felt252_as_u128(v6284 + SHIFT)); + let (_, r93) = bounded_int_div_rem(r93_bounded, FALCON512_Q_NZ); + let r94_bounded: U128AsBounded = upcast(felt252_as_u128(v6286 + SHIFT)); + let (_, r94) = bounded_int_div_rem(r94_bounded, FALCON512_Q_NZ); + let r95_bounded: U128AsBounded = upcast(felt252_as_u128(v6287 + SHIFT)); + let (_, r95) = bounded_int_div_rem(r95_bounded, FALCON512_Q_NZ); + let r96_bounded: U128AsBounded = upcast(felt252_as_u128(v6289 + SHIFT)); + let (_, r96) = bounded_int_div_rem(r96_bounded, FALCON512_Q_NZ); + let r97_bounded: U128AsBounded = upcast(felt252_as_u128(v6290 + SHIFT)); + let (_, r97) = bounded_int_div_rem(r97_bounded, FALCON512_Q_NZ); + let r98_bounded: U128AsBounded = upcast(felt252_as_u128(v6292 + SHIFT)); + let (_, r98) = bounded_int_div_rem(r98_bounded, FALCON512_Q_NZ); + let r99_bounded: U128AsBounded = upcast(felt252_as_u128(v6293 + SHIFT)); + let (_, r99) = bounded_int_div_rem(r99_bounded, FALCON512_Q_NZ); + let r100_bounded: U128AsBounded = upcast(felt252_as_u128(v6295 + SHIFT)); + let (_, r100) = bounded_int_div_rem(r100_bounded, FALCON512_Q_NZ); + let r101_bounded: U128AsBounded = upcast(felt252_as_u128(v6296 + SHIFT)); + let (_, r101) = bounded_int_div_rem(r101_bounded, FALCON512_Q_NZ); + let r102_bounded: U128AsBounded = upcast(felt252_as_u128(v6298 + SHIFT)); + let (_, r102) = bounded_int_div_rem(r102_bounded, FALCON512_Q_NZ); + let r103_bounded: U128AsBounded = upcast(felt252_as_u128(v6299 + SHIFT)); + let (_, r103) = bounded_int_div_rem(r103_bounded, FALCON512_Q_NZ); + let r104_bounded: U128AsBounded = upcast(felt252_as_u128(v6301 + SHIFT)); + let (_, r104) = bounded_int_div_rem(r104_bounded, FALCON512_Q_NZ); + let r105_bounded: U128AsBounded = upcast(felt252_as_u128(v6302 + SHIFT)); + let (_, r105) = bounded_int_div_rem(r105_bounded, FALCON512_Q_NZ); + let r106_bounded: U128AsBounded = upcast(felt252_as_u128(v6304 + SHIFT)); + let (_, r106) = bounded_int_div_rem(r106_bounded, FALCON512_Q_NZ); + let r107_bounded: U128AsBounded = upcast(felt252_as_u128(v6305 + SHIFT)); + let (_, r107) = bounded_int_div_rem(r107_bounded, FALCON512_Q_NZ); + let r108_bounded: U128AsBounded = upcast(felt252_as_u128(v6307 + SHIFT)); + let (_, r108) = bounded_int_div_rem(r108_bounded, FALCON512_Q_NZ); + let r109_bounded: U128AsBounded = upcast(felt252_as_u128(v6308 + SHIFT)); + let (_, r109) = bounded_int_div_rem(r109_bounded, FALCON512_Q_NZ); + let r110_bounded: U128AsBounded = upcast(felt252_as_u128(v6310 + SHIFT)); + let (_, r110) = bounded_int_div_rem(r110_bounded, FALCON512_Q_NZ); + let r111_bounded: U128AsBounded = upcast(felt252_as_u128(v6311 + SHIFT)); + let (_, r111) = bounded_int_div_rem(r111_bounded, FALCON512_Q_NZ); + let r112_bounded: U128AsBounded = upcast(felt252_as_u128(v6313 + SHIFT)); + let (_, r112) = bounded_int_div_rem(r112_bounded, FALCON512_Q_NZ); + let r113_bounded: U128AsBounded = upcast(felt252_as_u128(v6314 + SHIFT)); + let (_, r113) = bounded_int_div_rem(r113_bounded, FALCON512_Q_NZ); + let r114_bounded: U128AsBounded = upcast(felt252_as_u128(v6316 + SHIFT)); + let (_, r114) = bounded_int_div_rem(r114_bounded, FALCON512_Q_NZ); + let r115_bounded: U128AsBounded = upcast(felt252_as_u128(v6317 + SHIFT)); + let (_, r115) = bounded_int_div_rem(r115_bounded, FALCON512_Q_NZ); + let r116_bounded: U128AsBounded = upcast(felt252_as_u128(v6319 + SHIFT)); + let (_, r116) = bounded_int_div_rem(r116_bounded, FALCON512_Q_NZ); + let r117_bounded: U128AsBounded = upcast(felt252_as_u128(v6320 + SHIFT)); + let (_, r117) = bounded_int_div_rem(r117_bounded, FALCON512_Q_NZ); + let r118_bounded: U128AsBounded = upcast(felt252_as_u128(v6322 + SHIFT)); + let (_, r118) = bounded_int_div_rem(r118_bounded, FALCON512_Q_NZ); + let r119_bounded: U128AsBounded = upcast(felt252_as_u128(v6323 + SHIFT)); + let (_, r119) = bounded_int_div_rem(r119_bounded, FALCON512_Q_NZ); + let r120_bounded: U128AsBounded = upcast(felt252_as_u128(v6325 + SHIFT)); + let (_, r120) = bounded_int_div_rem(r120_bounded, FALCON512_Q_NZ); + let r121_bounded: U128AsBounded = upcast(felt252_as_u128(v6326 + SHIFT)); + let (_, r121) = bounded_int_div_rem(r121_bounded, FALCON512_Q_NZ); + let r122_bounded: U128AsBounded = upcast(felt252_as_u128(v6328 + SHIFT)); + let (_, r122) = bounded_int_div_rem(r122_bounded, FALCON512_Q_NZ); + let r123_bounded: U128AsBounded = upcast(felt252_as_u128(v6329 + SHIFT)); + let (_, r123) = bounded_int_div_rem(r123_bounded, FALCON512_Q_NZ); + let r124_bounded: U128AsBounded = upcast(felt252_as_u128(v6331 + SHIFT)); + let (_, r124) = bounded_int_div_rem(r124_bounded, FALCON512_Q_NZ); + let r125_bounded: U128AsBounded = upcast(felt252_as_u128(v6332 + SHIFT)); + let (_, r125) = bounded_int_div_rem(r125_bounded, FALCON512_Q_NZ); + let r126_bounded: U128AsBounded = upcast(felt252_as_u128(v6334 + SHIFT)); + let (_, r126) = bounded_int_div_rem(r126_bounded, FALCON512_Q_NZ); + let r127_bounded: U128AsBounded = upcast(felt252_as_u128(v6335 + SHIFT)); + let (_, r127) = bounded_int_div_rem(r127_bounded, FALCON512_Q_NZ); + let r128_bounded: U128AsBounded = upcast(felt252_as_u128(v6337 + SHIFT)); + let (_, r128) = bounded_int_div_rem(r128_bounded, FALCON512_Q_NZ); + let r129_bounded: U128AsBounded = upcast(felt252_as_u128(v6338 + SHIFT)); + let (_, r129) = bounded_int_div_rem(r129_bounded, FALCON512_Q_NZ); + let r130_bounded: U128AsBounded = upcast(felt252_as_u128(v6340 + SHIFT)); + let (_, r130) = bounded_int_div_rem(r130_bounded, FALCON512_Q_NZ); + let r131_bounded: U128AsBounded = upcast(felt252_as_u128(v6341 + SHIFT)); + let (_, r131) = bounded_int_div_rem(r131_bounded, FALCON512_Q_NZ); + let r132_bounded: U128AsBounded = upcast(felt252_as_u128(v6343 + SHIFT)); + let (_, r132) = bounded_int_div_rem(r132_bounded, FALCON512_Q_NZ); + let r133_bounded: U128AsBounded = upcast(felt252_as_u128(v6344 + SHIFT)); + let (_, r133) = bounded_int_div_rem(r133_bounded, FALCON512_Q_NZ); + let r134_bounded: U128AsBounded = upcast(felt252_as_u128(v6346 + SHIFT)); + let (_, r134) = bounded_int_div_rem(r134_bounded, FALCON512_Q_NZ); + let r135_bounded: U128AsBounded = upcast(felt252_as_u128(v6347 + SHIFT)); + let (_, r135) = bounded_int_div_rem(r135_bounded, FALCON512_Q_NZ); + let r136_bounded: U128AsBounded = upcast(felt252_as_u128(v6349 + SHIFT)); + let (_, r136) = bounded_int_div_rem(r136_bounded, FALCON512_Q_NZ); + let r137_bounded: U128AsBounded = upcast(felt252_as_u128(v6350 + SHIFT)); + let (_, r137) = bounded_int_div_rem(r137_bounded, FALCON512_Q_NZ); + let r138_bounded: U128AsBounded = upcast(felt252_as_u128(v6352 + SHIFT)); + let (_, r138) = bounded_int_div_rem(r138_bounded, FALCON512_Q_NZ); + let r139_bounded: U128AsBounded = upcast(felt252_as_u128(v6353 + SHIFT)); + let (_, r139) = bounded_int_div_rem(r139_bounded, FALCON512_Q_NZ); + let r140_bounded: U128AsBounded = upcast(felt252_as_u128(v6355 + SHIFT)); + let (_, r140) = bounded_int_div_rem(r140_bounded, FALCON512_Q_NZ); + let r141_bounded: U128AsBounded = upcast(felt252_as_u128(v6356 + SHIFT)); + let (_, r141) = bounded_int_div_rem(r141_bounded, FALCON512_Q_NZ); + let r142_bounded: U128AsBounded = upcast(felt252_as_u128(v6358 + SHIFT)); + let (_, r142) = bounded_int_div_rem(r142_bounded, FALCON512_Q_NZ); + let r143_bounded: U128AsBounded = upcast(felt252_as_u128(v6359 + SHIFT)); + let (_, r143) = bounded_int_div_rem(r143_bounded, FALCON512_Q_NZ); + let r144_bounded: U128AsBounded = upcast(felt252_as_u128(v6361 + SHIFT)); + let (_, r144) = bounded_int_div_rem(r144_bounded, FALCON512_Q_NZ); + let r145_bounded: U128AsBounded = upcast(felt252_as_u128(v6362 + SHIFT)); + let (_, r145) = bounded_int_div_rem(r145_bounded, FALCON512_Q_NZ); + let r146_bounded: U128AsBounded = upcast(felt252_as_u128(v6364 + SHIFT)); + let (_, r146) = bounded_int_div_rem(r146_bounded, FALCON512_Q_NZ); + let r147_bounded: U128AsBounded = upcast(felt252_as_u128(v6365 + SHIFT)); + let (_, r147) = bounded_int_div_rem(r147_bounded, FALCON512_Q_NZ); + let r148_bounded: U128AsBounded = upcast(felt252_as_u128(v6367 + SHIFT)); + let (_, r148) = bounded_int_div_rem(r148_bounded, FALCON512_Q_NZ); + let r149_bounded: U128AsBounded = upcast(felt252_as_u128(v6368 + SHIFT)); + let (_, r149) = bounded_int_div_rem(r149_bounded, FALCON512_Q_NZ); + let r150_bounded: U128AsBounded = upcast(felt252_as_u128(v6370 + SHIFT)); + let (_, r150) = bounded_int_div_rem(r150_bounded, FALCON512_Q_NZ); + let r151_bounded: U128AsBounded = upcast(felt252_as_u128(v6371 + SHIFT)); + let (_, r151) = bounded_int_div_rem(r151_bounded, FALCON512_Q_NZ); + let r152_bounded: U128AsBounded = upcast(felt252_as_u128(v6373 + SHIFT)); + let (_, r152) = bounded_int_div_rem(r152_bounded, FALCON512_Q_NZ); + let r153_bounded: U128AsBounded = upcast(felt252_as_u128(v6374 + SHIFT)); + let (_, r153) = bounded_int_div_rem(r153_bounded, FALCON512_Q_NZ); + let r154_bounded: U128AsBounded = upcast(felt252_as_u128(v6376 + SHIFT)); + let (_, r154) = bounded_int_div_rem(r154_bounded, FALCON512_Q_NZ); + let r155_bounded: U128AsBounded = upcast(felt252_as_u128(v6377 + SHIFT)); + let (_, r155) = bounded_int_div_rem(r155_bounded, FALCON512_Q_NZ); + let r156_bounded: U128AsBounded = upcast(felt252_as_u128(v6379 + SHIFT)); + let (_, r156) = bounded_int_div_rem(r156_bounded, FALCON512_Q_NZ); + let r157_bounded: U128AsBounded = upcast(felt252_as_u128(v6380 + SHIFT)); + let (_, r157) = bounded_int_div_rem(r157_bounded, FALCON512_Q_NZ); + let r158_bounded: U128AsBounded = upcast(felt252_as_u128(v6382 + SHIFT)); + let (_, r158) = bounded_int_div_rem(r158_bounded, FALCON512_Q_NZ); + let r159_bounded: U128AsBounded = upcast(felt252_as_u128(v6383 + SHIFT)); + let (_, r159) = bounded_int_div_rem(r159_bounded, FALCON512_Q_NZ); + let r160_bounded: U128AsBounded = upcast(felt252_as_u128(v6385 + SHIFT)); + let (_, r160) = bounded_int_div_rem(r160_bounded, FALCON512_Q_NZ); + let r161_bounded: U128AsBounded = upcast(felt252_as_u128(v6386 + SHIFT)); + let (_, r161) = bounded_int_div_rem(r161_bounded, FALCON512_Q_NZ); + let r162_bounded: U128AsBounded = upcast(felt252_as_u128(v6388 + SHIFT)); + let (_, r162) = bounded_int_div_rem(r162_bounded, FALCON512_Q_NZ); + let r163_bounded: U128AsBounded = upcast(felt252_as_u128(v6389 + SHIFT)); + let (_, r163) = bounded_int_div_rem(r163_bounded, FALCON512_Q_NZ); + let r164_bounded: U128AsBounded = upcast(felt252_as_u128(v6391 + SHIFT)); + let (_, r164) = bounded_int_div_rem(r164_bounded, FALCON512_Q_NZ); + let r165_bounded: U128AsBounded = upcast(felt252_as_u128(v6392 + SHIFT)); + let (_, r165) = bounded_int_div_rem(r165_bounded, FALCON512_Q_NZ); + let r166_bounded: U128AsBounded = upcast(felt252_as_u128(v6394 + SHIFT)); + let (_, r166) = bounded_int_div_rem(r166_bounded, FALCON512_Q_NZ); + let r167_bounded: U128AsBounded = upcast(felt252_as_u128(v6395 + SHIFT)); + let (_, r167) = bounded_int_div_rem(r167_bounded, FALCON512_Q_NZ); + let r168_bounded: U128AsBounded = upcast(felt252_as_u128(v6397 + SHIFT)); + let (_, r168) = bounded_int_div_rem(r168_bounded, FALCON512_Q_NZ); + let r169_bounded: U128AsBounded = upcast(felt252_as_u128(v6398 + SHIFT)); + let (_, r169) = bounded_int_div_rem(r169_bounded, FALCON512_Q_NZ); + let r170_bounded: U128AsBounded = upcast(felt252_as_u128(v6400 + SHIFT)); + let (_, r170) = bounded_int_div_rem(r170_bounded, FALCON512_Q_NZ); + let r171_bounded: U128AsBounded = upcast(felt252_as_u128(v6401 + SHIFT)); + let (_, r171) = bounded_int_div_rem(r171_bounded, FALCON512_Q_NZ); + let r172_bounded: U128AsBounded = upcast(felt252_as_u128(v6403 + SHIFT)); + let (_, r172) = bounded_int_div_rem(r172_bounded, FALCON512_Q_NZ); + let r173_bounded: U128AsBounded = upcast(felt252_as_u128(v6404 + SHIFT)); + let (_, r173) = bounded_int_div_rem(r173_bounded, FALCON512_Q_NZ); + let r174_bounded: U128AsBounded = upcast(felt252_as_u128(v6406 + SHIFT)); + let (_, r174) = bounded_int_div_rem(r174_bounded, FALCON512_Q_NZ); + let r175_bounded: U128AsBounded = upcast(felt252_as_u128(v6407 + SHIFT)); + let (_, r175) = bounded_int_div_rem(r175_bounded, FALCON512_Q_NZ); + let r176_bounded: U128AsBounded = upcast(felt252_as_u128(v6409 + SHIFT)); + let (_, r176) = bounded_int_div_rem(r176_bounded, FALCON512_Q_NZ); + let r177_bounded: U128AsBounded = upcast(felt252_as_u128(v6410 + SHIFT)); + let (_, r177) = bounded_int_div_rem(r177_bounded, FALCON512_Q_NZ); + let r178_bounded: U128AsBounded = upcast(felt252_as_u128(v6412 + SHIFT)); + let (_, r178) = bounded_int_div_rem(r178_bounded, FALCON512_Q_NZ); + let r179_bounded: U128AsBounded = upcast(felt252_as_u128(v6413 + SHIFT)); + let (_, r179) = bounded_int_div_rem(r179_bounded, FALCON512_Q_NZ); + let r180_bounded: U128AsBounded = upcast(felt252_as_u128(v6415 + SHIFT)); + let (_, r180) = bounded_int_div_rem(r180_bounded, FALCON512_Q_NZ); + let r181_bounded: U128AsBounded = upcast(felt252_as_u128(v6416 + SHIFT)); + let (_, r181) = bounded_int_div_rem(r181_bounded, FALCON512_Q_NZ); + let r182_bounded: U128AsBounded = upcast(felt252_as_u128(v6418 + SHIFT)); + let (_, r182) = bounded_int_div_rem(r182_bounded, FALCON512_Q_NZ); + let r183_bounded: U128AsBounded = upcast(felt252_as_u128(v6419 + SHIFT)); + let (_, r183) = bounded_int_div_rem(r183_bounded, FALCON512_Q_NZ); + let r184_bounded: U128AsBounded = upcast(felt252_as_u128(v6421 + SHIFT)); + let (_, r184) = bounded_int_div_rem(r184_bounded, FALCON512_Q_NZ); + let r185_bounded: U128AsBounded = upcast(felt252_as_u128(v6422 + SHIFT)); + let (_, r185) = bounded_int_div_rem(r185_bounded, FALCON512_Q_NZ); + let r186_bounded: U128AsBounded = upcast(felt252_as_u128(v6424 + SHIFT)); + let (_, r186) = bounded_int_div_rem(r186_bounded, FALCON512_Q_NZ); + let r187_bounded: U128AsBounded = upcast(felt252_as_u128(v6425 + SHIFT)); + let (_, r187) = bounded_int_div_rem(r187_bounded, FALCON512_Q_NZ); + let r188_bounded: U128AsBounded = upcast(felt252_as_u128(v6427 + SHIFT)); + let (_, r188) = bounded_int_div_rem(r188_bounded, FALCON512_Q_NZ); + let r189_bounded: U128AsBounded = upcast(felt252_as_u128(v6428 + SHIFT)); + let (_, r189) = bounded_int_div_rem(r189_bounded, FALCON512_Q_NZ); + let r190_bounded: U128AsBounded = upcast(felt252_as_u128(v6430 + SHIFT)); + let (_, r190) = bounded_int_div_rem(r190_bounded, FALCON512_Q_NZ); + let r191_bounded: U128AsBounded = upcast(felt252_as_u128(v6431 + SHIFT)); + let (_, r191) = bounded_int_div_rem(r191_bounded, FALCON512_Q_NZ); + let r192_bounded: U128AsBounded = upcast(felt252_as_u128(v6433 + SHIFT)); + let (_, r192) = bounded_int_div_rem(r192_bounded, FALCON512_Q_NZ); + let r193_bounded: U128AsBounded = upcast(felt252_as_u128(v6434 + SHIFT)); + let (_, r193) = bounded_int_div_rem(r193_bounded, FALCON512_Q_NZ); + let r194_bounded: U128AsBounded = upcast(felt252_as_u128(v6436 + SHIFT)); + let (_, r194) = bounded_int_div_rem(r194_bounded, FALCON512_Q_NZ); + let r195_bounded: U128AsBounded = upcast(felt252_as_u128(v6437 + SHIFT)); + let (_, r195) = bounded_int_div_rem(r195_bounded, FALCON512_Q_NZ); + let r196_bounded: U128AsBounded = upcast(felt252_as_u128(v6439 + SHIFT)); + let (_, r196) = bounded_int_div_rem(r196_bounded, FALCON512_Q_NZ); + let r197_bounded: U128AsBounded = upcast(felt252_as_u128(v6440 + SHIFT)); + let (_, r197) = bounded_int_div_rem(r197_bounded, FALCON512_Q_NZ); + let r198_bounded: U128AsBounded = upcast(felt252_as_u128(v6442 + SHIFT)); + let (_, r198) = bounded_int_div_rem(r198_bounded, FALCON512_Q_NZ); + let r199_bounded: U128AsBounded = upcast(felt252_as_u128(v6443 + SHIFT)); + let (_, r199) = bounded_int_div_rem(r199_bounded, FALCON512_Q_NZ); + let r200_bounded: U128AsBounded = upcast(felt252_as_u128(v6445 + SHIFT)); + let (_, r200) = bounded_int_div_rem(r200_bounded, FALCON512_Q_NZ); + let r201_bounded: U128AsBounded = upcast(felt252_as_u128(v6446 + SHIFT)); + let (_, r201) = bounded_int_div_rem(r201_bounded, FALCON512_Q_NZ); + let r202_bounded: U128AsBounded = upcast(felt252_as_u128(v6448 + SHIFT)); + let (_, r202) = bounded_int_div_rem(r202_bounded, FALCON512_Q_NZ); + let r203_bounded: U128AsBounded = upcast(felt252_as_u128(v6449 + SHIFT)); + let (_, r203) = bounded_int_div_rem(r203_bounded, FALCON512_Q_NZ); + let r204_bounded: U128AsBounded = upcast(felt252_as_u128(v6451 + SHIFT)); + let (_, r204) = bounded_int_div_rem(r204_bounded, FALCON512_Q_NZ); + let r205_bounded: U128AsBounded = upcast(felt252_as_u128(v6452 + SHIFT)); + let (_, r205) = bounded_int_div_rem(r205_bounded, FALCON512_Q_NZ); + let r206_bounded: U128AsBounded = upcast(felt252_as_u128(v6454 + SHIFT)); + let (_, r206) = bounded_int_div_rem(r206_bounded, FALCON512_Q_NZ); + let r207_bounded: U128AsBounded = upcast(felt252_as_u128(v6455 + SHIFT)); + let (_, r207) = bounded_int_div_rem(r207_bounded, FALCON512_Q_NZ); + let r208_bounded: U128AsBounded = upcast(felt252_as_u128(v6457 + SHIFT)); + let (_, r208) = bounded_int_div_rem(r208_bounded, FALCON512_Q_NZ); + let r209_bounded: U128AsBounded = upcast(felt252_as_u128(v6458 + SHIFT)); + let (_, r209) = bounded_int_div_rem(r209_bounded, FALCON512_Q_NZ); + let r210_bounded: U128AsBounded = upcast(felt252_as_u128(v6460 + SHIFT)); + let (_, r210) = bounded_int_div_rem(r210_bounded, FALCON512_Q_NZ); + let r211_bounded: U128AsBounded = upcast(felt252_as_u128(v6461 + SHIFT)); + let (_, r211) = bounded_int_div_rem(r211_bounded, FALCON512_Q_NZ); + let r212_bounded: U128AsBounded = upcast(felt252_as_u128(v6463 + SHIFT)); + let (_, r212) = bounded_int_div_rem(r212_bounded, FALCON512_Q_NZ); + let r213_bounded: U128AsBounded = upcast(felt252_as_u128(v6464 + SHIFT)); + let (_, r213) = bounded_int_div_rem(r213_bounded, FALCON512_Q_NZ); + let r214_bounded: U128AsBounded = upcast(felt252_as_u128(v6466 + SHIFT)); + let (_, r214) = bounded_int_div_rem(r214_bounded, FALCON512_Q_NZ); + let r215_bounded: U128AsBounded = upcast(felt252_as_u128(v6467 + SHIFT)); + let (_, r215) = bounded_int_div_rem(r215_bounded, FALCON512_Q_NZ); + let r216_bounded: U128AsBounded = upcast(felt252_as_u128(v6469 + SHIFT)); + let (_, r216) = bounded_int_div_rem(r216_bounded, FALCON512_Q_NZ); + let r217_bounded: U128AsBounded = upcast(felt252_as_u128(v6470 + SHIFT)); + let (_, r217) = bounded_int_div_rem(r217_bounded, FALCON512_Q_NZ); + let r218_bounded: U128AsBounded = upcast(felt252_as_u128(v6472 + SHIFT)); + let (_, r218) = bounded_int_div_rem(r218_bounded, FALCON512_Q_NZ); + let r219_bounded: U128AsBounded = upcast(felt252_as_u128(v6473 + SHIFT)); + let (_, r219) = bounded_int_div_rem(r219_bounded, FALCON512_Q_NZ); + let r220_bounded: U128AsBounded = upcast(felt252_as_u128(v6475 + SHIFT)); + let (_, r220) = bounded_int_div_rem(r220_bounded, FALCON512_Q_NZ); + let r221_bounded: U128AsBounded = upcast(felt252_as_u128(v6476 + SHIFT)); + let (_, r221) = bounded_int_div_rem(r221_bounded, FALCON512_Q_NZ); + let r222_bounded: U128AsBounded = upcast(felt252_as_u128(v6478 + SHIFT)); + let (_, r222) = bounded_int_div_rem(r222_bounded, FALCON512_Q_NZ); + let r223_bounded: U128AsBounded = upcast(felt252_as_u128(v6479 + SHIFT)); + let (_, r223) = bounded_int_div_rem(r223_bounded, FALCON512_Q_NZ); + let r224_bounded: U128AsBounded = upcast(felt252_as_u128(v6481 + SHIFT)); + let (_, r224) = bounded_int_div_rem(r224_bounded, FALCON512_Q_NZ); + let r225_bounded: U128AsBounded = upcast(felt252_as_u128(v6482 + SHIFT)); + let (_, r225) = bounded_int_div_rem(r225_bounded, FALCON512_Q_NZ); + let r226_bounded: U128AsBounded = upcast(felt252_as_u128(v6484 + SHIFT)); + let (_, r226) = bounded_int_div_rem(r226_bounded, FALCON512_Q_NZ); + let r227_bounded: U128AsBounded = upcast(felt252_as_u128(v6485 + SHIFT)); + let (_, r227) = bounded_int_div_rem(r227_bounded, FALCON512_Q_NZ); + let r228_bounded: U128AsBounded = upcast(felt252_as_u128(v6487 + SHIFT)); + let (_, r228) = bounded_int_div_rem(r228_bounded, FALCON512_Q_NZ); + let r229_bounded: U128AsBounded = upcast(felt252_as_u128(v6488 + SHIFT)); + let (_, r229) = bounded_int_div_rem(r229_bounded, FALCON512_Q_NZ); + let r230_bounded: U128AsBounded = upcast(felt252_as_u128(v6490 + SHIFT)); + let (_, r230) = bounded_int_div_rem(r230_bounded, FALCON512_Q_NZ); + let r231_bounded: U128AsBounded = upcast(felt252_as_u128(v6491 + SHIFT)); + let (_, r231) = bounded_int_div_rem(r231_bounded, FALCON512_Q_NZ); + let r232_bounded: U128AsBounded = upcast(felt252_as_u128(v6493 + SHIFT)); + let (_, r232) = bounded_int_div_rem(r232_bounded, FALCON512_Q_NZ); + let r233_bounded: U128AsBounded = upcast(felt252_as_u128(v6494 + SHIFT)); + let (_, r233) = bounded_int_div_rem(r233_bounded, FALCON512_Q_NZ); + let r234_bounded: U128AsBounded = upcast(felt252_as_u128(v6496 + SHIFT)); + let (_, r234) = bounded_int_div_rem(r234_bounded, FALCON512_Q_NZ); + let r235_bounded: U128AsBounded = upcast(felt252_as_u128(v6497 + SHIFT)); + let (_, r235) = bounded_int_div_rem(r235_bounded, FALCON512_Q_NZ); + let r236_bounded: U128AsBounded = upcast(felt252_as_u128(v6499 + SHIFT)); + let (_, r236) = bounded_int_div_rem(r236_bounded, FALCON512_Q_NZ); + let r237_bounded: U128AsBounded = upcast(felt252_as_u128(v6500 + SHIFT)); + let (_, r237) = bounded_int_div_rem(r237_bounded, FALCON512_Q_NZ); + let r238_bounded: U128AsBounded = upcast(felt252_as_u128(v6502 + SHIFT)); + let (_, r238) = bounded_int_div_rem(r238_bounded, FALCON512_Q_NZ); + let r239_bounded: U128AsBounded = upcast(felt252_as_u128(v6503 + SHIFT)); + let (_, r239) = bounded_int_div_rem(r239_bounded, FALCON512_Q_NZ); + let r240_bounded: U128AsBounded = upcast(felt252_as_u128(v6505 + SHIFT)); + let (_, r240) = bounded_int_div_rem(r240_bounded, FALCON512_Q_NZ); + let r241_bounded: U128AsBounded = upcast(felt252_as_u128(v6506 + SHIFT)); + let (_, r241) = bounded_int_div_rem(r241_bounded, FALCON512_Q_NZ); + let r242_bounded: U128AsBounded = upcast(felt252_as_u128(v6508 + SHIFT)); + let (_, r242) = bounded_int_div_rem(r242_bounded, FALCON512_Q_NZ); + let r243_bounded: U128AsBounded = upcast(felt252_as_u128(v6509 + SHIFT)); + let (_, r243) = bounded_int_div_rem(r243_bounded, FALCON512_Q_NZ); + let r244_bounded: U128AsBounded = upcast(felt252_as_u128(v6511 + SHIFT)); + let (_, r244) = bounded_int_div_rem(r244_bounded, FALCON512_Q_NZ); + let r245_bounded: U128AsBounded = upcast(felt252_as_u128(v6512 + SHIFT)); + let (_, r245) = bounded_int_div_rem(r245_bounded, FALCON512_Q_NZ); + let r246_bounded: U128AsBounded = upcast(felt252_as_u128(v6514 + SHIFT)); + let (_, r246) = bounded_int_div_rem(r246_bounded, FALCON512_Q_NZ); + let r247_bounded: U128AsBounded = upcast(felt252_as_u128(v6515 + SHIFT)); + let (_, r247) = bounded_int_div_rem(r247_bounded, FALCON512_Q_NZ); + let r248_bounded: U128AsBounded = upcast(felt252_as_u128(v6517 + SHIFT)); + let (_, r248) = bounded_int_div_rem(r248_bounded, FALCON512_Q_NZ); + let r249_bounded: U128AsBounded = upcast(felt252_as_u128(v6518 + SHIFT)); + let (_, r249) = bounded_int_div_rem(r249_bounded, FALCON512_Q_NZ); + let r250_bounded: U128AsBounded = upcast(felt252_as_u128(v6520 + SHIFT)); + let (_, r250) = bounded_int_div_rem(r250_bounded, FALCON512_Q_NZ); + let r251_bounded: U128AsBounded = upcast(felt252_as_u128(v6521 + SHIFT)); + let (_, r251) = bounded_int_div_rem(r251_bounded, FALCON512_Q_NZ); + let r252_bounded: U128AsBounded = upcast(felt252_as_u128(v6523 + SHIFT)); + let (_, r252) = bounded_int_div_rem(r252_bounded, FALCON512_Q_NZ); + let r253_bounded: U128AsBounded = upcast(felt252_as_u128(v6524 + SHIFT)); + let (_, r253) = bounded_int_div_rem(r253_bounded, FALCON512_Q_NZ); + let r254_bounded: U128AsBounded = upcast(felt252_as_u128(v6526 + SHIFT)); + let (_, r254) = bounded_int_div_rem(r254_bounded, FALCON512_Q_NZ); + let r255_bounded: U128AsBounded = upcast(felt252_as_u128(v6527 + SHIFT)); + let (_, r255) = bounded_int_div_rem(r255_bounded, FALCON512_Q_NZ); + let r256_bounded: U128AsBounded = upcast(felt252_as_u128(v6529 + SHIFT)); + let (_, r256) = bounded_int_div_rem(r256_bounded, FALCON512_Q_NZ); + let r257_bounded: U128AsBounded = upcast(felt252_as_u128(v6530 + SHIFT)); + let (_, r257) = bounded_int_div_rem(r257_bounded, FALCON512_Q_NZ); + let r258_bounded: U128AsBounded = upcast(felt252_as_u128(v6532 + SHIFT)); + let (_, r258) = bounded_int_div_rem(r258_bounded, FALCON512_Q_NZ); + let r259_bounded: U128AsBounded = upcast(felt252_as_u128(v6533 + SHIFT)); + let (_, r259) = bounded_int_div_rem(r259_bounded, FALCON512_Q_NZ); + let r260_bounded: U128AsBounded = upcast(felt252_as_u128(v6535 + SHIFT)); + let (_, r260) = bounded_int_div_rem(r260_bounded, FALCON512_Q_NZ); + let r261_bounded: U128AsBounded = upcast(felt252_as_u128(v6536 + SHIFT)); + let (_, r261) = bounded_int_div_rem(r261_bounded, FALCON512_Q_NZ); + let r262_bounded: U128AsBounded = upcast(felt252_as_u128(v6538 + SHIFT)); + let (_, r262) = bounded_int_div_rem(r262_bounded, FALCON512_Q_NZ); + let r263_bounded: U128AsBounded = upcast(felt252_as_u128(v6539 + SHIFT)); + let (_, r263) = bounded_int_div_rem(r263_bounded, FALCON512_Q_NZ); + let r264_bounded: U128AsBounded = upcast(felt252_as_u128(v6541 + SHIFT)); + let (_, r264) = bounded_int_div_rem(r264_bounded, FALCON512_Q_NZ); + let r265_bounded: U128AsBounded = upcast(felt252_as_u128(v6542 + SHIFT)); + let (_, r265) = bounded_int_div_rem(r265_bounded, FALCON512_Q_NZ); + let r266_bounded: U128AsBounded = upcast(felt252_as_u128(v6544 + SHIFT)); + let (_, r266) = bounded_int_div_rem(r266_bounded, FALCON512_Q_NZ); + let r267_bounded: U128AsBounded = upcast(felt252_as_u128(v6545 + SHIFT)); + let (_, r267) = bounded_int_div_rem(r267_bounded, FALCON512_Q_NZ); + let r268_bounded: U128AsBounded = upcast(felt252_as_u128(v6547 + SHIFT)); + let (_, r268) = bounded_int_div_rem(r268_bounded, FALCON512_Q_NZ); + let r269_bounded: U128AsBounded = upcast(felt252_as_u128(v6548 + SHIFT)); + let (_, r269) = bounded_int_div_rem(r269_bounded, FALCON512_Q_NZ); + let r270_bounded: U128AsBounded = upcast(felt252_as_u128(v6550 + SHIFT)); + let (_, r270) = bounded_int_div_rem(r270_bounded, FALCON512_Q_NZ); + let r271_bounded: U128AsBounded = upcast(felt252_as_u128(v6551 + SHIFT)); + let (_, r271) = bounded_int_div_rem(r271_bounded, FALCON512_Q_NZ); + let r272_bounded: U128AsBounded = upcast(felt252_as_u128(v6553 + SHIFT)); + let (_, r272) = bounded_int_div_rem(r272_bounded, FALCON512_Q_NZ); + let r273_bounded: U128AsBounded = upcast(felt252_as_u128(v6554 + SHIFT)); + let (_, r273) = bounded_int_div_rem(r273_bounded, FALCON512_Q_NZ); + let r274_bounded: U128AsBounded = upcast(felt252_as_u128(v6556 + SHIFT)); + let (_, r274) = bounded_int_div_rem(r274_bounded, FALCON512_Q_NZ); + let r275_bounded: U128AsBounded = upcast(felt252_as_u128(v6557 + SHIFT)); + let (_, r275) = bounded_int_div_rem(r275_bounded, FALCON512_Q_NZ); + let r276_bounded: U128AsBounded = upcast(felt252_as_u128(v6559 + SHIFT)); + let (_, r276) = bounded_int_div_rem(r276_bounded, FALCON512_Q_NZ); + let r277_bounded: U128AsBounded = upcast(felt252_as_u128(v6560 + SHIFT)); + let (_, r277) = bounded_int_div_rem(r277_bounded, FALCON512_Q_NZ); + let r278_bounded: U128AsBounded = upcast(felt252_as_u128(v6562 + SHIFT)); + let (_, r278) = bounded_int_div_rem(r278_bounded, FALCON512_Q_NZ); + let r279_bounded: U128AsBounded = upcast(felt252_as_u128(v6563 + SHIFT)); + let (_, r279) = bounded_int_div_rem(r279_bounded, FALCON512_Q_NZ); + let r280_bounded: U128AsBounded = upcast(felt252_as_u128(v6565 + SHIFT)); + let (_, r280) = bounded_int_div_rem(r280_bounded, FALCON512_Q_NZ); + let r281_bounded: U128AsBounded = upcast(felt252_as_u128(v6566 + SHIFT)); + let (_, r281) = bounded_int_div_rem(r281_bounded, FALCON512_Q_NZ); + let r282_bounded: U128AsBounded = upcast(felt252_as_u128(v6568 + SHIFT)); + let (_, r282) = bounded_int_div_rem(r282_bounded, FALCON512_Q_NZ); + let r283_bounded: U128AsBounded = upcast(felt252_as_u128(v6569 + SHIFT)); + let (_, r283) = bounded_int_div_rem(r283_bounded, FALCON512_Q_NZ); + let r284_bounded: U128AsBounded = upcast(felt252_as_u128(v6571 + SHIFT)); + let (_, r284) = bounded_int_div_rem(r284_bounded, FALCON512_Q_NZ); + let r285_bounded: U128AsBounded = upcast(felt252_as_u128(v6572 + SHIFT)); + let (_, r285) = bounded_int_div_rem(r285_bounded, FALCON512_Q_NZ); + let r286_bounded: U128AsBounded = upcast(felt252_as_u128(v6574 + SHIFT)); + let (_, r286) = bounded_int_div_rem(r286_bounded, FALCON512_Q_NZ); + let r287_bounded: U128AsBounded = upcast(felt252_as_u128(v6575 + SHIFT)); + let (_, r287) = bounded_int_div_rem(r287_bounded, FALCON512_Q_NZ); + let r288_bounded: U128AsBounded = upcast(felt252_as_u128(v6577 + SHIFT)); + let (_, r288) = bounded_int_div_rem(r288_bounded, FALCON512_Q_NZ); + let r289_bounded: U128AsBounded = upcast(felt252_as_u128(v6578 + SHIFT)); + let (_, r289) = bounded_int_div_rem(r289_bounded, FALCON512_Q_NZ); + let r290_bounded: U128AsBounded = upcast(felt252_as_u128(v6580 + SHIFT)); + let (_, r290) = bounded_int_div_rem(r290_bounded, FALCON512_Q_NZ); + let r291_bounded: U128AsBounded = upcast(felt252_as_u128(v6581 + SHIFT)); + let (_, r291) = bounded_int_div_rem(r291_bounded, FALCON512_Q_NZ); + let r292_bounded: U128AsBounded = upcast(felt252_as_u128(v6583 + SHIFT)); + let (_, r292) = bounded_int_div_rem(r292_bounded, FALCON512_Q_NZ); + let r293_bounded: U128AsBounded = upcast(felt252_as_u128(v6584 + SHIFT)); + let (_, r293) = bounded_int_div_rem(r293_bounded, FALCON512_Q_NZ); + let r294_bounded: U128AsBounded = upcast(felt252_as_u128(v6586 + SHIFT)); + let (_, r294) = bounded_int_div_rem(r294_bounded, FALCON512_Q_NZ); + let r295_bounded: U128AsBounded = upcast(felt252_as_u128(v6587 + SHIFT)); + let (_, r295) = bounded_int_div_rem(r295_bounded, FALCON512_Q_NZ); + let r296_bounded: U128AsBounded = upcast(felt252_as_u128(v6589 + SHIFT)); + let (_, r296) = bounded_int_div_rem(r296_bounded, FALCON512_Q_NZ); + let r297_bounded: U128AsBounded = upcast(felt252_as_u128(v6590 + SHIFT)); + let (_, r297) = bounded_int_div_rem(r297_bounded, FALCON512_Q_NZ); + let r298_bounded: U128AsBounded = upcast(felt252_as_u128(v6592 + SHIFT)); + let (_, r298) = bounded_int_div_rem(r298_bounded, FALCON512_Q_NZ); + let r299_bounded: U128AsBounded = upcast(felt252_as_u128(v6593 + SHIFT)); + let (_, r299) = bounded_int_div_rem(r299_bounded, FALCON512_Q_NZ); + let r300_bounded: U128AsBounded = upcast(felt252_as_u128(v6595 + SHIFT)); + let (_, r300) = bounded_int_div_rem(r300_bounded, FALCON512_Q_NZ); + let r301_bounded: U128AsBounded = upcast(felt252_as_u128(v6596 + SHIFT)); + let (_, r301) = bounded_int_div_rem(r301_bounded, FALCON512_Q_NZ); + let r302_bounded: U128AsBounded = upcast(felt252_as_u128(v6598 + SHIFT)); + let (_, r302) = bounded_int_div_rem(r302_bounded, FALCON512_Q_NZ); + let r303_bounded: U128AsBounded = upcast(felt252_as_u128(v6599 + SHIFT)); + let (_, r303) = bounded_int_div_rem(r303_bounded, FALCON512_Q_NZ); + let r304_bounded: U128AsBounded = upcast(felt252_as_u128(v6601 + SHIFT)); + let (_, r304) = bounded_int_div_rem(r304_bounded, FALCON512_Q_NZ); + let r305_bounded: U128AsBounded = upcast(felt252_as_u128(v6602 + SHIFT)); + let (_, r305) = bounded_int_div_rem(r305_bounded, FALCON512_Q_NZ); + let r306_bounded: U128AsBounded = upcast(felt252_as_u128(v6604 + SHIFT)); + let (_, r306) = bounded_int_div_rem(r306_bounded, FALCON512_Q_NZ); + let r307_bounded: U128AsBounded = upcast(felt252_as_u128(v6605 + SHIFT)); + let (_, r307) = bounded_int_div_rem(r307_bounded, FALCON512_Q_NZ); + let r308_bounded: U128AsBounded = upcast(felt252_as_u128(v6607 + SHIFT)); + let (_, r308) = bounded_int_div_rem(r308_bounded, FALCON512_Q_NZ); + let r309_bounded: U128AsBounded = upcast(felt252_as_u128(v6608 + SHIFT)); + let (_, r309) = bounded_int_div_rem(r309_bounded, FALCON512_Q_NZ); + let r310_bounded: U128AsBounded = upcast(felt252_as_u128(v6610 + SHIFT)); + let (_, r310) = bounded_int_div_rem(r310_bounded, FALCON512_Q_NZ); + let r311_bounded: U128AsBounded = upcast(felt252_as_u128(v6611 + SHIFT)); + let (_, r311) = bounded_int_div_rem(r311_bounded, FALCON512_Q_NZ); + let r312_bounded: U128AsBounded = upcast(felt252_as_u128(v6613 + SHIFT)); + let (_, r312) = bounded_int_div_rem(r312_bounded, FALCON512_Q_NZ); + let r313_bounded: U128AsBounded = upcast(felt252_as_u128(v6614 + SHIFT)); + let (_, r313) = bounded_int_div_rem(r313_bounded, FALCON512_Q_NZ); + let r314_bounded: U128AsBounded = upcast(felt252_as_u128(v6616 + SHIFT)); + let (_, r314) = bounded_int_div_rem(r314_bounded, FALCON512_Q_NZ); + let r315_bounded: U128AsBounded = upcast(felt252_as_u128(v6617 + SHIFT)); + let (_, r315) = bounded_int_div_rem(r315_bounded, FALCON512_Q_NZ); + let r316_bounded: U128AsBounded = upcast(felt252_as_u128(v6619 + SHIFT)); + let (_, r316) = bounded_int_div_rem(r316_bounded, FALCON512_Q_NZ); + let r317_bounded: U128AsBounded = upcast(felt252_as_u128(v6620 + SHIFT)); + let (_, r317) = bounded_int_div_rem(r317_bounded, FALCON512_Q_NZ); + let r318_bounded: U128AsBounded = upcast(felt252_as_u128(v6622 + SHIFT)); + let (_, r318) = bounded_int_div_rem(r318_bounded, FALCON512_Q_NZ); + let r319_bounded: U128AsBounded = upcast(felt252_as_u128(v6623 + SHIFT)); + let (_, r319) = bounded_int_div_rem(r319_bounded, FALCON512_Q_NZ); + let r320_bounded: U128AsBounded = upcast(felt252_as_u128(v6625 + SHIFT)); + let (_, r320) = bounded_int_div_rem(r320_bounded, FALCON512_Q_NZ); + let r321_bounded: U128AsBounded = upcast(felt252_as_u128(v6626 + SHIFT)); + let (_, r321) = bounded_int_div_rem(r321_bounded, FALCON512_Q_NZ); + let r322_bounded: U128AsBounded = upcast(felt252_as_u128(v6628 + SHIFT)); + let (_, r322) = bounded_int_div_rem(r322_bounded, FALCON512_Q_NZ); + let r323_bounded: U128AsBounded = upcast(felt252_as_u128(v6629 + SHIFT)); + let (_, r323) = bounded_int_div_rem(r323_bounded, FALCON512_Q_NZ); + let r324_bounded: U128AsBounded = upcast(felt252_as_u128(v6631 + SHIFT)); + let (_, r324) = bounded_int_div_rem(r324_bounded, FALCON512_Q_NZ); + let r325_bounded: U128AsBounded = upcast(felt252_as_u128(v6632 + SHIFT)); + let (_, r325) = bounded_int_div_rem(r325_bounded, FALCON512_Q_NZ); + let r326_bounded: U128AsBounded = upcast(felt252_as_u128(v6634 + SHIFT)); + let (_, r326) = bounded_int_div_rem(r326_bounded, FALCON512_Q_NZ); + let r327_bounded: U128AsBounded = upcast(felt252_as_u128(v6635 + SHIFT)); + let (_, r327) = bounded_int_div_rem(r327_bounded, FALCON512_Q_NZ); + let r328_bounded: U128AsBounded = upcast(felt252_as_u128(v6637 + SHIFT)); + let (_, r328) = bounded_int_div_rem(r328_bounded, FALCON512_Q_NZ); + let r329_bounded: U128AsBounded = upcast(felt252_as_u128(v6638 + SHIFT)); + let (_, r329) = bounded_int_div_rem(r329_bounded, FALCON512_Q_NZ); + let r330_bounded: U128AsBounded = upcast(felt252_as_u128(v6640 + SHIFT)); + let (_, r330) = bounded_int_div_rem(r330_bounded, FALCON512_Q_NZ); + let r331_bounded: U128AsBounded = upcast(felt252_as_u128(v6641 + SHIFT)); + let (_, r331) = bounded_int_div_rem(r331_bounded, FALCON512_Q_NZ); + let r332_bounded: U128AsBounded = upcast(felt252_as_u128(v6643 + SHIFT)); + let (_, r332) = bounded_int_div_rem(r332_bounded, FALCON512_Q_NZ); + let r333_bounded: U128AsBounded = upcast(felt252_as_u128(v6644 + SHIFT)); + let (_, r333) = bounded_int_div_rem(r333_bounded, FALCON512_Q_NZ); + let r334_bounded: U128AsBounded = upcast(felt252_as_u128(v6646 + SHIFT)); + let (_, r334) = bounded_int_div_rem(r334_bounded, FALCON512_Q_NZ); + let r335_bounded: U128AsBounded = upcast(felt252_as_u128(v6647 + SHIFT)); + let (_, r335) = bounded_int_div_rem(r335_bounded, FALCON512_Q_NZ); + let r336_bounded: U128AsBounded = upcast(felt252_as_u128(v6649 + SHIFT)); + let (_, r336) = bounded_int_div_rem(r336_bounded, FALCON512_Q_NZ); + let r337_bounded: U128AsBounded = upcast(felt252_as_u128(v6650 + SHIFT)); + let (_, r337) = bounded_int_div_rem(r337_bounded, FALCON512_Q_NZ); + let r338_bounded: U128AsBounded = upcast(felt252_as_u128(v6652 + SHIFT)); + let (_, r338) = bounded_int_div_rem(r338_bounded, FALCON512_Q_NZ); + let r339_bounded: U128AsBounded = upcast(felt252_as_u128(v6653 + SHIFT)); + let (_, r339) = bounded_int_div_rem(r339_bounded, FALCON512_Q_NZ); + let r340_bounded: U128AsBounded = upcast(felt252_as_u128(v6655 + SHIFT)); + let (_, r340) = bounded_int_div_rem(r340_bounded, FALCON512_Q_NZ); + let r341_bounded: U128AsBounded = upcast(felt252_as_u128(v6656 + SHIFT)); + let (_, r341) = bounded_int_div_rem(r341_bounded, FALCON512_Q_NZ); + let r342_bounded: U128AsBounded = upcast(felt252_as_u128(v6658 + SHIFT)); + let (_, r342) = bounded_int_div_rem(r342_bounded, FALCON512_Q_NZ); + let r343_bounded: U128AsBounded = upcast(felt252_as_u128(v6659 + SHIFT)); + let (_, r343) = bounded_int_div_rem(r343_bounded, FALCON512_Q_NZ); + let r344_bounded: U128AsBounded = upcast(felt252_as_u128(v6661 + SHIFT)); + let (_, r344) = bounded_int_div_rem(r344_bounded, FALCON512_Q_NZ); + let r345_bounded: U128AsBounded = upcast(felt252_as_u128(v6662 + SHIFT)); + let (_, r345) = bounded_int_div_rem(r345_bounded, FALCON512_Q_NZ); + let r346_bounded: U128AsBounded = upcast(felt252_as_u128(v6664 + SHIFT)); + let (_, r346) = bounded_int_div_rem(r346_bounded, FALCON512_Q_NZ); + let r347_bounded: U128AsBounded = upcast(felt252_as_u128(v6665 + SHIFT)); + let (_, r347) = bounded_int_div_rem(r347_bounded, FALCON512_Q_NZ); + let r348_bounded: U128AsBounded = upcast(felt252_as_u128(v6667 + SHIFT)); + let (_, r348) = bounded_int_div_rem(r348_bounded, FALCON512_Q_NZ); + let r349_bounded: U128AsBounded = upcast(felt252_as_u128(v6668 + SHIFT)); + let (_, r349) = bounded_int_div_rem(r349_bounded, FALCON512_Q_NZ); + let r350_bounded: U128AsBounded = upcast(felt252_as_u128(v6670 + SHIFT)); + let (_, r350) = bounded_int_div_rem(r350_bounded, FALCON512_Q_NZ); + let r351_bounded: U128AsBounded = upcast(felt252_as_u128(v6671 + SHIFT)); + let (_, r351) = bounded_int_div_rem(r351_bounded, FALCON512_Q_NZ); + let r352_bounded: U128AsBounded = upcast(felt252_as_u128(v6673 + SHIFT)); + let (_, r352) = bounded_int_div_rem(r352_bounded, FALCON512_Q_NZ); + let r353_bounded: U128AsBounded = upcast(felt252_as_u128(v6674 + SHIFT)); + let (_, r353) = bounded_int_div_rem(r353_bounded, FALCON512_Q_NZ); + let r354_bounded: U128AsBounded = upcast(felt252_as_u128(v6676 + SHIFT)); + let (_, r354) = bounded_int_div_rem(r354_bounded, FALCON512_Q_NZ); + let r355_bounded: U128AsBounded = upcast(felt252_as_u128(v6677 + SHIFT)); + let (_, r355) = bounded_int_div_rem(r355_bounded, FALCON512_Q_NZ); + let r356_bounded: U128AsBounded = upcast(felt252_as_u128(v6679 + SHIFT)); + let (_, r356) = bounded_int_div_rem(r356_bounded, FALCON512_Q_NZ); + let r357_bounded: U128AsBounded = upcast(felt252_as_u128(v6680 + SHIFT)); + let (_, r357) = bounded_int_div_rem(r357_bounded, FALCON512_Q_NZ); + let r358_bounded: U128AsBounded = upcast(felt252_as_u128(v6682 + SHIFT)); + let (_, r358) = bounded_int_div_rem(r358_bounded, FALCON512_Q_NZ); + let r359_bounded: U128AsBounded = upcast(felt252_as_u128(v6683 + SHIFT)); + let (_, r359) = bounded_int_div_rem(r359_bounded, FALCON512_Q_NZ); + let r360_bounded: U128AsBounded = upcast(felt252_as_u128(v6685 + SHIFT)); + let (_, r360) = bounded_int_div_rem(r360_bounded, FALCON512_Q_NZ); + let r361_bounded: U128AsBounded = upcast(felt252_as_u128(v6686 + SHIFT)); + let (_, r361) = bounded_int_div_rem(r361_bounded, FALCON512_Q_NZ); + let r362_bounded: U128AsBounded = upcast(felt252_as_u128(v6688 + SHIFT)); + let (_, r362) = bounded_int_div_rem(r362_bounded, FALCON512_Q_NZ); + let r363_bounded: U128AsBounded = upcast(felt252_as_u128(v6689 + SHIFT)); + let (_, r363) = bounded_int_div_rem(r363_bounded, FALCON512_Q_NZ); + let r364_bounded: U128AsBounded = upcast(felt252_as_u128(v6691 + SHIFT)); + let (_, r364) = bounded_int_div_rem(r364_bounded, FALCON512_Q_NZ); + let r365_bounded: U128AsBounded = upcast(felt252_as_u128(v6692 + SHIFT)); + let (_, r365) = bounded_int_div_rem(r365_bounded, FALCON512_Q_NZ); + let r366_bounded: U128AsBounded = upcast(felt252_as_u128(v6694 + SHIFT)); + let (_, r366) = bounded_int_div_rem(r366_bounded, FALCON512_Q_NZ); + let r367_bounded: U128AsBounded = upcast(felt252_as_u128(v6695 + SHIFT)); + let (_, r367) = bounded_int_div_rem(r367_bounded, FALCON512_Q_NZ); + let r368_bounded: U128AsBounded = upcast(felt252_as_u128(v6697 + SHIFT)); + let (_, r368) = bounded_int_div_rem(r368_bounded, FALCON512_Q_NZ); + let r369_bounded: U128AsBounded = upcast(felt252_as_u128(v6698 + SHIFT)); + let (_, r369) = bounded_int_div_rem(r369_bounded, FALCON512_Q_NZ); + let r370_bounded: U128AsBounded = upcast(felt252_as_u128(v6700 + SHIFT)); + let (_, r370) = bounded_int_div_rem(r370_bounded, FALCON512_Q_NZ); + let r371_bounded: U128AsBounded = upcast(felt252_as_u128(v6701 + SHIFT)); + let (_, r371) = bounded_int_div_rem(r371_bounded, FALCON512_Q_NZ); + let r372_bounded: U128AsBounded = upcast(felt252_as_u128(v6703 + SHIFT)); + let (_, r372) = bounded_int_div_rem(r372_bounded, FALCON512_Q_NZ); + let r373_bounded: U128AsBounded = upcast(felt252_as_u128(v6704 + SHIFT)); + let (_, r373) = bounded_int_div_rem(r373_bounded, FALCON512_Q_NZ); + let r374_bounded: U128AsBounded = upcast(felt252_as_u128(v6706 + SHIFT)); + let (_, r374) = bounded_int_div_rem(r374_bounded, FALCON512_Q_NZ); + let r375_bounded: U128AsBounded = upcast(felt252_as_u128(v6707 + SHIFT)); + let (_, r375) = bounded_int_div_rem(r375_bounded, FALCON512_Q_NZ); + let r376_bounded: U128AsBounded = upcast(felt252_as_u128(v6709 + SHIFT)); + let (_, r376) = bounded_int_div_rem(r376_bounded, FALCON512_Q_NZ); + let r377_bounded: U128AsBounded = upcast(felt252_as_u128(v6710 + SHIFT)); + let (_, r377) = bounded_int_div_rem(r377_bounded, FALCON512_Q_NZ); + let r378_bounded: U128AsBounded = upcast(felt252_as_u128(v6712 + SHIFT)); + let (_, r378) = bounded_int_div_rem(r378_bounded, FALCON512_Q_NZ); + let r379_bounded: U128AsBounded = upcast(felt252_as_u128(v6713 + SHIFT)); + let (_, r379) = bounded_int_div_rem(r379_bounded, FALCON512_Q_NZ); + let r380_bounded: U128AsBounded = upcast(felt252_as_u128(v6715 + SHIFT)); + let (_, r380) = bounded_int_div_rem(r380_bounded, FALCON512_Q_NZ); + let r381_bounded: U128AsBounded = upcast(felt252_as_u128(v6716 + SHIFT)); + let (_, r381) = bounded_int_div_rem(r381_bounded, FALCON512_Q_NZ); + let r382_bounded: U128AsBounded = upcast(felt252_as_u128(v6718 + SHIFT)); + let (_, r382) = bounded_int_div_rem(r382_bounded, FALCON512_Q_NZ); + let r383_bounded: U128AsBounded = upcast(felt252_as_u128(v6719 + SHIFT)); + let (_, r383) = bounded_int_div_rem(r383_bounded, FALCON512_Q_NZ); + let r384_bounded: U128AsBounded = upcast(felt252_as_u128(v6721 + SHIFT)); + let (_, r384) = bounded_int_div_rem(r384_bounded, FALCON512_Q_NZ); + let r385_bounded: U128AsBounded = upcast(felt252_as_u128(v6722 + SHIFT)); + let (_, r385) = bounded_int_div_rem(r385_bounded, FALCON512_Q_NZ); + let r386_bounded: U128AsBounded = upcast(felt252_as_u128(v6724 + SHIFT)); + let (_, r386) = bounded_int_div_rem(r386_bounded, FALCON512_Q_NZ); + let r387_bounded: U128AsBounded = upcast(felt252_as_u128(v6725 + SHIFT)); + let (_, r387) = bounded_int_div_rem(r387_bounded, FALCON512_Q_NZ); + let r388_bounded: U128AsBounded = upcast(felt252_as_u128(v6727 + SHIFT)); + let (_, r388) = bounded_int_div_rem(r388_bounded, FALCON512_Q_NZ); + let r389_bounded: U128AsBounded = upcast(felt252_as_u128(v6728 + SHIFT)); + let (_, r389) = bounded_int_div_rem(r389_bounded, FALCON512_Q_NZ); + let r390_bounded: U128AsBounded = upcast(felt252_as_u128(v6730 + SHIFT)); + let (_, r390) = bounded_int_div_rem(r390_bounded, FALCON512_Q_NZ); + let r391_bounded: U128AsBounded = upcast(felt252_as_u128(v6731 + SHIFT)); + let (_, r391) = bounded_int_div_rem(r391_bounded, FALCON512_Q_NZ); + let r392_bounded: U128AsBounded = upcast(felt252_as_u128(v6733 + SHIFT)); + let (_, r392) = bounded_int_div_rem(r392_bounded, FALCON512_Q_NZ); + let r393_bounded: U128AsBounded = upcast(felt252_as_u128(v6734 + SHIFT)); + let (_, r393) = bounded_int_div_rem(r393_bounded, FALCON512_Q_NZ); + let r394_bounded: U128AsBounded = upcast(felt252_as_u128(v6736 + SHIFT)); + let (_, r394) = bounded_int_div_rem(r394_bounded, FALCON512_Q_NZ); + let r395_bounded: U128AsBounded = upcast(felt252_as_u128(v6737 + SHIFT)); + let (_, r395) = bounded_int_div_rem(r395_bounded, FALCON512_Q_NZ); + let r396_bounded: U128AsBounded = upcast(felt252_as_u128(v6739 + SHIFT)); + let (_, r396) = bounded_int_div_rem(r396_bounded, FALCON512_Q_NZ); + let r397_bounded: U128AsBounded = upcast(felt252_as_u128(v6740 + SHIFT)); + let (_, r397) = bounded_int_div_rem(r397_bounded, FALCON512_Q_NZ); + let r398_bounded: U128AsBounded = upcast(felt252_as_u128(v6742 + SHIFT)); + let (_, r398) = bounded_int_div_rem(r398_bounded, FALCON512_Q_NZ); + let r399_bounded: U128AsBounded = upcast(felt252_as_u128(v6743 + SHIFT)); + let (_, r399) = bounded_int_div_rem(r399_bounded, FALCON512_Q_NZ); + let r400_bounded: U128AsBounded = upcast(felt252_as_u128(v6745 + SHIFT)); + let (_, r400) = bounded_int_div_rem(r400_bounded, FALCON512_Q_NZ); + let r401_bounded: U128AsBounded = upcast(felt252_as_u128(v6746 + SHIFT)); + let (_, r401) = bounded_int_div_rem(r401_bounded, FALCON512_Q_NZ); + let r402_bounded: U128AsBounded = upcast(felt252_as_u128(v6748 + SHIFT)); + let (_, r402) = bounded_int_div_rem(r402_bounded, FALCON512_Q_NZ); + let r403_bounded: U128AsBounded = upcast(felt252_as_u128(v6749 + SHIFT)); + let (_, r403) = bounded_int_div_rem(r403_bounded, FALCON512_Q_NZ); + let r404_bounded: U128AsBounded = upcast(felt252_as_u128(v6751 + SHIFT)); + let (_, r404) = bounded_int_div_rem(r404_bounded, FALCON512_Q_NZ); + let r405_bounded: U128AsBounded = upcast(felt252_as_u128(v6752 + SHIFT)); + let (_, r405) = bounded_int_div_rem(r405_bounded, FALCON512_Q_NZ); + let r406_bounded: U128AsBounded = upcast(felt252_as_u128(v6754 + SHIFT)); + let (_, r406) = bounded_int_div_rem(r406_bounded, FALCON512_Q_NZ); + let r407_bounded: U128AsBounded = upcast(felt252_as_u128(v6755 + SHIFT)); + let (_, r407) = bounded_int_div_rem(r407_bounded, FALCON512_Q_NZ); + let r408_bounded: U128AsBounded = upcast(felt252_as_u128(v6757 + SHIFT)); + let (_, r408) = bounded_int_div_rem(r408_bounded, FALCON512_Q_NZ); + let r409_bounded: U128AsBounded = upcast(felt252_as_u128(v6758 + SHIFT)); + let (_, r409) = bounded_int_div_rem(r409_bounded, FALCON512_Q_NZ); + let r410_bounded: U128AsBounded = upcast(felt252_as_u128(v6760 + SHIFT)); + let (_, r410) = bounded_int_div_rem(r410_bounded, FALCON512_Q_NZ); + let r411_bounded: U128AsBounded = upcast(felt252_as_u128(v6761 + SHIFT)); + let (_, r411) = bounded_int_div_rem(r411_bounded, FALCON512_Q_NZ); + let r412_bounded: U128AsBounded = upcast(felt252_as_u128(v6763 + SHIFT)); + let (_, r412) = bounded_int_div_rem(r412_bounded, FALCON512_Q_NZ); + let r413_bounded: U128AsBounded = upcast(felt252_as_u128(v6764 + SHIFT)); + let (_, r413) = bounded_int_div_rem(r413_bounded, FALCON512_Q_NZ); + let r414_bounded: U128AsBounded = upcast(felt252_as_u128(v6766 + SHIFT)); + let (_, r414) = bounded_int_div_rem(r414_bounded, FALCON512_Q_NZ); + let r415_bounded: U128AsBounded = upcast(felt252_as_u128(v6767 + SHIFT)); + let (_, r415) = bounded_int_div_rem(r415_bounded, FALCON512_Q_NZ); + let r416_bounded: U128AsBounded = upcast(felt252_as_u128(v6769 + SHIFT)); + let (_, r416) = bounded_int_div_rem(r416_bounded, FALCON512_Q_NZ); + let r417_bounded: U128AsBounded = upcast(felt252_as_u128(v6770 + SHIFT)); + let (_, r417) = bounded_int_div_rem(r417_bounded, FALCON512_Q_NZ); + let r418_bounded: U128AsBounded = upcast(felt252_as_u128(v6772 + SHIFT)); + let (_, r418) = bounded_int_div_rem(r418_bounded, FALCON512_Q_NZ); + let r419_bounded: U128AsBounded = upcast(felt252_as_u128(v6773 + SHIFT)); + let (_, r419) = bounded_int_div_rem(r419_bounded, FALCON512_Q_NZ); + let r420_bounded: U128AsBounded = upcast(felt252_as_u128(v6775 + SHIFT)); + let (_, r420) = bounded_int_div_rem(r420_bounded, FALCON512_Q_NZ); + let r421_bounded: U128AsBounded = upcast(felt252_as_u128(v6776 + SHIFT)); + let (_, r421) = bounded_int_div_rem(r421_bounded, FALCON512_Q_NZ); + let r422_bounded: U128AsBounded = upcast(felt252_as_u128(v6778 + SHIFT)); + let (_, r422) = bounded_int_div_rem(r422_bounded, FALCON512_Q_NZ); + let r423_bounded: U128AsBounded = upcast(felt252_as_u128(v6779 + SHIFT)); + let (_, r423) = bounded_int_div_rem(r423_bounded, FALCON512_Q_NZ); + let r424_bounded: U128AsBounded = upcast(felt252_as_u128(v6781 + SHIFT)); + let (_, r424) = bounded_int_div_rem(r424_bounded, FALCON512_Q_NZ); + let r425_bounded: U128AsBounded = upcast(felt252_as_u128(v6782 + SHIFT)); + let (_, r425) = bounded_int_div_rem(r425_bounded, FALCON512_Q_NZ); + let r426_bounded: U128AsBounded = upcast(felt252_as_u128(v6784 + SHIFT)); + let (_, r426) = bounded_int_div_rem(r426_bounded, FALCON512_Q_NZ); + let r427_bounded: U128AsBounded = upcast(felt252_as_u128(v6785 + SHIFT)); + let (_, r427) = bounded_int_div_rem(r427_bounded, FALCON512_Q_NZ); + let r428_bounded: U128AsBounded = upcast(felt252_as_u128(v6787 + SHIFT)); + let (_, r428) = bounded_int_div_rem(r428_bounded, FALCON512_Q_NZ); + let r429_bounded: U128AsBounded = upcast(felt252_as_u128(v6788 + SHIFT)); + let (_, r429) = bounded_int_div_rem(r429_bounded, FALCON512_Q_NZ); + let r430_bounded: U128AsBounded = upcast(felt252_as_u128(v6790 + SHIFT)); + let (_, r430) = bounded_int_div_rem(r430_bounded, FALCON512_Q_NZ); + let r431_bounded: U128AsBounded = upcast(felt252_as_u128(v6791 + SHIFT)); + let (_, r431) = bounded_int_div_rem(r431_bounded, FALCON512_Q_NZ); + let r432_bounded: U128AsBounded = upcast(felt252_as_u128(v6793 + SHIFT)); + let (_, r432) = bounded_int_div_rem(r432_bounded, FALCON512_Q_NZ); + let r433_bounded: U128AsBounded = upcast(felt252_as_u128(v6794 + SHIFT)); + let (_, r433) = bounded_int_div_rem(r433_bounded, FALCON512_Q_NZ); + let r434_bounded: U128AsBounded = upcast(felt252_as_u128(v6796 + SHIFT)); + let (_, r434) = bounded_int_div_rem(r434_bounded, FALCON512_Q_NZ); + let r435_bounded: U128AsBounded = upcast(felt252_as_u128(v6797 + SHIFT)); + let (_, r435) = bounded_int_div_rem(r435_bounded, FALCON512_Q_NZ); + let r436_bounded: U128AsBounded = upcast(felt252_as_u128(v6799 + SHIFT)); + let (_, r436) = bounded_int_div_rem(r436_bounded, FALCON512_Q_NZ); + let r437_bounded: U128AsBounded = upcast(felt252_as_u128(v6800 + SHIFT)); + let (_, r437) = bounded_int_div_rem(r437_bounded, FALCON512_Q_NZ); + let r438_bounded: U128AsBounded = upcast(felt252_as_u128(v6802 + SHIFT)); + let (_, r438) = bounded_int_div_rem(r438_bounded, FALCON512_Q_NZ); + let r439_bounded: U128AsBounded = upcast(felt252_as_u128(v6803 + SHIFT)); + let (_, r439) = bounded_int_div_rem(r439_bounded, FALCON512_Q_NZ); + let r440_bounded: U128AsBounded = upcast(felt252_as_u128(v6805 + SHIFT)); + let (_, r440) = bounded_int_div_rem(r440_bounded, FALCON512_Q_NZ); + let r441_bounded: U128AsBounded = upcast(felt252_as_u128(v6806 + SHIFT)); + let (_, r441) = bounded_int_div_rem(r441_bounded, FALCON512_Q_NZ); + let r442_bounded: U128AsBounded = upcast(felt252_as_u128(v6808 + SHIFT)); + let (_, r442) = bounded_int_div_rem(r442_bounded, FALCON512_Q_NZ); + let r443_bounded: U128AsBounded = upcast(felt252_as_u128(v6809 + SHIFT)); + let (_, r443) = bounded_int_div_rem(r443_bounded, FALCON512_Q_NZ); + let r444_bounded: U128AsBounded = upcast(felt252_as_u128(v6811 + SHIFT)); + let (_, r444) = bounded_int_div_rem(r444_bounded, FALCON512_Q_NZ); + let r445_bounded: U128AsBounded = upcast(felt252_as_u128(v6812 + SHIFT)); + let (_, r445) = bounded_int_div_rem(r445_bounded, FALCON512_Q_NZ); + let r446_bounded: U128AsBounded = upcast(felt252_as_u128(v6814 + SHIFT)); + let (_, r446) = bounded_int_div_rem(r446_bounded, FALCON512_Q_NZ); + let r447_bounded: U128AsBounded = upcast(felt252_as_u128(v6815 + SHIFT)); + let (_, r447) = bounded_int_div_rem(r447_bounded, FALCON512_Q_NZ); + let r448_bounded: U128AsBounded = upcast(felt252_as_u128(v6817 + SHIFT)); + let (_, r448) = bounded_int_div_rem(r448_bounded, FALCON512_Q_NZ); + let r449_bounded: U128AsBounded = upcast(felt252_as_u128(v6818 + SHIFT)); + let (_, r449) = bounded_int_div_rem(r449_bounded, FALCON512_Q_NZ); + let r450_bounded: U128AsBounded = upcast(felt252_as_u128(v6820 + SHIFT)); + let (_, r450) = bounded_int_div_rem(r450_bounded, FALCON512_Q_NZ); + let r451_bounded: U128AsBounded = upcast(felt252_as_u128(v6821 + SHIFT)); + let (_, r451) = bounded_int_div_rem(r451_bounded, FALCON512_Q_NZ); + let r452_bounded: U128AsBounded = upcast(felt252_as_u128(v6823 + SHIFT)); + let (_, r452) = bounded_int_div_rem(r452_bounded, FALCON512_Q_NZ); + let r453_bounded: U128AsBounded = upcast(felt252_as_u128(v6824 + SHIFT)); + let (_, r453) = bounded_int_div_rem(r453_bounded, FALCON512_Q_NZ); + let r454_bounded: U128AsBounded = upcast(felt252_as_u128(v6826 + SHIFT)); + let (_, r454) = bounded_int_div_rem(r454_bounded, FALCON512_Q_NZ); + let r455_bounded: U128AsBounded = upcast(felt252_as_u128(v6827 + SHIFT)); + let (_, r455) = bounded_int_div_rem(r455_bounded, FALCON512_Q_NZ); + let r456_bounded: U128AsBounded = upcast(felt252_as_u128(v6829 + SHIFT)); + let (_, r456) = bounded_int_div_rem(r456_bounded, FALCON512_Q_NZ); + let r457_bounded: U128AsBounded = upcast(felt252_as_u128(v6830 + SHIFT)); + let (_, r457) = bounded_int_div_rem(r457_bounded, FALCON512_Q_NZ); + let r458_bounded: U128AsBounded = upcast(felt252_as_u128(v6832 + SHIFT)); + let (_, r458) = bounded_int_div_rem(r458_bounded, FALCON512_Q_NZ); + let r459_bounded: U128AsBounded = upcast(felt252_as_u128(v6833 + SHIFT)); + let (_, r459) = bounded_int_div_rem(r459_bounded, FALCON512_Q_NZ); + let r460_bounded: U128AsBounded = upcast(felt252_as_u128(v6835 + SHIFT)); + let (_, r460) = bounded_int_div_rem(r460_bounded, FALCON512_Q_NZ); + let r461_bounded: U128AsBounded = upcast(felt252_as_u128(v6836 + SHIFT)); + let (_, r461) = bounded_int_div_rem(r461_bounded, FALCON512_Q_NZ); + let r462_bounded: U128AsBounded = upcast(felt252_as_u128(v6838 + SHIFT)); + let (_, r462) = bounded_int_div_rem(r462_bounded, FALCON512_Q_NZ); + let r463_bounded: U128AsBounded = upcast(felt252_as_u128(v6839 + SHIFT)); + let (_, r463) = bounded_int_div_rem(r463_bounded, FALCON512_Q_NZ); + let r464_bounded: U128AsBounded = upcast(felt252_as_u128(v6841 + SHIFT)); + let (_, r464) = bounded_int_div_rem(r464_bounded, FALCON512_Q_NZ); + let r465_bounded: U128AsBounded = upcast(felt252_as_u128(v6842 + SHIFT)); + let (_, r465) = bounded_int_div_rem(r465_bounded, FALCON512_Q_NZ); + let r466_bounded: U128AsBounded = upcast(felt252_as_u128(v6844 + SHIFT)); + let (_, r466) = bounded_int_div_rem(r466_bounded, FALCON512_Q_NZ); + let r467_bounded: U128AsBounded = upcast(felt252_as_u128(v6845 + SHIFT)); + let (_, r467) = bounded_int_div_rem(r467_bounded, FALCON512_Q_NZ); + let r468_bounded: U128AsBounded = upcast(felt252_as_u128(v6847 + SHIFT)); + let (_, r468) = bounded_int_div_rem(r468_bounded, FALCON512_Q_NZ); + let r469_bounded: U128AsBounded = upcast(felt252_as_u128(v6848 + SHIFT)); + let (_, r469) = bounded_int_div_rem(r469_bounded, FALCON512_Q_NZ); + let r470_bounded: U128AsBounded = upcast(felt252_as_u128(v6850 + SHIFT)); + let (_, r470) = bounded_int_div_rem(r470_bounded, FALCON512_Q_NZ); + let r471_bounded: U128AsBounded = upcast(felt252_as_u128(v6851 + SHIFT)); + let (_, r471) = bounded_int_div_rem(r471_bounded, FALCON512_Q_NZ); + let r472_bounded: U128AsBounded = upcast(felt252_as_u128(v6853 + SHIFT)); + let (_, r472) = bounded_int_div_rem(r472_bounded, FALCON512_Q_NZ); + let r473_bounded: U128AsBounded = upcast(felt252_as_u128(v6854 + SHIFT)); + let (_, r473) = bounded_int_div_rem(r473_bounded, FALCON512_Q_NZ); + let r474_bounded: U128AsBounded = upcast(felt252_as_u128(v6856 + SHIFT)); + let (_, r474) = bounded_int_div_rem(r474_bounded, FALCON512_Q_NZ); + let r475_bounded: U128AsBounded = upcast(felt252_as_u128(v6857 + SHIFT)); + let (_, r475) = bounded_int_div_rem(r475_bounded, FALCON512_Q_NZ); + let r476_bounded: U128AsBounded = upcast(felt252_as_u128(v6859 + SHIFT)); + let (_, r476) = bounded_int_div_rem(r476_bounded, FALCON512_Q_NZ); + let r477_bounded: U128AsBounded = upcast(felt252_as_u128(v6860 + SHIFT)); + let (_, r477) = bounded_int_div_rem(r477_bounded, FALCON512_Q_NZ); + let r478_bounded: U128AsBounded = upcast(felt252_as_u128(v6862 + SHIFT)); + let (_, r478) = bounded_int_div_rem(r478_bounded, FALCON512_Q_NZ); + let r479_bounded: U128AsBounded = upcast(felt252_as_u128(v6863 + SHIFT)); + let (_, r479) = bounded_int_div_rem(r479_bounded, FALCON512_Q_NZ); + let r480_bounded: U128AsBounded = upcast(felt252_as_u128(v6865 + SHIFT)); + let (_, r480) = bounded_int_div_rem(r480_bounded, FALCON512_Q_NZ); + let r481_bounded: U128AsBounded = upcast(felt252_as_u128(v6866 + SHIFT)); + let (_, r481) = bounded_int_div_rem(r481_bounded, FALCON512_Q_NZ); + let r482_bounded: U128AsBounded = upcast(felt252_as_u128(v6868 + SHIFT)); + let (_, r482) = bounded_int_div_rem(r482_bounded, FALCON512_Q_NZ); + let r483_bounded: U128AsBounded = upcast(felt252_as_u128(v6869 + SHIFT)); + let (_, r483) = bounded_int_div_rem(r483_bounded, FALCON512_Q_NZ); + let r484_bounded: U128AsBounded = upcast(felt252_as_u128(v6871 + SHIFT)); + let (_, r484) = bounded_int_div_rem(r484_bounded, FALCON512_Q_NZ); + let r485_bounded: U128AsBounded = upcast(felt252_as_u128(v6872 + SHIFT)); + let (_, r485) = bounded_int_div_rem(r485_bounded, FALCON512_Q_NZ); + let r486_bounded: U128AsBounded = upcast(felt252_as_u128(v6874 + SHIFT)); + let (_, r486) = bounded_int_div_rem(r486_bounded, FALCON512_Q_NZ); + let r487_bounded: U128AsBounded = upcast(felt252_as_u128(v6875 + SHIFT)); + let (_, r487) = bounded_int_div_rem(r487_bounded, FALCON512_Q_NZ); + let r488_bounded: U128AsBounded = upcast(felt252_as_u128(v6877 + SHIFT)); + let (_, r488) = bounded_int_div_rem(r488_bounded, FALCON512_Q_NZ); + let r489_bounded: U128AsBounded = upcast(felt252_as_u128(v6878 + SHIFT)); + let (_, r489) = bounded_int_div_rem(r489_bounded, FALCON512_Q_NZ); + let r490_bounded: U128AsBounded = upcast(felt252_as_u128(v6880 + SHIFT)); + let (_, r490) = bounded_int_div_rem(r490_bounded, FALCON512_Q_NZ); + let r491_bounded: U128AsBounded = upcast(felt252_as_u128(v6881 + SHIFT)); + let (_, r491) = bounded_int_div_rem(r491_bounded, FALCON512_Q_NZ); + let r492_bounded: U128AsBounded = upcast(felt252_as_u128(v6883 + SHIFT)); + let (_, r492) = bounded_int_div_rem(r492_bounded, FALCON512_Q_NZ); + let r493_bounded: U128AsBounded = upcast(felt252_as_u128(v6884 + SHIFT)); + let (_, r493) = bounded_int_div_rem(r493_bounded, FALCON512_Q_NZ); + let r494_bounded: U128AsBounded = upcast(felt252_as_u128(v6886 + SHIFT)); + let (_, r494) = bounded_int_div_rem(r494_bounded, FALCON512_Q_NZ); + let r495_bounded: U128AsBounded = upcast(felt252_as_u128(v6887 + SHIFT)); + let (_, r495) = bounded_int_div_rem(r495_bounded, FALCON512_Q_NZ); + let r496_bounded: U128AsBounded = upcast(felt252_as_u128(v6889 + SHIFT)); + let (_, r496) = bounded_int_div_rem(r496_bounded, FALCON512_Q_NZ); + let r497_bounded: U128AsBounded = upcast(felt252_as_u128(v6890 + SHIFT)); + let (_, r497) = bounded_int_div_rem(r497_bounded, FALCON512_Q_NZ); + let r498_bounded: U128AsBounded = upcast(felt252_as_u128(v6892 + SHIFT)); + let (_, r498) = bounded_int_div_rem(r498_bounded, FALCON512_Q_NZ); + let r499_bounded: U128AsBounded = upcast(felt252_as_u128(v6893 + SHIFT)); + let (_, r499) = bounded_int_div_rem(r499_bounded, FALCON512_Q_NZ); + let r500_bounded: U128AsBounded = upcast(felt252_as_u128(v6895 + SHIFT)); + let (_, r500) = bounded_int_div_rem(r500_bounded, FALCON512_Q_NZ); + let r501_bounded: U128AsBounded = upcast(felt252_as_u128(v6896 + SHIFT)); + let (_, r501) = bounded_int_div_rem(r501_bounded, FALCON512_Q_NZ); + let r502_bounded: U128AsBounded = upcast(felt252_as_u128(v6898 + SHIFT)); + let (_, r502) = bounded_int_div_rem(r502_bounded, FALCON512_Q_NZ); + let r503_bounded: U128AsBounded = upcast(felt252_as_u128(v6899 + SHIFT)); + let (_, r503) = bounded_int_div_rem(r503_bounded, FALCON512_Q_NZ); + let r504_bounded: U128AsBounded = upcast(felt252_as_u128(v6901 + SHIFT)); + let (_, r504) = bounded_int_div_rem(r504_bounded, FALCON512_Q_NZ); + let r505_bounded: U128AsBounded = upcast(felt252_as_u128(v6902 + SHIFT)); + let (_, r505) = bounded_int_div_rem(r505_bounded, FALCON512_Q_NZ); + let r506_bounded: U128AsBounded = upcast(felt252_as_u128(v6904 + SHIFT)); + let (_, r506) = bounded_int_div_rem(r506_bounded, FALCON512_Q_NZ); + let r507_bounded: U128AsBounded = upcast(felt252_as_u128(v6905 + SHIFT)); + let (_, r507) = bounded_int_div_rem(r507_bounded, FALCON512_Q_NZ); + let r508_bounded: U128AsBounded = upcast(felt252_as_u128(v6907 + SHIFT)); + let (_, r508) = bounded_int_div_rem(r508_bounded, FALCON512_Q_NZ); + let r509_bounded: U128AsBounded = upcast(felt252_as_u128(v6908 + SHIFT)); + let (_, r509) = bounded_int_div_rem(r509_bounded, FALCON512_Q_NZ); + let r510_bounded: U128AsBounded = upcast(felt252_as_u128(v6910 + SHIFT)); + let (_, r510) = bounded_int_div_rem(r510_bounded, FALCON512_Q_NZ); + let r511_bounded: U128AsBounded = upcast(felt252_as_u128(v6911 + SHIFT)); + let (_, r511) = bounded_int_div_rem(r511_bounded, FALCON512_Q_NZ); + ( + r0, + r1, + r2, + r3, + r4, + r5, + r6, + r7, + r8, + r9, + r10, + r11, + r12, + r13, + r14, + r15, + r16, + r17, + r18, + r19, + r20, + r21, + r22, + r23, + r24, + r25, + r26, + r27, + r28, + r29, + r30, + r31, + r32, + r33, + r34, + r35, + r36, + r37, + r38, + r39, + r40, + r41, + r42, + r43, + r44, + r45, + r46, + r47, + r48, + r49, + r50, + r51, + r52, + r53, + r54, + r55, + r56, + r57, + r58, + r59, + r60, + r61, + r62, + r63, + r64, + r65, + r66, + r67, + r68, + r69, + r70, + r71, + r72, + r73, + r74, + r75, + r76, + r77, + r78, + r79, + r80, + r81, + r82, + r83, + r84, + r85, + r86, + r87, + r88, + r89, + r90, + r91, + r92, + r93, + r94, + r95, + r96, + r97, + r98, + r99, + r100, + r101, + r102, + r103, + r104, + r105, + r106, + r107, + r108, + r109, + r110, + r111, + r112, + r113, + r114, + r115, + r116, + r117, + r118, + r119, + r120, + r121, + r122, + r123, + r124, + r125, + r126, + r127, + r128, + r129, + r130, + r131, + r132, + r133, + r134, + r135, + r136, + r137, + r138, + r139, + r140, + r141, + r142, + r143, + r144, + r145, + r146, + r147, + r148, + r149, + r150, + r151, + r152, + r153, + r154, + r155, + r156, + r157, + r158, + r159, + r160, + r161, + r162, + r163, + r164, + r165, + r166, + r167, + r168, + r169, + r170, + r171, + r172, + r173, + r174, + r175, + r176, + r177, + r178, + r179, + r180, + r181, + r182, + r183, + r184, + r185, + r186, + r187, + r188, + r189, + r190, + r191, + r192, + r193, + r194, + r195, + r196, + r197, + r198, + r199, + r200, + r201, + r202, + r203, + r204, + r205, + r206, + r207, + r208, + r209, + r210, + r211, + r212, + r213, + r214, + r215, + r216, + r217, + r218, + r219, + r220, + r221, + r222, + r223, + r224, + r225, + r226, + r227, + r228, + r229, + r230, + r231, + r232, + r233, + r234, + r235, + r236, + r237, + r238, + r239, + r240, + r241, + r242, + r243, + r244, + r245, + r246, + r247, + r248, + r249, + r250, + r251, + r252, + r253, + r254, + r255, + r256, + r257, + r258, + r259, + r260, + r261, + r262, + r263, + r264, + r265, + r266, + r267, + r268, + r269, + r270, + r271, + r272, + r273, + r274, + r275, + r276, + r277, + r278, + r279, + r280, + r281, + r282, + r283, + r284, + r285, + r286, + r287, + r288, + r289, + r290, + r291, + r292, + r293, + r294, + r295, + r296, + r297, + r298, + r299, + r300, + r301, + r302, + r303, + r304, + r305, + r306, + r307, + r308, + r309, + r310, + r311, + r312, + r313, + r314, + r315, + r316, + r317, + r318, + r319, + r320, + r321, + r322, + r323, + r324, + r325, + r326, + r327, + r328, + r329, + r330, + r331, + r332, + r333, + r334, + r335, + r336, + r337, + r338, + r339, + r340, + r341, + r342, + r343, + r344, + r345, + r346, + r347, + r348, + r349, + r350, + r351, + r352, + r353, + r354, + r355, + r356, + r357, + r358, + r359, + r360, + r361, + r362, + r363, + r364, + r365, + r366, + r367, + r368, + r369, + r370, + r371, + r372, + r373, + r374, + r375, + r376, + r377, + r378, + r379, + r380, + r381, + r382, + r383, + r384, + r385, + r386, + r387, + r388, + r389, + r390, + r391, + r392, + r393, + r394, + r395, + r396, + r397, + r398, + r399, + r400, + r401, + r402, + r403, + r404, + r405, + r406, + r407, + r408, + r409, + r410, + r411, + r412, + r413, + r414, + r415, + r416, + r417, + r418, + r419, + r420, + r421, + r422, + r423, + r424, + r425, + r426, + r427, + r428, + r429, + r430, + r431, + r432, + r433, + r434, + r435, + r436, + r437, + r438, + r439, + r440, + r441, + r442, + r443, + r444, + r445, + r446, + r447, + r448, + r449, + r450, + r451, + r452, + r453, + r454, + r455, + r456, + r457, + r458, + r459, + r460, + r461, + r462, + r463, + r464, + r465, + r466, + r467, + r468, + r469, + r470, + r471, + r472, + r473, + r474, + r475, + r476, + r477, + r478, + r479, + r480, + r481, + r482, + r483, + r484, + r485, + r486, + r487, + r488, + r489, + r490, + r491, + r492, + r493, + r494, + r495, + r496, + r497, + r498, + r499, + r500, + r501, + r502, + r503, + r504, + r505, + r506, + r507, + r508, + r509, + r510, + r511, + ) +} + +/// Compute the Falcon-512 forward NTT for 512 reduced coefficients without +/// validating each coefficient. +/// +/// Inputs must be in `[0, 12289)`; violating that precondition may return an +/// invalid transform. Outputs are in the Falcon/falcon.py evaluation order and +/// reduced to the same range. +pub fn ntt_falcon512_fast_unchecked(mut f: Span) -> Array { + assert(f.len() == 512, 'fast NTT: bad length'); + let boxed = f.multi_pop_front::<512>().unwrap(); + let [ + f0, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + f18, + f19, + f20, + f21, + f22, + f23, + f24, + f25, + f26, + f27, + f28, + f29, + f30, + f31, + f32, + f33, + f34, + f35, + f36, + f37, + f38, + f39, + f40, + f41, + f42, + f43, + f44, + f45, + f46, + f47, + f48, + f49, + f50, + f51, + f52, + f53, + f54, + f55, + f56, + f57, + f58, + f59, + f60, + f61, + f62, + f63, + f64, + f65, + f66, + f67, + f68, + f69, + f70, + f71, + f72, + f73, + f74, + f75, + f76, + f77, + f78, + f79, + f80, + f81, + f82, + f83, + f84, + f85, + f86, + f87, + f88, + f89, + f90, + f91, + f92, + f93, + f94, + f95, + f96, + f97, + f98, + f99, + f100, + f101, + f102, + f103, + f104, + f105, + f106, + f107, + f108, + f109, + f110, + f111, + f112, + f113, + f114, + f115, + f116, + f117, + f118, + f119, + f120, + f121, + f122, + f123, + f124, + f125, + f126, + f127, + f128, + f129, + f130, + f131, + f132, + f133, + f134, + f135, + f136, + f137, + f138, + f139, + f140, + f141, + f142, + f143, + f144, + f145, + f146, + f147, + f148, + f149, + f150, + f151, + f152, + f153, + f154, + f155, + f156, + f157, + f158, + f159, + f160, + f161, + f162, + f163, + f164, + f165, + f166, + f167, + f168, + f169, + f170, + f171, + f172, + f173, + f174, + f175, + f176, + f177, + f178, + f179, + f180, + f181, + f182, + f183, + f184, + f185, + f186, + f187, + f188, + f189, + f190, + f191, + f192, + f193, + f194, + f195, + f196, + f197, + f198, + f199, + f200, + f201, + f202, + f203, + f204, + f205, + f206, + f207, + f208, + f209, + f210, + f211, + f212, + f213, + f214, + f215, + f216, + f217, + f218, + f219, + f220, + f221, + f222, + f223, + f224, + f225, + f226, + f227, + f228, + f229, + f230, + f231, + f232, + f233, + f234, + f235, + f236, + f237, + f238, + f239, + f240, + f241, + f242, + f243, + f244, + f245, + f246, + f247, + f248, + f249, + f250, + f251, + f252, + f253, + f254, + f255, + f256, + f257, + f258, + f259, + f260, + f261, + f262, + f263, + f264, + f265, + f266, + f267, + f268, + f269, + f270, + f271, + f272, + f273, + f274, + f275, + f276, + f277, + f278, + f279, + f280, + f281, + f282, + f283, + f284, + f285, + f286, + f287, + f288, + f289, + f290, + f291, + f292, + f293, + f294, + f295, + f296, + f297, + f298, + f299, + f300, + f301, + f302, + f303, + f304, + f305, + f306, + f307, + f308, + f309, + f310, + f311, + f312, + f313, + f314, + f315, + f316, + f317, + f318, + f319, + f320, + f321, + f322, + f323, + f324, + f325, + f326, + f327, + f328, + f329, + f330, + f331, + f332, + f333, + f334, + f335, + f336, + f337, + f338, + f339, + f340, + f341, + f342, + f343, + f344, + f345, + f346, + f347, + f348, + f349, + f350, + f351, + f352, + f353, + f354, + f355, + f356, + f357, + f358, + f359, + f360, + f361, + f362, + f363, + f364, + f365, + f366, + f367, + f368, + f369, + f370, + f371, + f372, + f373, + f374, + f375, + f376, + f377, + f378, + f379, + f380, + f381, + f382, + f383, + f384, + f385, + f386, + f387, + f388, + f389, + f390, + f391, + f392, + f393, + f394, + f395, + f396, + f397, + f398, + f399, + f400, + f401, + f402, + f403, + f404, + f405, + f406, + f407, + f408, + f409, + f410, + f411, + f412, + f413, + f414, + f415, + f416, + f417, + f418, + f419, + f420, + f421, + f422, + f423, + f424, + f425, + f426, + f427, + f428, + f429, + f430, + f431, + f432, + f433, + f434, + f435, + f436, + f437, + f438, + f439, + f440, + f441, + f442, + f443, + f444, + f445, + f446, + f447, + f448, + f449, + f450, + f451, + f452, + f453, + f454, + f455, + f456, + f457, + f458, + f459, + f460, + f461, + f462, + f463, + f464, + f465, + f466, + f467, + f468, + f469, + f470, + f471, + f472, + f473, + f474, + f475, + f476, + f477, + f478, + f479, + f480, + f481, + f482, + f483, + f484, + f485, + f486, + f487, + f488, + f489, + f490, + f491, + f492, + f493, + f494, + f495, + f496, + f497, + f498, + f499, + f500, + f501, + f502, + f503, + f504, + f505, + f506, + f507, + f508, + f509, + f510, + f511, + ] = + boxed + .unbox(); + let ( + r0, + r1, + r2, + r3, + r4, + r5, + r6, + r7, + r8, + r9, + r10, + r11, + r12, + r13, + r14, + r15, + r16, + r17, + r18, + r19, + r20, + r21, + r22, + r23, + r24, + r25, + r26, + r27, + r28, + r29, + r30, + r31, + r32, + r33, + r34, + r35, + r36, + r37, + r38, + r39, + r40, + r41, + r42, + r43, + r44, + r45, + r46, + r47, + r48, + r49, + r50, + r51, + r52, + r53, + r54, + r55, + r56, + r57, + r58, + r59, + r60, + r61, + r62, + r63, + r64, + r65, + r66, + r67, + r68, + r69, + r70, + r71, + r72, + r73, + r74, + r75, + r76, + r77, + r78, + r79, + r80, + r81, + r82, + r83, + r84, + r85, + r86, + r87, + r88, + r89, + r90, + r91, + r92, + r93, + r94, + r95, + r96, + r97, + r98, + r99, + r100, + r101, + r102, + r103, + r104, + r105, + r106, + r107, + r108, + r109, + r110, + r111, + r112, + r113, + r114, + r115, + r116, + r117, + r118, + r119, + r120, + r121, + r122, + r123, + r124, + r125, + r126, + r127, + r128, + r129, + r130, + r131, + r132, + r133, + r134, + r135, + r136, + r137, + r138, + r139, + r140, + r141, + r142, + r143, + r144, + r145, + r146, + r147, + r148, + r149, + r150, + r151, + r152, + r153, + r154, + r155, + r156, + r157, + r158, + r159, + r160, + r161, + r162, + r163, + r164, + r165, + r166, + r167, + r168, + r169, + r170, + r171, + r172, + r173, + r174, + r175, + r176, + r177, + r178, + r179, + r180, + r181, + r182, + r183, + r184, + r185, + r186, + r187, + r188, + r189, + r190, + r191, + r192, + r193, + r194, + r195, + r196, + r197, + r198, + r199, + r200, + r201, + r202, + r203, + r204, + r205, + r206, + r207, + r208, + r209, + r210, + r211, + r212, + r213, + r214, + r215, + r216, + r217, + r218, + r219, + r220, + r221, + r222, + r223, + r224, + r225, + r226, + r227, + r228, + r229, + r230, + r231, + r232, + r233, + r234, + r235, + r236, + r237, + r238, + r239, + r240, + r241, + r242, + r243, + r244, + r245, + r246, + r247, + r248, + r249, + r250, + r251, + r252, + r253, + r254, + r255, + r256, + r257, + r258, + r259, + r260, + r261, + r262, + r263, + r264, + r265, + r266, + r267, + r268, + r269, + r270, + r271, + r272, + r273, + r274, + r275, + r276, + r277, + r278, + r279, + r280, + r281, + r282, + r283, + r284, + r285, + r286, + r287, + r288, + r289, + r290, + r291, + r292, + r293, + r294, + r295, + r296, + r297, + r298, + r299, + r300, + r301, + r302, + r303, + r304, + r305, + r306, + r307, + r308, + r309, + r310, + r311, + r312, + r313, + r314, + r315, + r316, + r317, + r318, + r319, + r320, + r321, + r322, + r323, + r324, + r325, + r326, + r327, + r328, + r329, + r330, + r331, + r332, + r333, + r334, + r335, + r336, + r337, + r338, + r339, + r340, + r341, + r342, + r343, + r344, + r345, + r346, + r347, + r348, + r349, + r350, + r351, + r352, + r353, + r354, + r355, + r356, + r357, + r358, + r359, + r360, + r361, + r362, + r363, + r364, + r365, + r366, + r367, + r368, + r369, + r370, + r371, + r372, + r373, + r374, + r375, + r376, + r377, + r378, + r379, + r380, + r381, + r382, + r383, + r384, + r385, + r386, + r387, + r388, + r389, + r390, + r391, + r392, + r393, + r394, + r395, + r396, + r397, + r398, + r399, + r400, + r401, + r402, + r403, + r404, + r405, + r406, + r407, + r408, + r409, + r410, + r411, + r412, + r413, + r414, + r415, + r416, + r417, + r418, + r419, + r420, + r421, + r422, + r423, + r424, + r425, + r426, + r427, + r428, + r429, + r430, + r431, + r432, + r433, + r434, + r435, + r436, + r437, + r438, + r439, + r440, + r441, + r442, + r443, + r444, + r445, + r446, + r447, + r448, + r449, + r450, + r451, + r452, + r453, + r454, + r455, + r456, + r457, + r458, + r459, + r460, + r461, + r462, + r463, + r464, + r465, + r466, + r467, + r468, + r469, + r470, + r471, + r472, + r473, + r474, + r475, + r476, + r477, + r478, + r479, + r480, + r481, + r482, + r483, + r484, + r485, + r486, + r487, + r488, + r489, + r490, + r491, + r492, + r493, + r494, + r495, + r496, + r497, + r498, + r499, + r500, + r501, + r502, + r503, + r504, + r505, + r506, + r507, + r508, + r509, + r510, + r511, + ) = + ntt_falcon512_fast_inner( + f0, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + f18, + f19, + f20, + f21, + f22, + f23, + f24, + f25, + f26, + f27, + f28, + f29, + f30, + f31, + f32, + f33, + f34, + f35, + f36, + f37, + f38, + f39, + f40, + f41, + f42, + f43, + f44, + f45, + f46, + f47, + f48, + f49, + f50, + f51, + f52, + f53, + f54, + f55, + f56, + f57, + f58, + f59, + f60, + f61, + f62, + f63, + f64, + f65, + f66, + f67, + f68, + f69, + f70, + f71, + f72, + f73, + f74, + f75, + f76, + f77, + f78, + f79, + f80, + f81, + f82, + f83, + f84, + f85, + f86, + f87, + f88, + f89, + f90, + f91, + f92, + f93, + f94, + f95, + f96, + f97, + f98, + f99, + f100, + f101, + f102, + f103, + f104, + f105, + f106, + f107, + f108, + f109, + f110, + f111, + f112, + f113, + f114, + f115, + f116, + f117, + f118, + f119, + f120, + f121, + f122, + f123, + f124, + f125, + f126, + f127, + f128, + f129, + f130, + f131, + f132, + f133, + f134, + f135, + f136, + f137, + f138, + f139, + f140, + f141, + f142, + f143, + f144, + f145, + f146, + f147, + f148, + f149, + f150, + f151, + f152, + f153, + f154, + f155, + f156, + f157, + f158, + f159, + f160, + f161, + f162, + f163, + f164, + f165, + f166, + f167, + f168, + f169, + f170, + f171, + f172, + f173, + f174, + f175, + f176, + f177, + f178, + f179, + f180, + f181, + f182, + f183, + f184, + f185, + f186, + f187, + f188, + f189, + f190, + f191, + f192, + f193, + f194, + f195, + f196, + f197, + f198, + f199, + f200, + f201, + f202, + f203, + f204, + f205, + f206, + f207, + f208, + f209, + f210, + f211, + f212, + f213, + f214, + f215, + f216, + f217, + f218, + f219, + f220, + f221, + f222, + f223, + f224, + f225, + f226, + f227, + f228, + f229, + f230, + f231, + f232, + f233, + f234, + f235, + f236, + f237, + f238, + f239, + f240, + f241, + f242, + f243, + f244, + f245, + f246, + f247, + f248, + f249, + f250, + f251, + f252, + f253, + f254, + f255, + f256, + f257, + f258, + f259, + f260, + f261, + f262, + f263, + f264, + f265, + f266, + f267, + f268, + f269, + f270, + f271, + f272, + f273, + f274, + f275, + f276, + f277, + f278, + f279, + f280, + f281, + f282, + f283, + f284, + f285, + f286, + f287, + f288, + f289, + f290, + f291, + f292, + f293, + f294, + f295, + f296, + f297, + f298, + f299, + f300, + f301, + f302, + f303, + f304, + f305, + f306, + f307, + f308, + f309, + f310, + f311, + f312, + f313, + f314, + f315, + f316, + f317, + f318, + f319, + f320, + f321, + f322, + f323, + f324, + f325, + f326, + f327, + f328, + f329, + f330, + f331, + f332, + f333, + f334, + f335, + f336, + f337, + f338, + f339, + f340, + f341, + f342, + f343, + f344, + f345, + f346, + f347, + f348, + f349, + f350, + f351, + f352, + f353, + f354, + f355, + f356, + f357, + f358, + f359, + f360, + f361, + f362, + f363, + f364, + f365, + f366, + f367, + f368, + f369, + f370, + f371, + f372, + f373, + f374, + f375, + f376, + f377, + f378, + f379, + f380, + f381, + f382, + f383, + f384, + f385, + f386, + f387, + f388, + f389, + f390, + f391, + f392, + f393, + f394, + f395, + f396, + f397, + f398, + f399, + f400, + f401, + f402, + f403, + f404, + f405, + f406, + f407, + f408, + f409, + f410, + f411, + f412, + f413, + f414, + f415, + f416, + f417, + f418, + f419, + f420, + f421, + f422, + f423, + f424, + f425, + f426, + f427, + f428, + f429, + f430, + f431, + f432, + f433, + f434, + f435, + f436, + f437, + f438, + f439, + f440, + f441, + f442, + f443, + f444, + f445, + f446, + f447, + f448, + f449, + f450, + f451, + f452, + f453, + f454, + f455, + f456, + f457, + f458, + f459, + f460, + f461, + f462, + f463, + f464, + f465, + f466, + f467, + f468, + f469, + f470, + f471, + f472, + f473, + f474, + f475, + f476, + f477, + f478, + f479, + f480, + f481, + f482, + f483, + f484, + f485, + f486, + f487, + f488, + f489, + f490, + f491, + f492, + f493, + f494, + f495, + f496, + f497, + f498, + f499, + f500, + f501, + f502, + f503, + f504, + f505, + f506, + f507, + f508, + f509, + f510, + f511, + ); + array![ + upcast(r0), upcast(r1), upcast(r2), upcast(r3), upcast(r4), upcast(r5), upcast(r6), + upcast(r7), upcast(r8), upcast(r9), upcast(r10), upcast(r11), upcast(r12), upcast(r13), + upcast(r14), upcast(r15), upcast(r16), upcast(r17), upcast(r18), upcast(r19), upcast(r20), + upcast(r21), upcast(r22), upcast(r23), upcast(r24), upcast(r25), upcast(r26), upcast(r27), + upcast(r28), upcast(r29), upcast(r30), upcast(r31), upcast(r32), upcast(r33), upcast(r34), + upcast(r35), upcast(r36), upcast(r37), upcast(r38), upcast(r39), upcast(r40), upcast(r41), + upcast(r42), upcast(r43), upcast(r44), upcast(r45), upcast(r46), upcast(r47), upcast(r48), + upcast(r49), upcast(r50), upcast(r51), upcast(r52), upcast(r53), upcast(r54), upcast(r55), + upcast(r56), upcast(r57), upcast(r58), upcast(r59), upcast(r60), upcast(r61), upcast(r62), + upcast(r63), upcast(r64), upcast(r65), upcast(r66), upcast(r67), upcast(r68), upcast(r69), + upcast(r70), upcast(r71), upcast(r72), upcast(r73), upcast(r74), upcast(r75), upcast(r76), + upcast(r77), upcast(r78), upcast(r79), upcast(r80), upcast(r81), upcast(r82), upcast(r83), + upcast(r84), upcast(r85), upcast(r86), upcast(r87), upcast(r88), upcast(r89), upcast(r90), + upcast(r91), upcast(r92), upcast(r93), upcast(r94), upcast(r95), upcast(r96), upcast(r97), + upcast(r98), upcast(r99), upcast(r100), upcast(r101), upcast(r102), upcast(r103), + upcast(r104), upcast(r105), upcast(r106), upcast(r107), upcast(r108), upcast(r109), + upcast(r110), upcast(r111), upcast(r112), upcast(r113), upcast(r114), upcast(r115), + upcast(r116), upcast(r117), upcast(r118), upcast(r119), upcast(r120), upcast(r121), + upcast(r122), upcast(r123), upcast(r124), upcast(r125), upcast(r126), upcast(r127), + upcast(r128), upcast(r129), upcast(r130), upcast(r131), upcast(r132), upcast(r133), + upcast(r134), upcast(r135), upcast(r136), upcast(r137), upcast(r138), upcast(r139), + upcast(r140), upcast(r141), upcast(r142), upcast(r143), upcast(r144), upcast(r145), + upcast(r146), upcast(r147), upcast(r148), upcast(r149), upcast(r150), upcast(r151), + upcast(r152), upcast(r153), upcast(r154), upcast(r155), upcast(r156), upcast(r157), + upcast(r158), upcast(r159), upcast(r160), upcast(r161), upcast(r162), upcast(r163), + upcast(r164), upcast(r165), upcast(r166), upcast(r167), upcast(r168), upcast(r169), + upcast(r170), upcast(r171), upcast(r172), upcast(r173), upcast(r174), upcast(r175), + upcast(r176), upcast(r177), upcast(r178), upcast(r179), upcast(r180), upcast(r181), + upcast(r182), upcast(r183), upcast(r184), upcast(r185), upcast(r186), upcast(r187), + upcast(r188), upcast(r189), upcast(r190), upcast(r191), upcast(r192), upcast(r193), + upcast(r194), upcast(r195), upcast(r196), upcast(r197), upcast(r198), upcast(r199), + upcast(r200), upcast(r201), upcast(r202), upcast(r203), upcast(r204), upcast(r205), + upcast(r206), upcast(r207), upcast(r208), upcast(r209), upcast(r210), upcast(r211), + upcast(r212), upcast(r213), upcast(r214), upcast(r215), upcast(r216), upcast(r217), + upcast(r218), upcast(r219), upcast(r220), upcast(r221), upcast(r222), upcast(r223), + upcast(r224), upcast(r225), upcast(r226), upcast(r227), upcast(r228), upcast(r229), + upcast(r230), upcast(r231), upcast(r232), upcast(r233), upcast(r234), upcast(r235), + upcast(r236), upcast(r237), upcast(r238), upcast(r239), upcast(r240), upcast(r241), + upcast(r242), upcast(r243), upcast(r244), upcast(r245), upcast(r246), upcast(r247), + upcast(r248), upcast(r249), upcast(r250), upcast(r251), upcast(r252), upcast(r253), + upcast(r254), upcast(r255), upcast(r256), upcast(r257), upcast(r258), upcast(r259), + upcast(r260), upcast(r261), upcast(r262), upcast(r263), upcast(r264), upcast(r265), + upcast(r266), upcast(r267), upcast(r268), upcast(r269), upcast(r270), upcast(r271), + upcast(r272), upcast(r273), upcast(r274), upcast(r275), upcast(r276), upcast(r277), + upcast(r278), upcast(r279), upcast(r280), upcast(r281), upcast(r282), upcast(r283), + upcast(r284), upcast(r285), upcast(r286), upcast(r287), upcast(r288), upcast(r289), + upcast(r290), upcast(r291), upcast(r292), upcast(r293), upcast(r294), upcast(r295), + upcast(r296), upcast(r297), upcast(r298), upcast(r299), upcast(r300), upcast(r301), + upcast(r302), upcast(r303), upcast(r304), upcast(r305), upcast(r306), upcast(r307), + upcast(r308), upcast(r309), upcast(r310), upcast(r311), upcast(r312), upcast(r313), + upcast(r314), upcast(r315), upcast(r316), upcast(r317), upcast(r318), upcast(r319), + upcast(r320), upcast(r321), upcast(r322), upcast(r323), upcast(r324), upcast(r325), + upcast(r326), upcast(r327), upcast(r328), upcast(r329), upcast(r330), upcast(r331), + upcast(r332), upcast(r333), upcast(r334), upcast(r335), upcast(r336), upcast(r337), + upcast(r338), upcast(r339), upcast(r340), upcast(r341), upcast(r342), upcast(r343), + upcast(r344), upcast(r345), upcast(r346), upcast(r347), upcast(r348), upcast(r349), + upcast(r350), upcast(r351), upcast(r352), upcast(r353), upcast(r354), upcast(r355), + upcast(r356), upcast(r357), upcast(r358), upcast(r359), upcast(r360), upcast(r361), + upcast(r362), upcast(r363), upcast(r364), upcast(r365), upcast(r366), upcast(r367), + upcast(r368), upcast(r369), upcast(r370), upcast(r371), upcast(r372), upcast(r373), + upcast(r374), upcast(r375), upcast(r376), upcast(r377), upcast(r378), upcast(r379), + upcast(r380), upcast(r381), upcast(r382), upcast(r383), upcast(r384), upcast(r385), + upcast(r386), upcast(r387), upcast(r388), upcast(r389), upcast(r390), upcast(r391), + upcast(r392), upcast(r393), upcast(r394), upcast(r395), upcast(r396), upcast(r397), + upcast(r398), upcast(r399), upcast(r400), upcast(r401), upcast(r402), upcast(r403), + upcast(r404), upcast(r405), upcast(r406), upcast(r407), upcast(r408), upcast(r409), + upcast(r410), upcast(r411), upcast(r412), upcast(r413), upcast(r414), upcast(r415), + upcast(r416), upcast(r417), upcast(r418), upcast(r419), upcast(r420), upcast(r421), + upcast(r422), upcast(r423), upcast(r424), upcast(r425), upcast(r426), upcast(r427), + upcast(r428), upcast(r429), upcast(r430), upcast(r431), upcast(r432), upcast(r433), + upcast(r434), upcast(r435), upcast(r436), upcast(r437), upcast(r438), upcast(r439), + upcast(r440), upcast(r441), upcast(r442), upcast(r443), upcast(r444), upcast(r445), + upcast(r446), upcast(r447), upcast(r448), upcast(r449), upcast(r450), upcast(r451), + upcast(r452), upcast(r453), upcast(r454), upcast(r455), upcast(r456), upcast(r457), + upcast(r458), upcast(r459), upcast(r460), upcast(r461), upcast(r462), upcast(r463), + upcast(r464), upcast(r465), upcast(r466), upcast(r467), upcast(r468), upcast(r469), + upcast(r470), upcast(r471), upcast(r472), upcast(r473), upcast(r474), upcast(r475), + upcast(r476), upcast(r477), upcast(r478), upcast(r479), upcast(r480), upcast(r481), + upcast(r482), upcast(r483), upcast(r484), upcast(r485), upcast(r486), upcast(r487), + upcast(r488), upcast(r489), upcast(r490), upcast(r491), upcast(r492), upcast(r493), + upcast(r494), upcast(r495), upcast(r496), upcast(r497), upcast(r498), upcast(r499), + upcast(r500), upcast(r501), upcast(r502), upcast(r503), upcast(r504), upcast(r505), + upcast(r506), upcast(r507), upcast(r508), upcast(r509), upcast(r510), upcast(r511), + ] +} + +/// Compute the Falcon-512 forward NTT from canonical `u16` coefficients without +/// validating each coefficient. +/// +/// Inputs must be in `[0, 12289)`; violating that precondition may return an +/// invalid transform. Outputs are in the Falcon/falcon.py evaluation order and +/// reduced to the same range. +pub fn ntt_falcon512_fast_u16_unchecked(mut f: Span) -> Array { + assert(f.len() == 512, 'fast NTT: bad length'); + let boxed = f.multi_pop_front::<512>().unwrap(); + let [ + f0, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + f18, + f19, + f20, + f21, + f22, + f23, + f24, + f25, + f26, + f27, + f28, + f29, + f30, + f31, + f32, + f33, + f34, + f35, + f36, + f37, + f38, + f39, + f40, + f41, + f42, + f43, + f44, + f45, + f46, + f47, + f48, + f49, + f50, + f51, + f52, + f53, + f54, + f55, + f56, + f57, + f58, + f59, + f60, + f61, + f62, + f63, + f64, + f65, + f66, + f67, + f68, + f69, + f70, + f71, + f72, + f73, + f74, + f75, + f76, + f77, + f78, + f79, + f80, + f81, + f82, + f83, + f84, + f85, + f86, + f87, + f88, + f89, + f90, + f91, + f92, + f93, + f94, + f95, + f96, + f97, + f98, + f99, + f100, + f101, + f102, + f103, + f104, + f105, + f106, + f107, + f108, + f109, + f110, + f111, + f112, + f113, + f114, + f115, + f116, + f117, + f118, + f119, + f120, + f121, + f122, + f123, + f124, + f125, + f126, + f127, + f128, + f129, + f130, + f131, + f132, + f133, + f134, + f135, + f136, + f137, + f138, + f139, + f140, + f141, + f142, + f143, + f144, + f145, + f146, + f147, + f148, + f149, + f150, + f151, + f152, + f153, + f154, + f155, + f156, + f157, + f158, + f159, + f160, + f161, + f162, + f163, + f164, + f165, + f166, + f167, + f168, + f169, + f170, + f171, + f172, + f173, + f174, + f175, + f176, + f177, + f178, + f179, + f180, + f181, + f182, + f183, + f184, + f185, + f186, + f187, + f188, + f189, + f190, + f191, + f192, + f193, + f194, + f195, + f196, + f197, + f198, + f199, + f200, + f201, + f202, + f203, + f204, + f205, + f206, + f207, + f208, + f209, + f210, + f211, + f212, + f213, + f214, + f215, + f216, + f217, + f218, + f219, + f220, + f221, + f222, + f223, + f224, + f225, + f226, + f227, + f228, + f229, + f230, + f231, + f232, + f233, + f234, + f235, + f236, + f237, + f238, + f239, + f240, + f241, + f242, + f243, + f244, + f245, + f246, + f247, + f248, + f249, + f250, + f251, + f252, + f253, + f254, + f255, + f256, + f257, + f258, + f259, + f260, + f261, + f262, + f263, + f264, + f265, + f266, + f267, + f268, + f269, + f270, + f271, + f272, + f273, + f274, + f275, + f276, + f277, + f278, + f279, + f280, + f281, + f282, + f283, + f284, + f285, + f286, + f287, + f288, + f289, + f290, + f291, + f292, + f293, + f294, + f295, + f296, + f297, + f298, + f299, + f300, + f301, + f302, + f303, + f304, + f305, + f306, + f307, + f308, + f309, + f310, + f311, + f312, + f313, + f314, + f315, + f316, + f317, + f318, + f319, + f320, + f321, + f322, + f323, + f324, + f325, + f326, + f327, + f328, + f329, + f330, + f331, + f332, + f333, + f334, + f335, + f336, + f337, + f338, + f339, + f340, + f341, + f342, + f343, + f344, + f345, + f346, + f347, + f348, + f349, + f350, + f351, + f352, + f353, + f354, + f355, + f356, + f357, + f358, + f359, + f360, + f361, + f362, + f363, + f364, + f365, + f366, + f367, + f368, + f369, + f370, + f371, + f372, + f373, + f374, + f375, + f376, + f377, + f378, + f379, + f380, + f381, + f382, + f383, + f384, + f385, + f386, + f387, + f388, + f389, + f390, + f391, + f392, + f393, + f394, + f395, + f396, + f397, + f398, + f399, + f400, + f401, + f402, + f403, + f404, + f405, + f406, + f407, + f408, + f409, + f410, + f411, + f412, + f413, + f414, + f415, + f416, + f417, + f418, + f419, + f420, + f421, + f422, + f423, + f424, + f425, + f426, + f427, + f428, + f429, + f430, + f431, + f432, + f433, + f434, + f435, + f436, + f437, + f438, + f439, + f440, + f441, + f442, + f443, + f444, + f445, + f446, + f447, + f448, + f449, + f450, + f451, + f452, + f453, + f454, + f455, + f456, + f457, + f458, + f459, + f460, + f461, + f462, + f463, + f464, + f465, + f466, + f467, + f468, + f469, + f470, + f471, + f472, + f473, + f474, + f475, + f476, + f477, + f478, + f479, + f480, + f481, + f482, + f483, + f484, + f485, + f486, + f487, + f488, + f489, + f490, + f491, + f492, + f493, + f494, + f495, + f496, + f497, + f498, + f499, + f500, + f501, + f502, + f503, + f504, + f505, + f506, + f507, + f508, + f509, + f510, + f511, + ] = + boxed + .unbox(); + let ( + r0, + r1, + r2, + r3, + r4, + r5, + r6, + r7, + r8, + r9, + r10, + r11, + r12, + r13, + r14, + r15, + r16, + r17, + r18, + r19, + r20, + r21, + r22, + r23, + r24, + r25, + r26, + r27, + r28, + r29, + r30, + r31, + r32, + r33, + r34, + r35, + r36, + r37, + r38, + r39, + r40, + r41, + r42, + r43, + r44, + r45, + r46, + r47, + r48, + r49, + r50, + r51, + r52, + r53, + r54, + r55, + r56, + r57, + r58, + r59, + r60, + r61, + r62, + r63, + r64, + r65, + r66, + r67, + r68, + r69, + r70, + r71, + r72, + r73, + r74, + r75, + r76, + r77, + r78, + r79, + r80, + r81, + r82, + r83, + r84, + r85, + r86, + r87, + r88, + r89, + r90, + r91, + r92, + r93, + r94, + r95, + r96, + r97, + r98, + r99, + r100, + r101, + r102, + r103, + r104, + r105, + r106, + r107, + r108, + r109, + r110, + r111, + r112, + r113, + r114, + r115, + r116, + r117, + r118, + r119, + r120, + r121, + r122, + r123, + r124, + r125, + r126, + r127, + r128, + r129, + r130, + r131, + r132, + r133, + r134, + r135, + r136, + r137, + r138, + r139, + r140, + r141, + r142, + r143, + r144, + r145, + r146, + r147, + r148, + r149, + r150, + r151, + r152, + r153, + r154, + r155, + r156, + r157, + r158, + r159, + r160, + r161, + r162, + r163, + r164, + r165, + r166, + r167, + r168, + r169, + r170, + r171, + r172, + r173, + r174, + r175, + r176, + r177, + r178, + r179, + r180, + r181, + r182, + r183, + r184, + r185, + r186, + r187, + r188, + r189, + r190, + r191, + r192, + r193, + r194, + r195, + r196, + r197, + r198, + r199, + r200, + r201, + r202, + r203, + r204, + r205, + r206, + r207, + r208, + r209, + r210, + r211, + r212, + r213, + r214, + r215, + r216, + r217, + r218, + r219, + r220, + r221, + r222, + r223, + r224, + r225, + r226, + r227, + r228, + r229, + r230, + r231, + r232, + r233, + r234, + r235, + r236, + r237, + r238, + r239, + r240, + r241, + r242, + r243, + r244, + r245, + r246, + r247, + r248, + r249, + r250, + r251, + r252, + r253, + r254, + r255, + r256, + r257, + r258, + r259, + r260, + r261, + r262, + r263, + r264, + r265, + r266, + r267, + r268, + r269, + r270, + r271, + r272, + r273, + r274, + r275, + r276, + r277, + r278, + r279, + r280, + r281, + r282, + r283, + r284, + r285, + r286, + r287, + r288, + r289, + r290, + r291, + r292, + r293, + r294, + r295, + r296, + r297, + r298, + r299, + r300, + r301, + r302, + r303, + r304, + r305, + r306, + r307, + r308, + r309, + r310, + r311, + r312, + r313, + r314, + r315, + r316, + r317, + r318, + r319, + r320, + r321, + r322, + r323, + r324, + r325, + r326, + r327, + r328, + r329, + r330, + r331, + r332, + r333, + r334, + r335, + r336, + r337, + r338, + r339, + r340, + r341, + r342, + r343, + r344, + r345, + r346, + r347, + r348, + r349, + r350, + r351, + r352, + r353, + r354, + r355, + r356, + r357, + r358, + r359, + r360, + r361, + r362, + r363, + r364, + r365, + r366, + r367, + r368, + r369, + r370, + r371, + r372, + r373, + r374, + r375, + r376, + r377, + r378, + r379, + r380, + r381, + r382, + r383, + r384, + r385, + r386, + r387, + r388, + r389, + r390, + r391, + r392, + r393, + r394, + r395, + r396, + r397, + r398, + r399, + r400, + r401, + r402, + r403, + r404, + r405, + r406, + r407, + r408, + r409, + r410, + r411, + r412, + r413, + r414, + r415, + r416, + r417, + r418, + r419, + r420, + r421, + r422, + r423, + r424, + r425, + r426, + r427, + r428, + r429, + r430, + r431, + r432, + r433, + r434, + r435, + r436, + r437, + r438, + r439, + r440, + r441, + r442, + r443, + r444, + r445, + r446, + r447, + r448, + r449, + r450, + r451, + r452, + r453, + r454, + r455, + r456, + r457, + r458, + r459, + r460, + r461, + r462, + r463, + r464, + r465, + r466, + r467, + r468, + r469, + r470, + r471, + r472, + r473, + r474, + r475, + r476, + r477, + r478, + r479, + r480, + r481, + r482, + r483, + r484, + r485, + r486, + r487, + r488, + r489, + r490, + r491, + r492, + r493, + r494, + r495, + r496, + r497, + r498, + r499, + r500, + r501, + r502, + r503, + r504, + r505, + r506, + r507, + r508, + r509, + r510, + r511, + ) = + ntt_falcon512_fast_inner( + f0.into(), + f1.into(), + f2.into(), + f3.into(), + f4.into(), + f5.into(), + f6.into(), + f7.into(), + f8.into(), + f9.into(), + f10.into(), + f11.into(), + f12.into(), + f13.into(), + f14.into(), + f15.into(), + f16.into(), + f17.into(), + f18.into(), + f19.into(), + f20.into(), + f21.into(), + f22.into(), + f23.into(), + f24.into(), + f25.into(), + f26.into(), + f27.into(), + f28.into(), + f29.into(), + f30.into(), + f31.into(), + f32.into(), + f33.into(), + f34.into(), + f35.into(), + f36.into(), + f37.into(), + f38.into(), + f39.into(), + f40.into(), + f41.into(), + f42.into(), + f43.into(), + f44.into(), + f45.into(), + f46.into(), + f47.into(), + f48.into(), + f49.into(), + f50.into(), + f51.into(), + f52.into(), + f53.into(), + f54.into(), + f55.into(), + f56.into(), + f57.into(), + f58.into(), + f59.into(), + f60.into(), + f61.into(), + f62.into(), + f63.into(), + f64.into(), + f65.into(), + f66.into(), + f67.into(), + f68.into(), + f69.into(), + f70.into(), + f71.into(), + f72.into(), + f73.into(), + f74.into(), + f75.into(), + f76.into(), + f77.into(), + f78.into(), + f79.into(), + f80.into(), + f81.into(), + f82.into(), + f83.into(), + f84.into(), + f85.into(), + f86.into(), + f87.into(), + f88.into(), + f89.into(), + f90.into(), + f91.into(), + f92.into(), + f93.into(), + f94.into(), + f95.into(), + f96.into(), + f97.into(), + f98.into(), + f99.into(), + f100.into(), + f101.into(), + f102.into(), + f103.into(), + f104.into(), + f105.into(), + f106.into(), + f107.into(), + f108.into(), + f109.into(), + f110.into(), + f111.into(), + f112.into(), + f113.into(), + f114.into(), + f115.into(), + f116.into(), + f117.into(), + f118.into(), + f119.into(), + f120.into(), + f121.into(), + f122.into(), + f123.into(), + f124.into(), + f125.into(), + f126.into(), + f127.into(), + f128.into(), + f129.into(), + f130.into(), + f131.into(), + f132.into(), + f133.into(), + f134.into(), + f135.into(), + f136.into(), + f137.into(), + f138.into(), + f139.into(), + f140.into(), + f141.into(), + f142.into(), + f143.into(), + f144.into(), + f145.into(), + f146.into(), + f147.into(), + f148.into(), + f149.into(), + f150.into(), + f151.into(), + f152.into(), + f153.into(), + f154.into(), + f155.into(), + f156.into(), + f157.into(), + f158.into(), + f159.into(), + f160.into(), + f161.into(), + f162.into(), + f163.into(), + f164.into(), + f165.into(), + f166.into(), + f167.into(), + f168.into(), + f169.into(), + f170.into(), + f171.into(), + f172.into(), + f173.into(), + f174.into(), + f175.into(), + f176.into(), + f177.into(), + f178.into(), + f179.into(), + f180.into(), + f181.into(), + f182.into(), + f183.into(), + f184.into(), + f185.into(), + f186.into(), + f187.into(), + f188.into(), + f189.into(), + f190.into(), + f191.into(), + f192.into(), + f193.into(), + f194.into(), + f195.into(), + f196.into(), + f197.into(), + f198.into(), + f199.into(), + f200.into(), + f201.into(), + f202.into(), + f203.into(), + f204.into(), + f205.into(), + f206.into(), + f207.into(), + f208.into(), + f209.into(), + f210.into(), + f211.into(), + f212.into(), + f213.into(), + f214.into(), + f215.into(), + f216.into(), + f217.into(), + f218.into(), + f219.into(), + f220.into(), + f221.into(), + f222.into(), + f223.into(), + f224.into(), + f225.into(), + f226.into(), + f227.into(), + f228.into(), + f229.into(), + f230.into(), + f231.into(), + f232.into(), + f233.into(), + f234.into(), + f235.into(), + f236.into(), + f237.into(), + f238.into(), + f239.into(), + f240.into(), + f241.into(), + f242.into(), + f243.into(), + f244.into(), + f245.into(), + f246.into(), + f247.into(), + f248.into(), + f249.into(), + f250.into(), + f251.into(), + f252.into(), + f253.into(), + f254.into(), + f255.into(), + f256.into(), + f257.into(), + f258.into(), + f259.into(), + f260.into(), + f261.into(), + f262.into(), + f263.into(), + f264.into(), + f265.into(), + f266.into(), + f267.into(), + f268.into(), + f269.into(), + f270.into(), + f271.into(), + f272.into(), + f273.into(), + f274.into(), + f275.into(), + f276.into(), + f277.into(), + f278.into(), + f279.into(), + f280.into(), + f281.into(), + f282.into(), + f283.into(), + f284.into(), + f285.into(), + f286.into(), + f287.into(), + f288.into(), + f289.into(), + f290.into(), + f291.into(), + f292.into(), + f293.into(), + f294.into(), + f295.into(), + f296.into(), + f297.into(), + f298.into(), + f299.into(), + f300.into(), + f301.into(), + f302.into(), + f303.into(), + f304.into(), + f305.into(), + f306.into(), + f307.into(), + f308.into(), + f309.into(), + f310.into(), + f311.into(), + f312.into(), + f313.into(), + f314.into(), + f315.into(), + f316.into(), + f317.into(), + f318.into(), + f319.into(), + f320.into(), + f321.into(), + f322.into(), + f323.into(), + f324.into(), + f325.into(), + f326.into(), + f327.into(), + f328.into(), + f329.into(), + f330.into(), + f331.into(), + f332.into(), + f333.into(), + f334.into(), + f335.into(), + f336.into(), + f337.into(), + f338.into(), + f339.into(), + f340.into(), + f341.into(), + f342.into(), + f343.into(), + f344.into(), + f345.into(), + f346.into(), + f347.into(), + f348.into(), + f349.into(), + f350.into(), + f351.into(), + f352.into(), + f353.into(), + f354.into(), + f355.into(), + f356.into(), + f357.into(), + f358.into(), + f359.into(), + f360.into(), + f361.into(), + f362.into(), + f363.into(), + f364.into(), + f365.into(), + f366.into(), + f367.into(), + f368.into(), + f369.into(), + f370.into(), + f371.into(), + f372.into(), + f373.into(), + f374.into(), + f375.into(), + f376.into(), + f377.into(), + f378.into(), + f379.into(), + f380.into(), + f381.into(), + f382.into(), + f383.into(), + f384.into(), + f385.into(), + f386.into(), + f387.into(), + f388.into(), + f389.into(), + f390.into(), + f391.into(), + f392.into(), + f393.into(), + f394.into(), + f395.into(), + f396.into(), + f397.into(), + f398.into(), + f399.into(), + f400.into(), + f401.into(), + f402.into(), + f403.into(), + f404.into(), + f405.into(), + f406.into(), + f407.into(), + f408.into(), + f409.into(), + f410.into(), + f411.into(), + f412.into(), + f413.into(), + f414.into(), + f415.into(), + f416.into(), + f417.into(), + f418.into(), + f419.into(), + f420.into(), + f421.into(), + f422.into(), + f423.into(), + f424.into(), + f425.into(), + f426.into(), + f427.into(), + f428.into(), + f429.into(), + f430.into(), + f431.into(), + f432.into(), + f433.into(), + f434.into(), + f435.into(), + f436.into(), + f437.into(), + f438.into(), + f439.into(), + f440.into(), + f441.into(), + f442.into(), + f443.into(), + f444.into(), + f445.into(), + f446.into(), + f447.into(), + f448.into(), + f449.into(), + f450.into(), + f451.into(), + f452.into(), + f453.into(), + f454.into(), + f455.into(), + f456.into(), + f457.into(), + f458.into(), + f459.into(), + f460.into(), + f461.into(), + f462.into(), + f463.into(), + f464.into(), + f465.into(), + f466.into(), + f467.into(), + f468.into(), + f469.into(), + f470.into(), + f471.into(), + f472.into(), + f473.into(), + f474.into(), + f475.into(), + f476.into(), + f477.into(), + f478.into(), + f479.into(), + f480.into(), + f481.into(), + f482.into(), + f483.into(), + f484.into(), + f485.into(), + f486.into(), + f487.into(), + f488.into(), + f489.into(), + f490.into(), + f491.into(), + f492.into(), + f493.into(), + f494.into(), + f495.into(), + f496.into(), + f497.into(), + f498.into(), + f499.into(), + f500.into(), + f501.into(), + f502.into(), + f503.into(), + f504.into(), + f505.into(), + f506.into(), + f507.into(), + f508.into(), + f509.into(), + f510.into(), + f511.into(), + ); + array![ + upcast(r0), upcast(r1), upcast(r2), upcast(r3), upcast(r4), upcast(r5), upcast(r6), + upcast(r7), upcast(r8), upcast(r9), upcast(r10), upcast(r11), upcast(r12), upcast(r13), + upcast(r14), upcast(r15), upcast(r16), upcast(r17), upcast(r18), upcast(r19), upcast(r20), + upcast(r21), upcast(r22), upcast(r23), upcast(r24), upcast(r25), upcast(r26), upcast(r27), + upcast(r28), upcast(r29), upcast(r30), upcast(r31), upcast(r32), upcast(r33), upcast(r34), + upcast(r35), upcast(r36), upcast(r37), upcast(r38), upcast(r39), upcast(r40), upcast(r41), + upcast(r42), upcast(r43), upcast(r44), upcast(r45), upcast(r46), upcast(r47), upcast(r48), + upcast(r49), upcast(r50), upcast(r51), upcast(r52), upcast(r53), upcast(r54), upcast(r55), + upcast(r56), upcast(r57), upcast(r58), upcast(r59), upcast(r60), upcast(r61), upcast(r62), + upcast(r63), upcast(r64), upcast(r65), upcast(r66), upcast(r67), upcast(r68), upcast(r69), + upcast(r70), upcast(r71), upcast(r72), upcast(r73), upcast(r74), upcast(r75), upcast(r76), + upcast(r77), upcast(r78), upcast(r79), upcast(r80), upcast(r81), upcast(r82), upcast(r83), + upcast(r84), upcast(r85), upcast(r86), upcast(r87), upcast(r88), upcast(r89), upcast(r90), + upcast(r91), upcast(r92), upcast(r93), upcast(r94), upcast(r95), upcast(r96), upcast(r97), + upcast(r98), upcast(r99), upcast(r100), upcast(r101), upcast(r102), upcast(r103), + upcast(r104), upcast(r105), upcast(r106), upcast(r107), upcast(r108), upcast(r109), + upcast(r110), upcast(r111), upcast(r112), upcast(r113), upcast(r114), upcast(r115), + upcast(r116), upcast(r117), upcast(r118), upcast(r119), upcast(r120), upcast(r121), + upcast(r122), upcast(r123), upcast(r124), upcast(r125), upcast(r126), upcast(r127), + upcast(r128), upcast(r129), upcast(r130), upcast(r131), upcast(r132), upcast(r133), + upcast(r134), upcast(r135), upcast(r136), upcast(r137), upcast(r138), upcast(r139), + upcast(r140), upcast(r141), upcast(r142), upcast(r143), upcast(r144), upcast(r145), + upcast(r146), upcast(r147), upcast(r148), upcast(r149), upcast(r150), upcast(r151), + upcast(r152), upcast(r153), upcast(r154), upcast(r155), upcast(r156), upcast(r157), + upcast(r158), upcast(r159), upcast(r160), upcast(r161), upcast(r162), upcast(r163), + upcast(r164), upcast(r165), upcast(r166), upcast(r167), upcast(r168), upcast(r169), + upcast(r170), upcast(r171), upcast(r172), upcast(r173), upcast(r174), upcast(r175), + upcast(r176), upcast(r177), upcast(r178), upcast(r179), upcast(r180), upcast(r181), + upcast(r182), upcast(r183), upcast(r184), upcast(r185), upcast(r186), upcast(r187), + upcast(r188), upcast(r189), upcast(r190), upcast(r191), upcast(r192), upcast(r193), + upcast(r194), upcast(r195), upcast(r196), upcast(r197), upcast(r198), upcast(r199), + upcast(r200), upcast(r201), upcast(r202), upcast(r203), upcast(r204), upcast(r205), + upcast(r206), upcast(r207), upcast(r208), upcast(r209), upcast(r210), upcast(r211), + upcast(r212), upcast(r213), upcast(r214), upcast(r215), upcast(r216), upcast(r217), + upcast(r218), upcast(r219), upcast(r220), upcast(r221), upcast(r222), upcast(r223), + upcast(r224), upcast(r225), upcast(r226), upcast(r227), upcast(r228), upcast(r229), + upcast(r230), upcast(r231), upcast(r232), upcast(r233), upcast(r234), upcast(r235), + upcast(r236), upcast(r237), upcast(r238), upcast(r239), upcast(r240), upcast(r241), + upcast(r242), upcast(r243), upcast(r244), upcast(r245), upcast(r246), upcast(r247), + upcast(r248), upcast(r249), upcast(r250), upcast(r251), upcast(r252), upcast(r253), + upcast(r254), upcast(r255), upcast(r256), upcast(r257), upcast(r258), upcast(r259), + upcast(r260), upcast(r261), upcast(r262), upcast(r263), upcast(r264), upcast(r265), + upcast(r266), upcast(r267), upcast(r268), upcast(r269), upcast(r270), upcast(r271), + upcast(r272), upcast(r273), upcast(r274), upcast(r275), upcast(r276), upcast(r277), + upcast(r278), upcast(r279), upcast(r280), upcast(r281), upcast(r282), upcast(r283), + upcast(r284), upcast(r285), upcast(r286), upcast(r287), upcast(r288), upcast(r289), + upcast(r290), upcast(r291), upcast(r292), upcast(r293), upcast(r294), upcast(r295), + upcast(r296), upcast(r297), upcast(r298), upcast(r299), upcast(r300), upcast(r301), + upcast(r302), upcast(r303), upcast(r304), upcast(r305), upcast(r306), upcast(r307), + upcast(r308), upcast(r309), upcast(r310), upcast(r311), upcast(r312), upcast(r313), + upcast(r314), upcast(r315), upcast(r316), upcast(r317), upcast(r318), upcast(r319), + upcast(r320), upcast(r321), upcast(r322), upcast(r323), upcast(r324), upcast(r325), + upcast(r326), upcast(r327), upcast(r328), upcast(r329), upcast(r330), upcast(r331), + upcast(r332), upcast(r333), upcast(r334), upcast(r335), upcast(r336), upcast(r337), + upcast(r338), upcast(r339), upcast(r340), upcast(r341), upcast(r342), upcast(r343), + upcast(r344), upcast(r345), upcast(r346), upcast(r347), upcast(r348), upcast(r349), + upcast(r350), upcast(r351), upcast(r352), upcast(r353), upcast(r354), upcast(r355), + upcast(r356), upcast(r357), upcast(r358), upcast(r359), upcast(r360), upcast(r361), + upcast(r362), upcast(r363), upcast(r364), upcast(r365), upcast(r366), upcast(r367), + upcast(r368), upcast(r369), upcast(r370), upcast(r371), upcast(r372), upcast(r373), + upcast(r374), upcast(r375), upcast(r376), upcast(r377), upcast(r378), upcast(r379), + upcast(r380), upcast(r381), upcast(r382), upcast(r383), upcast(r384), upcast(r385), + upcast(r386), upcast(r387), upcast(r388), upcast(r389), upcast(r390), upcast(r391), + upcast(r392), upcast(r393), upcast(r394), upcast(r395), upcast(r396), upcast(r397), + upcast(r398), upcast(r399), upcast(r400), upcast(r401), upcast(r402), upcast(r403), + upcast(r404), upcast(r405), upcast(r406), upcast(r407), upcast(r408), upcast(r409), + upcast(r410), upcast(r411), upcast(r412), upcast(r413), upcast(r414), upcast(r415), + upcast(r416), upcast(r417), upcast(r418), upcast(r419), upcast(r420), upcast(r421), + upcast(r422), upcast(r423), upcast(r424), upcast(r425), upcast(r426), upcast(r427), + upcast(r428), upcast(r429), upcast(r430), upcast(r431), upcast(r432), upcast(r433), + upcast(r434), upcast(r435), upcast(r436), upcast(r437), upcast(r438), upcast(r439), + upcast(r440), upcast(r441), upcast(r442), upcast(r443), upcast(r444), upcast(r445), + upcast(r446), upcast(r447), upcast(r448), upcast(r449), upcast(r450), upcast(r451), + upcast(r452), upcast(r453), upcast(r454), upcast(r455), upcast(r456), upcast(r457), + upcast(r458), upcast(r459), upcast(r460), upcast(r461), upcast(r462), upcast(r463), + upcast(r464), upcast(r465), upcast(r466), upcast(r467), upcast(r468), upcast(r469), + upcast(r470), upcast(r471), upcast(r472), upcast(r473), upcast(r474), upcast(r475), + upcast(r476), upcast(r477), upcast(r478), upcast(r479), upcast(r480), upcast(r481), + upcast(r482), upcast(r483), upcast(r484), upcast(r485), upcast(r486), upcast(r487), + upcast(r488), upcast(r489), upcast(r490), upcast(r491), upcast(r492), upcast(r493), + upcast(r494), upcast(r495), upcast(r496), upcast(r497), upcast(r498), upcast(r499), + upcast(r500), upcast(r501), upcast(r502), upcast(r503), upcast(r504), upcast(r505), + upcast(r506), upcast(r507), upcast(r508), upcast(r509), upcast(r510), upcast(r511), + ] +} diff --git a/packages/account/src/falcon_512/ntt/roots_felt.cairo b/packages/account/src/falcon_512/ntt/roots_felt.cairo new file mode 100644 index 000000000..e3fb9bc54 --- /dev/null +++ b/packages/account/src/falcon_512/ntt/roots_felt.cairo @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/ntt/roots_felt.cairo) + +// Generated from the Falcon-512 NTT parameters — do not edit by hand. +// Forward merge-root tables as felt252 constants, value-identical to the +// u16 tables in roots.cairo; the engine multiplies roots into felt252 +// butterflies, so the constants are stored ready to consume. + +//! Forward NTT root tables as felts (generated). + +const phi4_roots_zq_felt: [felt252; 1] = [1479]; +const phi8_roots_zq_felt: [felt252; 2] = [4043, 5146]; +const phi16_roots_zq_felt: [felt252; 4] = [5736, 4134, 722, 1305]; +const phi32_roots_zq_felt: [felt252; 8] = [1646, 1212, 5860, 3195, 2545, 3621, 3504, 3542]; +const phi64_roots_zq_felt: [felt252; 16] = [ + 4591, 5728, 5023, 5828, 4978, 1351, 3328, 5777, 2975, 563, 3006, 2744, 949, 2625, 4821, 2639, +]; +const phi128_roots_zq_felt: [felt252; 32] = [ + 1000, 4320, 3091, 81, 2963, 4896, 3051, 2366, 1853, 140, 4611, 726, 4255, 1177, 2768, 1635, + 3712, 3135, 2747, 4846, 3553, 4805, 2294, 1062, 1326, 5086, 3014, 3201, 1170, 2319, 955, 790, +]; +const phi256_roots_zq_felt: [felt252; 64] = [ + 544, 5791, 339, 2468, 2842, 480, 9, 1022, 4278, 1673, 4989, 5331, 3584, 4177, 1381, 2525, 2396, + 4452, 3296, 3949, 130, 4354, 5374, 2837, 5767, 827, 3748, 953, 5067, 2197, 118, 2476, 2548, + 4231, 355, 3382, 3707, 1759, 3694, 5179, 5542, 145, 3637, 3459, 5911, 4890, 3932, 2731, 2089, + 5092, 2881, 3284, 729, 3241, 3289, 2013, 5755, 4632, 1260, 4388, 334, 2426, 1696, 1428, +]; +const phi512_roots_zq_felt: [felt252; 128] = [ + 1663, 1777, 1426, 4654, 5291, 2704, 4938, 3636, 3915, 2166, 113, 4919, 3, 4437, 160, 3149, 4057, + 3271, 1689, 3364, 4372, 2174, 4414, 2847, 2645, 4053, 2305, 5042, 5195, 2780, 1484, 4895, 3016, + 243, 3000, 671, 3136, 5191, 2399, 3400, 2178, 1544, 420, 5559, 476, 3531, 3985, 4905, 5332, + 3510, 2370, 2865, 2969, 3978, 2686, 3247, 4048, 2249, 1153, 2884, 5407, 3186, 1630, 2126, 2187, + 2566, 2422, 6039, 2987, 6022, 2437, 3646, 875, 3780, 1607, 4976, 5011, 1002, 4284, 5088, 3248, + 1207, 1168, 5277, 1065, 2143, 404, 4645, 1912, 1378, 435, 4337, 2381, 5444, 4096, 493, 545, + 5019, 3704, 2678, 1537, 242, 4714, 4143, 27, 3066, 3763, 1440, 5084, 1632, 1017, 4885, 3778, + 3833, 390, 773, 2401, 442, 5101, 1067, 2912, 5698, 354, 4861, 2859, 1045, 5012, 2481, +]; +const phi1024_roots_zq_felt: [felt252; 256] = [ + 3957, 2839, 2127, 151, 431, 1579, 5906, 2505, 1323, 2766, 52, 3174, 6055, 3336, 677, 5874, 4169, + 3127, 5241, 2920, 1010, 5468, 787, 3482, 1321, 192, 4912, 2049, 4698, 5057, 4780, 3445, 1956, + 5009, 6008, 885, 3532, 1003, 58, 241, 975, 4212, 2844, 3438, 1105, 142, 5681, 3477, 2302, 605, + 4213, 504, 5886, 4782, 5594, 3029, 421, 4080, 6068, 3602, 6077, 4624, 3263, 3600, 4948, 6137, + 400, 1728, 5862, 6136, 5415, 3643, 56, 3199, 5206, 5529, 3565, 654, 1987, 1702, 3988, 468, 316, + 382, 3710, 6093, 5446, 5339, 973, 1254, 1359, 5435, 2033, 3998, 3879, 1922, 3860, 5445, 4536, + 1050, 3818, 6118, 1190, 2683, 3789, 147, 5456, 4449, 4749, 5537, 4789, 4467, 1018, 5925, 1041, + 3514, 2344, 1278, 5574, 1973, 4324, 4916, 4075, 5315, 5079, 3262, 522, 2169, 1200, 5184, 2555, + 6122, 5297, 6119, 3956, 1360, 1962, 1594, 5961, 5106, 4298, 3329, 168, 2692, 4049, 3728, 1159, + 5990, 948, 1146, 1404, 325, 5766, 652, 295, 6099, 2919, 3762, 4016, 4077, 6065, 835, 3570, 4240, + 4046, 709, 3150, 1319, 1058, 4079, 922, 441, 4322, 1958, 2078, 1112, 3834, 5257, 4433, 5919, + 5486, 3054, 1747, 3123, 2948, 2503, 5782, 1566, 64, 3656, 2459, 683, 1293, 4737, 5429, 4774, + 5908, 453, 418, 3772, 3991, 3969, 2767, 156, 2281, 5876, 5333, 2031, 3963, 576, 2447, 6142, + 2051, 1954, 1805, 2882, 3529, 3434, 2908, 218, 3030, 4115, 1843, 2361, 3202, 4493, 2057, 5369, + 1512, 350, 1815, 5383, 49, 1263, 5915, 1483, 1489, 2500, 5942, 1583, 1693, 3009, 174, 723, 2738, + 5868, 5735, 2655, 3315, 426, 4754, 1858, 1975, 3757, 2925, 347, +]; + +/// Forward merge roots for the given degree, as felts (the values of +/// `roots::get_even_roots`). +pub fn get_even_roots_felt(degree: u32) -> Span { + if degree == 2 { + phi4_roots_zq_felt.span() + } else if degree == 4 { + phi8_roots_zq_felt.span() + } else if degree == 8 { + phi16_roots_zq_felt.span() + } else if degree == 16 { + phi32_roots_zq_felt.span() + } else if degree == 32 { + phi64_roots_zq_felt.span() + } else if degree == 64 { + phi128_roots_zq_felt.span() + } else if degree == 128 { + phi256_roots_zq_felt.span() + } else if degree == 256 { + phi512_roots_zq_felt.span() + } else if degree == 512 { + phi1024_roots_zq_felt.span() + } else { + panic!("no root table for degree") + } +} diff --git a/packages/account/src/falcon_512/ntt/roots_scaled.cairo b/packages/account/src/falcon_512/ntt/roots_scaled.cairo new file mode 100644 index 000000000..34e92e89d --- /dev/null +++ b/packages/account/src/falcon_512/ntt/roots_scaled.cairo @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/ntt/roots_scaled.cairo) + +// Generated from the Falcon-512 NTT parameters — do not edit by hand. +// Inverse merge-root tables prescaled by I2 = 2^-1 mod q +// (tinv[i] = I2 * root_inv[i] mod q), derived from roots.cairo; the +// split levels of the INTT consume them as single felt252 multipliers. + +//! I2-scaled inverse NTT root tables (generated). + +const phi4_roots_zq_inv_scaled: [felt252; 1] = [5405]; +const phi8_roots_zq_inv_scaled: [felt252; 2] = [2573, 8166]; +const phi16_roots_zq_inv_scaled: [felt252; 4] = [11928, 5492, 9421, 10222]; +const phi32_roots_zq_inv_scaled: [felt252; 8] = [7955, 7417, 10518, 1752, 606, 823, 7742, 9359]; +const phi64_roots_zq_inv_scaled: [felt252; 16] = [ + 10786, 1372, 7632, 6426, 7464, 8555, 5670, 4832, 8656, 2914, 3849, 2864, 10625, 3256, 6820, + 2489, +]; +const phi128_roots_zq_inv_scaled: [felt252; 32] = [ + 8547, 4368, 1147, 11758, 1856, 4577, 4771, 9866, 5667, 11894, 11704, 7304, 2543, 11626, 4544, + 1507, 7626, 9841, 4619, 11106, 10129, 500, 7690, 6104, 5556, 8272, 6962, 10905, 3839, 363, 5218, + 12219, +]; +const phi256_roots_zq_inv_scaled: [felt252; 64] = [ + 4326, 4415, 6072, 9518, 3189, 2445, 7510, 1966, 1274, 4029, 1691, 5967, 5265, 4291, 8734, 10442, + 11076, 12122, 714, 11441, 9973, 3267, 630, 10095, 4704, 10647, 9743, 5100, 7151, 7789, 5780, + 4524, 2139, 5308, 3479, 8639, 4056, 10497, 4882, 6835, 11055, 5975, 12017, 3249, 10868, 240, + 511, 6149, 5668, 10415, 3261, 5731, 12230, 11051, 7243, 8678, 10112, 12224, 2687, 4726, 10063, + 11091, 4170, 1648, +]; +const phi512_roots_zq_inv_scaled: [felt252; 128] = [ + 5073, 5612, 3822, 12087, 3506, 11705, 6748, 10665, 11600, 11333, 5927, 3976, 10241, 5898, 7335, + 2722, 7238, 11006, 9164, 1211, 7363, 10466, 3011, 7638, 2488, 6948, 10399, 5707, 2142, 2544, + 11788, 8650, 3714, 12112, 2849, 1456, 2506, 4904, 5622, 7574, 6531, 12094, 8061, 1889, 4944, + 12068, 6678, 8695, 12168, 5376, 4073, 9932, 10950, 1852, 8654, 5872, 6653, 3702, 9747, 816, + 1533, 6131, 720, 4263, 8173, 4509, 1682, 6989, 7568, 2207, 2186, 11202, 9768, 4992, 8171, 7467, + 8592, 11547, 8742, 1390, 10471, 2469, 10937, 3499, 5256, 5313, 9962, 11576, 11206, 4187, 6088, + 3685, 80, 7719, 6143, 3926, 4521, 1343, 1989, 4660, 10534, 9623, 4712, 11104, 10696, 8848, + 11226, 815, 5568, 1442, 2024, 5020, 8924, 210, 11517, 1089, 12051, 4379, 8597, 8137, 6480, 1500, + 6023, 10781, 1700, 4945, 1568, 3549, +]; +const phi1024_roots_zq_inv_scaled: [felt252; 256] = [ + 5924, 461, 4105, 529, 556, 11250, 11310, 2161, 5485, 10714, 6499, 10266, 10504, 2120, 6562, + 3112, 3185, 3928, 3516, 1917, 1527, 9546, 7706, 5271, 10461, 12257, 5803, 7374, 1474, 7396, + 9398, 11506, 600, 9697, 9228, 4867, 10311, 680, 8793, 3085, 2149, 4480, 1346, 84, 9125, 2553, + 11308, 11492, 702, 6307, 11815, 573, 9294, 5565, 10425, 4120, 4685, 10408, 2008, 4106, 3095, + 6292, 9406, 326, 4817, 3277, 2934, 10920, 87, 5783, 6991, 4640, 5157, 8023, 5971, 7607, 11360, + 9912, 7802, 213, 5237, 3453, 12114, 756, 7173, 3460, 1601, 3898, 5513, 6169, 6886, 9102, 9318, + 5353, 6889, 1250, 12211, 7528, 4160, 8140, 5129, 3478, 9351, 7285, 1886, 209, 2954, 6371, 3430, + 9902, 3776, 6791, 1515, 4087, 7325, 5223, 1717, 4380, 1454, 12180, 4921, 9218, 288, 4163, 7047, + 10848, 977, 7170, 978, 3640, 5702, 9285, 29, 6024, 10523, 6646, 3304, 4406, 6697, 71, 6632, + 10183, 1719, 1422, 4630, 9492, 9898, 9346, 4038, 252, 1151, 6447, 4513, 1800, 2312, 3106, 5934, + 10249, 3034, 10488, 9352, 6483, 3117, 1668, 10702, 26, 10906, 5483, 6220, 5081, 7564, 4166, + 7397, 9336, 5355, 6360, 1460, 3524, 4581, 4060, 9555, 505, 10548, 6538, 2349, 8673, 9899, 4422, + 12193, 5484, 7169, 9833, 8378, 3750, 3770, 3376, 2728, 8369, 6218, 8039, 525, 10021, 8867, + 10359, 7486, 11694, 9230, 10380, 3487, 8182, 10127, 9831, 7229, 261, 8684, 10658, 11650, 1172, + 2787, 5158, 509, 3182, 10532, 6665, 2723, 3475, 1855, 3098, 191, 12131, 10295, 12055, 627, 5658, + 8862, 6824, 8084, 961, 5128, 10290, 8852, 4323, 2931, 9221, 11425, 200, 3076, 9815, 7138, 851, + 11962, 4362, 12261, 7744, 8909, 9686, +]; + +/// I2-prescaled inverse roots for the split level of the given degree +/// (the output size of the merge it inverts). +pub fn get_scaled_inv_roots(degree: u32) -> Span { + if degree == 2 { + phi4_roots_zq_inv_scaled.span() + } else if degree == 4 { + phi8_roots_zq_inv_scaled.span() + } else if degree == 8 { + phi16_roots_zq_inv_scaled.span() + } else if degree == 16 { + phi32_roots_zq_inv_scaled.span() + } else if degree == 32 { + phi64_roots_zq_inv_scaled.span() + } else if degree == 64 { + phi128_roots_zq_inv_scaled.span() + } else if degree == 128 { + phi256_roots_zq_inv_scaled.span() + } else if degree == 256 { + phi512_roots_zq_inv_scaled.span() + } else if degree == 512 { + phi1024_roots_zq_inv_scaled.span() + } else { + panic!("no root table for degree") + } +} diff --git a/packages/account/src/falcon_512/packing.cairo b/packages/account/src/falcon_512/packing.cairo new file mode 100644 index 000000000..b02f56037 --- /dev/null +++ b/packages/account/src/falcon_512/packing.cairo @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/packing.cairo) + +//! Base-Q packing of 512 Z_q coefficients into 29 felt252 slots. +//! +//! Each full slot carries two u128 halves; each half Horner-packs 9 coefficients in base +//! Q = 12289 (Q^9 < 2^128): `half = c0 + Q*(c1 + Q*(c2 + ...))`. 28 full slots hold 18 +//! coefficients each; the last slot holds the remaining 8 in its low half. +//! +//! Unpacking is validating: every extracted base-Q digit is < Q by construction, and the +//! residual quotient after the last digit must be zero (each half < Q^9, the last slot +//! < Q^8). The accepted felt vectors are therefore in bijection with coefficient vectors +//! in [0, Q)^512 — no coefficient can be smuggled in a non-canonical encoding. + +use corelib_imports::bounded_int::{ + BoundedInt, DivRemHelper, UnitInt, bounded_int_div_rem, downcast, upcast, +}; +use super::zq::Q; + +/// Coefficients per full felt252 slot (two u128 halves of 9). +pub const VALS_PER_FELT: u32 = 18; +/// felt252 slots for 512 coefficients: 28 full slots + 1 slot with 8. +pub const PACKED_SLOTS: u32 = 29; +/// Coefficients in the last slot: 512 - 28*18. +const LAST_SLOT_VALS: u32 = 8; + +const TWO_POW_128: felt252 = 0x100000000000000000000000000000000; + +type PackingZq = BoundedInt<0, 12288>; +type PackingQ = UnitInt<12289>; +type Acc1 = BoundedInt<0, 151019520>; +type Acc2 = BoundedInt<0, 1855878893568>; +type Acc3 = BoundedInt<0, 22806895723069440>; +type Acc4 = BoundedInt<0, 280273941540800360448>; +type Acc5 = BoundedInt<0, 3444286467594895629557760>; +type Acc6 = BoundedInt<0, 42326836400273672391635324928>; +type Acc7 = BoundedInt<0, 520154492522963160020806508052480>; +type Acc8 = BoundedInt<0, 6392178558614694273495691177456939008>; + +const PACKING_Q_NZ: NonZero = 12289; + +impl PackingDivRemAcc1Impl of DivRemHelper { + type DivT = PackingZq; + type RemT = PackingZq; +} + +impl PackingDivRemAcc2Impl of DivRemHelper { + type DivT = Acc1; + type RemT = PackingZq; +} + +impl PackingDivRemAcc3Impl of DivRemHelper { + type DivT = Acc2; + type RemT = PackingZq; +} + +impl PackingDivRemAcc4Impl of DivRemHelper { + type DivT = Acc3; + type RemT = PackingZq; +} + +impl PackingDivRemAcc5Impl of DivRemHelper { + type DivT = Acc4; + type RemT = PackingZq; +} + +impl PackingDivRemAcc6Impl of DivRemHelper { + type DivT = Acc5; + type RemT = PackingZq; +} + +impl PackingDivRemAcc7Impl of DivRemHelper { + type DivT = Acc6; + type RemT = PackingZq; +} + +impl PackingDivRemAcc8Impl of DivRemHelper { + type DivT = Acc7; + type RemT = PackingZq; +} + +/// Unpack 29 packed felts into 512 coefficients in [0, Q), as felts (each is a +/// base-Q digit, so the range holds by construction). +/// Returns `None` on wrong length or any non-canonical slot encoding. +pub fn unpack_512(packed: Span) -> Option> { + let coeffs_u16 = match unpack_512_u16(packed) { + Some(v) => v, + None => { return None; }, + }; + let mut coeffs_u16 = coeffs_u16.span(); + let mut coeffs: Array = array![]; + while let Some(coeff) = coeffs_u16.pop_front() { + coeffs.append((*coeff).into()); + } + Some(coeffs) +} + +/// Unpack the canonical 29-slot encoding directly into the verifier's `u16` form. +/// Returns `None` on wrong length or any non-canonical slot encoding. +pub(crate) fn unpack_512_u16(packed: Span) -> Option> { + if packed.len() != PACKED_SLOTS { + return None; + } + let mut coeffs: Array = array![]; + let mut full_slots = packed.slice(0, PACKED_SLOTS - 1); + while let Some(chunk) = full_slots.multi_pop_front::<7>() { + let [f0, f1, f2, f3, f4, f5, f6] = (*chunk).unbox(); + if !unpack_full_slot_u16(f0, ref coeffs) + || !unpack_full_slot_u16(f1, ref coeffs) + || !unpack_full_slot_u16(f2, ref coeffs) + || !unpack_full_slot_u16(f3, ref coeffs) + || !unpack_full_slot_u16(f4, ref coeffs) + || !unpack_full_slot_u16(f5, ref coeffs) + || !unpack_full_slot_u16(f6, ref coeffs) { + return None; + } + } + let last: u256 = (*packed.at(PACKED_SLOTS - 1)).into(); + if last.high != 0 || !unpack_half8_u16(last.low, ref coeffs) { + return None; + } + Some(coeffs) +} + +#[inline(always)] +fn unpack_full_slot_u16(felt: felt252, ref coeffs: Array) -> bool { + let value: u256 = felt.into(); + unpack_half9_u16(value.low, ref coeffs) && unpack_half9_u16(value.high, ref coeffs) +} + +/// Extract nine base-Q digits from a u128 half. Values below Q^9 are canonical; +/// after eight divisions the remaining quotient is the ninth digit. +#[inline(always)] +fn unpack_half9_u16(value: u128, ref coeffs: Array) -> bool { + let value: Acc8 = match downcast(value) { + Some(v) => v, + None => { return false; }, + }; + let (rest, d0) = bounded_int_div_rem(value, PACKING_Q_NZ); + let (rest, d1) = bounded_int_div_rem(rest, PACKING_Q_NZ); + let (rest, d2) = bounded_int_div_rem(rest, PACKING_Q_NZ); + let (rest, d3) = bounded_int_div_rem(rest, PACKING_Q_NZ); + let (rest, d4) = bounded_int_div_rem(rest, PACKING_Q_NZ); + let (rest, d5) = bounded_int_div_rem(rest, PACKING_Q_NZ); + let (rest, d6) = bounded_int_div_rem(rest, PACKING_Q_NZ); + let (rest, d7) = bounded_int_div_rem(rest, PACKING_Q_NZ); + coeffs.append(upcast(d0)); + coeffs.append(upcast(d1)); + coeffs.append(upcast(d2)); + coeffs.append(upcast(d3)); + coeffs.append(upcast(d4)); + coeffs.append(upcast(d5)); + coeffs.append(upcast(d6)); + coeffs.append(upcast(d7)); + coeffs.append(upcast(rest)); + true +} + +/// Extract the final slot's eight digits after checking the value is below Q^8. +#[inline(always)] +fn unpack_half8_u16(value: u128, ref coeffs: Array) -> bool { + let value: Acc7 = match downcast(value) { + Some(v) => v, + None => { return false; }, + }; + let (rest, d0) = bounded_int_div_rem(value, PACKING_Q_NZ); + let (rest, d1) = bounded_int_div_rem(rest, PACKING_Q_NZ); + let (rest, d2) = bounded_int_div_rem(rest, PACKING_Q_NZ); + let (rest, d3) = bounded_int_div_rem(rest, PACKING_Q_NZ); + let (rest, d4) = bounded_int_div_rem(rest, PACKING_Q_NZ); + let (rest, d5) = bounded_int_div_rem(rest, PACKING_Q_NZ); + let (rest, d6) = bounded_int_div_rem(rest, PACKING_Q_NZ); + coeffs.append(upcast(d0)); + coeffs.append(upcast(d1)); + coeffs.append(upcast(d2)); + coeffs.append(upcast(d3)); + coeffs.append(upcast(d4)); + coeffs.append(upcast(d5)); + coeffs.append(upcast(d6)); + coeffs.append(upcast(rest)); + true +} + +/// Pack 512 coefficients (each < Q) into 29 felts. Inverse of [`unpack_512`]; +/// used by tests and off-chain tooling mirrors. Panics on bad length or coefficient. +pub fn pack_512(coeffs: Span) -> Array { + assert(coeffs.len() == 512, 'pack: need 512 coeffs'); + let mut packed: Array = array![]; + let mut slot: u32 = 0; + while slot != PACKED_SLOTS - 1 { + let low = pack_half(coeffs.slice(slot * VALS_PER_FELT, 9)); + let high = pack_half(coeffs.slice(slot * VALS_PER_FELT + 9, 9)); + packed.append(low.into() + high.into() * TWO_POW_128); + slot += 1; + } + packed.append(pack_half(coeffs.slice(504, LAST_SLOT_VALS)).into()); + packed +} + +/// Horner-encode up to 9 coefficients into a u128: c0 + Q*(c1 + Q*(c2 + ...)). +fn pack_half(coeffs: Span) -> u128 { + let mut acc: u128 = 0; + let mut i = coeffs.len(); + while i != 0 { + i -= 1; + let c = *coeffs.at(i); + assert(c < Q, 'pack: coeff >= Q'); + acc = acc * 12289 + c.into(); + } + acc +} + +#[cfg(test)] +mod tests { + use super::{PACKED_SLOTS, pack_512, unpack_512, unpack_512_u16}; + + /// Q^9: the smallest non-canonical low-half value (all 9 digits zero, residue 1). + const Q_POW_9: felt252 = 6392178558614694273495691177456939009; + /// Q^8: the smallest non-canonical last-slot value. + const Q_POW_8: felt252 = 520154492522963160020806508052481; + const TWO_POW_128: felt252 = 0x100000000000000000000000000000000; + + fn pseudorandom_coeffs() -> Array { + let mut f: Array = array![]; + let mut state: u64 = 7; + for _ in 0_u32..512 { + state = (state * 1664525 + 1013904223) % 0x100000000; + f.append((state % 12289).try_into().unwrap()); + } + f + } + + fn as_felts(mut vals: Span) -> Array { + let mut out: Array = array![]; + while let Some(v) = vals.pop_front() { + out.append((*v).into()); + } + out + } + + #[test] + fn test_pack_unpack_roundtrip() { + let coeffs = pseudorandom_coeffs(); + let packed = pack_512(coeffs.span()); + assert_eq!(packed.len(), PACKED_SLOTS); + let unpacked = unpack_512(packed.span()).unwrap(); + assert_eq!(unpacked.span(), as_felts(coeffs.span()).span()); + assert_eq!(unpack_512_u16(packed.span()).unwrap().span(), coeffs.span()); + } + + #[test] + fn test_unpack_all_zero_and_max() { + let mut zeros: Array = array![]; + let mut maxed: Array = array![]; + for _ in 0_u32..512 { + zeros.append(0); + maxed.append(12288); + } + let z = unpack_512(pack_512(zeros.span()).span()).unwrap(); + assert_eq!(z.span(), as_felts(zeros.span()).span()); + let m = unpack_512(pack_512(maxed.span()).span()).unwrap(); + assert_eq!(m.span(), as_felts(maxed.span()).span()); + } + + #[test] + fn test_unpack_rejects_wrong_length() { + let coeffs = pseudorandom_coeffs(); + let packed = pack_512(coeffs.span()); + assert!(unpack_512(packed.span().slice(0, 28)).is_none()); + assert!(unpack_512(array![].span()).is_none()); + let mut too_long = packed; + too_long.append(0); + assert!(unpack_512(too_long.span()).is_none()); + } + + #[test] + fn test_unpack_rejects_noncanonical_slot_in_each_unrolled_position() { + let packed = pack_512(pseudorandom_coeffs().span()); + let mut bad_index = 0; + while bad_index != 7 { + let mut tampered: Array = array![]; + let mut index = 0; + while index != packed.len() { + if index == bad_index { + tampered.append(Q_POW_9); + } else { + tampered.append(*packed.at(index)); + } + index += 1; + } + assert!(unpack_512(tampered.span()).is_none()); + bad_index += 1; + } + } + + #[test] + fn test_unpack_rejects_noncanonical_half() { + // Q^9 in a low half: nine zero digits with a nonzero residue. + let mut packed = pack_512(pseudorandom_coeffs().span()); + let mut tampered: Array = array![Q_POW_9]; + let mut rest = packed.span().slice(1, 28); + while let Some(f) = rest.pop_front() { + tampered.append(*f); + } + assert!(unpack_512(tampered.span()).is_none()); + // Same overflow in a high half. + let mut tampered2: Array = array![Q_POW_9 * TWO_POW_128]; + let mut rest2 = packed.span().slice(1, 28); + while let Some(f) = rest2.pop_front() { + tampered2.append(*f); + } + assert!(unpack_512(tampered2.span()).is_none()); + } + + #[test] + fn test_unpack_rejects_noncanonical_last_slot() { + let packed = pack_512(pseudorandom_coeffs().span()); + // Non-zero high half in the last slot. + let mut tampered: Array = array![]; + let mut head = packed.span().slice(0, 28); + while let Some(f) = head.pop_front() { + tampered.append(*f); + } + tampered.append(TWO_POW_128); + assert!(unpack_512(tampered.span()).is_none()); + // Q^8 in the last slot: eight zero digits with a nonzero residue. + let mut tampered2: Array = array![]; + let mut head2 = packed.span().slice(0, 28); + while let Some(f) = head2.pop_front() { + tampered2.append(*f); + } + tampered2.append(Q_POW_8); + assert!(unpack_512(tampered2.span()).is_none()); + } +} diff --git a/packages/account/src/falcon_512/zq.cairo b/packages/account/src/falcon_512/zq.cairo new file mode 100644 index 000000000..67f5a7940 --- /dev/null +++ b/packages/account/src/falcon_512/zq.cairo @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/zq.cairo) + +//! Operations on the base ring Z_q (q = 12289). +//! +//! Coefficients are `u16` values in `[0, Q)` by construction — every function returns a +//! `% Q` result — with u32/u64 intermediates sized so the arithmetic cannot overflow. + +/// The Falcon modulus q = 12289 = 12·1024 + 1. +pub const Q: u16 = 12289; +pub const Q32: u32 = 12289; +pub const Q64: u64 = 12289; + +/// Largest centered-low value: (Q-1)/2. Coefficients above this represent negatives. +pub const Q_HALF: u16 = 6144; + +/// Add two values modulo Q. +#[inline(always)] +pub fn add_mod(a: u16, b: u16) -> u16 { + // a, b < Q so a + b <= 24576 < 2^16: the checked u16 add never overflows, and a + // single conditional subtraction reduces the sum. + let d = a + b; + if d >= Q { + d - Q + } else { + d + } +} + +/// Subtract two values modulo Q, via a + Q - b and one conditional subtraction. +#[inline(always)] +pub fn sub_mod(a: u16, b: u16) -> u16 { + // a < Q so a + Q <= 24577 < 2^16; a + Q - b is in [1, 2Q-1]. + let d = a + Q - b; + if d >= Q { + d - Q + } else { + d + } +} + +/// Multiply two values modulo Q. +#[inline(always)] +pub fn mul_mod(a: u16, b: u16) -> u16 { + let a: u32 = a.into(); + let b: u32 = b.into(); + // a·b <= 12288² < 2^31. + let res = (a * b) % Q32; + res.try_into().unwrap() +} + +/// Squared centered representative of a coefficient, as felt252: +/// x ∈ [0, 6144] → x²; x ∈ [6145, 12288] → (Q - x)². +#[inline(always)] +pub fn center_sq(coeff: u16) -> felt252 { + if coeff <= Q_HALF { + let x: felt252 = coeff.into(); + x * x + } else { + let x: felt252 = (Q - coeff).into(); + x * x + } +} + +/// Squared centered representative of `a - b` for canonical coefficients. +#[inline(always)] +pub(crate) fn centered_difference_sq(a: u16, b: u16) -> felt252 { + let difference = if a >= b { + a - b + } else { + b - a + }; + let centered = if difference <= Q_HALF { + difference + } else { + Q - difference + }; + let centered: felt252 = centered.into(); + centered * centered +} + +#[cfg(test)] +mod tests { + use super::{Q, add_mod, center_sq, centered_difference_sq, mul_mod, sub_mod}; + + #[test] + fn test_add_mod_wraps() { + assert_eq!(add_mod(12288, 1), 0); + assert_eq!(add_mod(12288, 12288), 12287); // (-1) + (-1) = -2 = q - 2 + assert_eq!(add_mod(0, 0), 0); + } + + #[test] + fn test_sub_mod_wraps() { + assert_eq!(sub_mod(0, 1), 12288); + assert_eq!(sub_mod(1, 1), 0); + assert_eq!(sub_mod(12288, 12287), 1); + } + + #[test] + fn test_mul_mod() { + assert_eq!(mul_mod(12288, 12288), 1); // (-1)² = 1 + assert_eq!(mul_mod(0, 12288), 0); + // SQR1 = 1479 is a square root of -1 mod q + assert_eq!(mul_mod(1479, 1479), Q - 1); + // 2 · 6145 = 12290 = 1 mod q (6145 = 2⁻¹) + assert_eq!(mul_mod(2, 6145), 1); + } + + #[test] + fn test_center_sq() { + assert_eq!(center_sq(0), 0); + assert_eq!(center_sq(1), 1); + assert_eq!(center_sq(6144), 6144 * 6144); // largest low-half value + assert_eq!(center_sq(6145), 6144 * 6144); // q - 6145 = 6144 + assert_eq!(center_sq(12288), 1); // -1 centered is ±1 + } + + #[test] + fn test_centered_difference_sq_boundaries_and_equivalence() { + assert_eq!(centered_difference_sq(0, 0), 0); + assert_eq!(centered_difference_sq(0, 1), 1); + assert_eq!(centered_difference_sq(1, 0), 1); + assert_eq!(centered_difference_sq(0, 6144), 6144 * 6144); + assert_eq!(centered_difference_sq(0, 6145), 6144 * 6144); + assert_eq!(centered_difference_sq(0, 12288), 1); + assert_eq!(centered_difference_sq(12288, 0), 1); + + let boundaries: [u16; 7] = [0, 1, 6143, 6144, 6145, 12287, 12288]; + for a in boundaries.span() { + for b in boundaries.span() { + assert_eq!(centered_difference_sq(*a, *b), center_sq(sub_mod(*a, *b))); + } + } + } + + // Probe retained from the port plan: `[u16; N]` const tables are the format the + // generated NTT root tables use. + const PROBE_TABLE: [u16; 3] = [0, 6144, 12288]; + + #[test] + fn test_const_u16_table() { + let t = PROBE_TABLE.span(); + assert_eq!(*t.at(0), 0); + assert_eq!(*t.at(1), 6144); + assert_eq!(*t.at(2), 12288); + } +} diff --git a/packages/account/src/lib.cairo b/packages/account/src/lib.cairo index 93ec3545e..b872440d9 100644 --- a/packages/account/src/lib.cairo +++ b/packages/account/src/lib.cairo @@ -1,6 +1,7 @@ pub mod account; pub mod eth_account; pub mod extensions; +pub mod falcon_512; #[cfg(test)] mod tests; @@ -8,3 +9,4 @@ pub mod utils; pub use account::AccountComponent; pub use eth_account::EthAccountComponent; +pub use falcon_512::{Falcon512ShakeAccount, Falcon512ShakeDirectAccount}; diff --git a/packages/account/src/tests.cairo b/packages/account/src/tests.cairo index 1d84fc250..4ed994f9b 100644 --- a/packages/account/src/tests.cairo +++ b/packages/account/src/tests.cairo @@ -1,6 +1,9 @@ mod extensions; +mod falcon_512_fixture; mod test_account; mod test_eth_account; +mod test_falcon_512; +mod test_falcon_512_ntt; mod test_secp256_point; mod test_signature; diff --git a/packages/account/src/tests/falcon_512_fixture.cairo b/packages/account/src/tests/falcon_512_fixture.cairo new file mode 100644 index 000000000..17cf0435a --- /dev/null +++ b/packages/account/src/tests/falcon_512_fixture.cairo @@ -0,0 +1,111 @@ +//! Generated legacy Falcon-512 submission-algorithm test vector — do not edit by hand. +//! +//! The fixture contains a genuine tprest/falcon.py keypair and signature over +//! `BENCH_MSG`, using falcon.py's SHAKE-256 hash-to-point. Its felt encoding is specific +//! to these Cairo accounts and is not the serialized signature format of a NIST standard. +//! salt = 0x4ad23197d576f705594ac172f4fca80e3338e991b8b55e269d421aa1522cd0aa2490034127651fc2. + +/// The benchmark message hash ('BENCH_MSG'). +pub fn msg() -> felt252 { + 1222479111879746474823 +} + +/// Packed NTT-domain public key h (29 felts). +pub fn public_key() -> Array { + array![ + 0x4b5883f5c89b0d4556d3c04683f7773007c23e91e17fd81f7c07e44e1723b16, + 0x80828f753351be72b867969629e57f03c53eec73696ed2a42f46e0731ebd18, + 0xa3526ad70f16bd9a22c88f50b6827e0313e613847af64b94c907ee28fddfba, + 0x4c00d1147cd00b633e46aea6dd7b821029a674204e7684da2a46353e1bef622, + 0x38e739bf5c8e77dff055d5ca6fc46f30134a2f6761fcde7291a84b642cc8870, + 0x2f15c3f7c1440e382e190c38c1405090314d0f38571505dbd139c39f2b16f11, + 0x5f608e2270192abd47c2b841f226520173d949c07dec39370e3fde1d38f5bc, + 0x29e50592fdb70623642bc59db478bbc017345a2ef3c8aa24fcfb8fbdbad82d9, + 0x24d3c1f17b53784199b2ba981fa556a0119d898c9752789105fff98c1b9cd76, + 0x9f5217536d557a0554fa6981843049023a282890b2c50117e7b06f23aa46b6, + 0x14e85ff168a577fdcf4645c2737f54002181be04e75f31f37e647b665cd71e2, + 0x4ed6d9d92397dd450dade5c08473a401ade31eba36b0058303b60540e141d6, + 0x4962bdf39b526379cc6f943a8651f4a009eb2008a088bdf0c700cd8f8b707ff, + 0x1e5722f7a6b5849d2dba1482e1b6c9f0176383d44cec027385c9ee3b60adccf, + 0x32ddae92481958f566883e90d9db7c5040b7b5cfb4768a8f4fd03aa33151687, + 0x2a40da74f65c493097754208c90f4e1032412af5c4c07ecd5551108bd89e6d7, + 0x4445ae0aaaa0249d4de7e2fc77a49ba016ece2eae0297ad2e2e617b5f36ed8d, + 0x4323277a8e1ebd7cdfe0b0b291277cb03138b7e1aac1316213f3b8093bcf0ae, + 0x48db6dc62abdf00d5a4f00f67486fce040f0b092dbf4f512b975f7dd8e3867b, + 0x367deb3eeb11eefda6708f11dc0138004d0534582489f8fafb2f2fd47f371d, + 0x40752ed0116a666130d5b8cc3746a400287fab22f80a41057b56bf8a54db3a6, + 0x3843a1c0c723b9abd43c6fb84928f6a028232d1102eb631ce1e7de44c871599, + 0x303137e0620a78b84c0dfb2b44a322026c291b4f357c1beef235e802a5939a, + 0x3d8037ba551320cc9be7f4b14e1ebe1000c59f734b66e3fdd527d41a1200860, + 0x1fbd87f9480222de9609ebda655338b04353863afce87ee3ed6349e66389e15, + 0x2f08581081c3f60a0c58b7e4d70c45103c1ea9f33e76209c09340a06cf990d3, + 0x34f19aad64c761cc21f74cf4554f8a00611661c1596564c01ba296469d4c54, + 0x1627799a0710d18e5a2d46127da74f0040fdb0167400b5927ee792de206e8b9, + 0x168f7f69a1d0308fa7b6a298cea8, + ] +} + +/// Signature: packed s1 (29 felts) || salt (2 felts) || packed mul_hint (29 felts). +pub fn signature() -> Array { + array![ + 0xc3876e3982b541f962e4f77ff8382000c1f7eb26dc69208bab3afdc3dc1d0, + 0x4c12a028535570b1ba507ce01cc515304b2f01b9554ceec9023ab26b30f105e, + 0x7b7405cb5587555e7d99b9d65225b0005e8e09b2b645a4a11b3a3c5bc0fa2, + 0x4b90c64a108041f3dbce09cc6a91f1900279253640884bc8434a2fbf9c6c264, + 0x4cc2ec3bd4d44bd43cafa7ae0b83edf001b8c67cdce1653683c34850b9393db, + 0x59cb22d890fb18658c1d0d8ea206100178a97902b5adf97845649c0281f92, + 0x4ce48f8b68eff13a34c337d9e01acd700193e8ac7c158019b329e7a0917cfe4, + 0x4b5a55606078b3e0bc98ff6b219710c0006b716700749407bb9c7e45ec39499, + 0x5020f0241dc5f42b38ad5eff0310f0009d1a3470afdab515ace284180ff6d, + 0x81d4f61c65ececef5a64a1fbad2d000175733e7e7e1b3febff606bf0b2fcb, + 0x4b0d5b1b5547a96157ada24d1836da404cb9552b1834b1762c5be67eb82bd76, + 0xa389199f55f1e4bdda4285fcf933d04c0f66970291d36368569e1b3704f28, + 0x4c9618f6ec02f9b9bb8fdd066e6d4670007b69f057ec7e5d2271a362bc6ddd2, + 0xe6c9028315d81274dcf007bb761fb0014a35dcc154df026ee2da9cf6d4ef9, + 0x4c512ded713bd52e8ffcf4d829bb01504c6783ff3d7697f39e04296799dfa44, + 0x4cd7c362fcd15d9683f84d07627d2610009eb09842f9be47ce597b19758af46, + 0x2342c2f878bdfbbf4ddb714447317c0000b3106f585585761e9f927c055e00, + 0x12bb87c597fbe680e7f3be18d4513604cbae51d0ec49525ec7e1bf47d36005, + 0x4ce639a281d0ecab39e59b828bf3feb04cb14fc8dcd4037cbafd2218af7bf45, + 0x1c5997da66c46510de044b2039e07404bff62803296c615893c6de7628519d, + 0x34e51bc4d8704b0e55abaf32f804204c51240b70f81d7936168a5523dfeb2, + 0x4c4453721150ab5ffb65c9e75f2e08304bef585531b0898edd50c55d0019ffc, + 0x1c40344b02b6ba74a6c2e5792ad0c100058266388043d278b0d608094a3299, + 0x4ca7b1ac5802cb8c22a18dfbf2cf20804c4de9022dfb608b3974059dd5feed3, + 0x4c3de0cfec3b63c7468556d7ea4af24000280c43023a9756b0fba3cf730fda4, + 0xc060cb704a3a464022f951996321e000380a602e6ac8fd8ff9e30c28eefb7, + 0x188ac6d6736d1058c8cf45a1cfc0de04bac0cefe9a65354de0d9fdcdec215a, + 0x7509d6424f2debe0e4ca0e2e7e085000a05000229fbffa62948066e727277, + 0x199a938840456e14796bb66d9ebb, 0x91e938330ea8fcf472c14a5905f776d59731d24a, + 0xc21f652741039024aad02c52a11a429d265eb5b8, + 0x107be11221f9c4b7381cf0c3970444f03701fbcc120a592b8121062e5b59b9a, + 0x1e44009df2457c77dacad9b85613b650185cea7da868df396b735f790e36edb, + 0xb99d364347b55e7f73e01761fe10b301194ac214fbad5128efd03c4e56b82c, + 0x220473e38c276e61815e5384cb3612801250c05d9ebf0099d6da0a94885de10, + 0x1c58455b7629f447c77aa9773af7ffe02d094284c08cb92fdfe41cd0b098b50, + 0xaec193cb1033aa746adea102dd829a04c724ddad243a4d6ba745f9c6cccb78, + 0x34bd9bf4ff6a789a4208c1a4feb5c8a00b76706eda239e170496b046c555f4d, + 0x20dd278182b85864a1218708ae9a78003f80ee5d5aa23e76f865ebfae119d17, + 0x56516fef301e3a0f8f7b206b5bf53e01f42a57a26c2bf7ced9430a25c75617, + 0x4be6010beac3f16464bbc71723cdbb8029b329a07e2c9032d603e14048d5488, + 0x13eab76df94131e0fb4e903ca57caeb0423778dfb37815e87b5f70f622e6498, + 0x61317a493aad9b43e3e0c6bfb86b1b043cbe3e29d79a80f71887dc427a79c3, + 0x2e9667e027151b686f33ebbccc12e2b020b21c61b50ee07e71b3ae9b86290d4, + 0xc3a660592015363eabca73a1a3523f045d06263282512faf6c0ab10c6468a3, + 0x1faba087a7577afe1a5a21e51d7cdb0044d90ddfaa348b6a341dc26222576ae, + 0x34552f457ee4bc2e897521604c4ed63026eb0a14d1d9c18c9b069c7b14f78c9, + 0x786e4c34f2393cfb6c8bdfea0606a602e85c417aa840aaccc320f69eb64a60, + 0xf890af43edfa6cdbc89198d829749702914725c367eb262dd7141723152f51, + 0x49d602c25063dc3518ab42d7c9287ac00c41590eea487fea340a8d9eb66b1d3, + 0x1c8306b4665fa20daf5b8bd4deb5c34047bfabcfad5c16ee6a3e5987e0dc17d, + 0x43e929dbb8942b8b0947f95cd683da600684e1f86e95272f4710dad3ed04d0f, + 0x8617cd70a49f8bc9305336233784e101f2dda080df6c21f4a160b891828bdd, + 0x9056f9a67d7852ac619169ec80e8cb010080f040f169c7e84d213297708091, + 0x2687075c64f19db4f68fcc9612919d201d434101448db72e4300ecf097e0795, + 0x23122edeaeef1949ad8c7724d755485044272d01aa40fd080c80c15f4075a07, + 0x1edf55a60cd99472510ee283f06576f049cce7d601e6a0dab3c7ff637df457f, + 0x26a621693f4b69a4b92d89efdbf558f02ce0f378d7a885a1637c4dac8a341a6, + 0x2bbb2baf68ae0d7c9bd4f8b21e476ab019fc66a54b84c25e34f54146d62dc29, + 0x7706513a985e051d497fc9cb2a3, + ] +} diff --git a/packages/account/src/tests/test_falcon_512.cairo b/packages/account/src/tests/test_falcon_512.cairo new file mode 100644 index 000000000..3bddd69e9 --- /dev/null +++ b/packages/account/src/tests/test_falcon_512.cairo @@ -0,0 +1,443 @@ +use core::num::traits::Zero; +use openzeppelin_interfaces::accounts::{ + IDeclarerDispatcher, IDeclarerDispatcherTrait, IFeltArrayDeployableDispatcher, + IFeltArrayDeployableDispatcherTrait, IFeltArrayPublicKeyDispatcher, + IFeltArrayPublicKeyDispatcherTrait, ISRC6Dispatcher, ISRC6DispatcherTrait, ISRC6_ID, +}; +use openzeppelin_interfaces::introspection::{ISRC5Dispatcher, ISRC5DispatcherTrait, ISRC5_ID}; +use openzeppelin_test_common::mocks::simple::{ISimpleMockDispatcher, ISimpleMockDispatcherTrait}; +use openzeppelin_testing as utils; +use openzeppelin_testing::constants::{ + CALLER, MIN_TRANSACTION_VERSION, QUERY_OFFSET, QUERY_VERSION, SALT, ZERO, +}; +use snforge_std::{ + start_cheat_caller_address, start_cheat_signature_global, start_cheat_transaction_hash_global, + start_cheat_transaction_version_global, +}; +use starknet::ContractAddress; +use starknet::account::Call; +use crate::falcon_512::{ + DIRECT_SIGNATURE_FELTS, Falcon512ShakeDirectVerifier, Falcon512ShakeVerifier, PUBLIC_KEY_FELTS, + SIGNATURE_FELTS, +}; +use super::falcon_512_fixture::{msg, public_key, signature}; + +const Q_POW_9: felt252 = 6392178558614694273495691177456939009; +const TWO_POW_160: felt252 = 0x10000000000000000000000000000000000000000; + +fn copy_prefix(mut values: Span, length: u32) -> Array { + let mut output = array![]; + let mut index = 0; + while index != length { + output.append(*values.pop_front().unwrap()); + index += 1; + } + output +} + +fn with_replaced(mut values: Span, target: u32, replacement: felt252) -> Array { + let mut output = array![]; + let mut index = 0; + while let Some(value) = values.pop_front() { + if index == target { + output.append(replacement); + } else { + output.append(*value); + } + index += 1; + } + output +} + +fn with_swapped(values: Span, first: u32, second: u32) -> Array { + let first_value = *values.at(first); + let second_value = *values.at(second); + with_replaced(with_replaced(values, first, second_value).span(), second, first_value) +} + +fn with_appended(mut values: Span, value: felt252) -> Array { + let mut output = array![]; + while let Some(current) = values.pop_front() { + output.append(*current); + } + output.append(value); + output +} + +fn direct_signature() -> Array { + copy_prefix(signature().span(), DIRECT_SIGNATURE_FELTS) +} + +fn deploy_account(contract_name: ByteArray) -> (ContractAddress, felt252) { + let contract_class = utils::declare_class(contract_name); + let mut calldata = array![]; + public_key().serialize(ref calldata); + let contract_address = utils::deploy(contract_class, calldata); + (contract_address, contract_class.class_hash.into()) +} + +fn setup_transaction( + contract_address: ContractAddress, + tx_hash: felt252, + tx_signature: Array, + version: felt252, +) { + start_cheat_signature_global(tx_signature.span()); + start_cheat_transaction_hash_global(tx_hash); + start_cheat_transaction_version_global(version); + start_cheat_caller_address(contract_address, ZERO); +} + +fn execute_increase( + account: ISRC6Dispatcher, target: ContractAddress, amount: felt252, version: felt252, +) { + start_cheat_transaction_version_global(version); + let calldata = array![amount]; + let call = Call { + to: target, selector: selector!("increase_balance"), calldata: calldata.span(), + }; + account.__execute__(array![call]); +} + +#[test] +fn test_verifiers_accept_reference_signature_and_reject_cross_layout() { + let key = public_key(); + let hint_signature = signature(); + let direct_signature = direct_signature(); + + assert!(Falcon512ShakeVerifier::verify(msg(), key.span(), hint_signature.span())); + assert!(Falcon512ShakeDirectVerifier::verify(msg(), key.span(), direct_signature.span())); + assert!(!Falcon512ShakeVerifier::verify(msg(), key.span(), direct_signature.span())); + assert!(!Falcon512ShakeDirectVerifier::verify(msg(), key.span(), hint_signature.span())); +} + +#[test] +fn test_hint_verifier_rejects_all_malformed_layout_branches() { + let key = public_key(); + let valid_signature = signature(); + + assert!( + !Falcon512ShakeVerifier::verify( + msg(), key.span().slice(0, PUBLIC_KEY_FELTS - 1), valid_signature.span(), + ), + ); + assert!( + !Falcon512ShakeVerifier::verify( + msg(), with_appended(key.span(), 0).span(), valid_signature.span(), + ), + ); + assert!( + !Falcon512ShakeVerifier::verify( + msg(), key.span(), valid_signature.span().slice(0, SIGNATURE_FELTS - 1), + ), + ); + assert!( + !Falcon512ShakeVerifier::verify( + msg(), key.span(), with_appended(valid_signature.span(), 0).span(), + ), + ); + assert!( + !Falcon512ShakeVerifier::verify( + msg(), with_replaced(key.span(), 0, Q_POW_9).span(), valid_signature.span(), + ), + ); + assert!( + !Falcon512ShakeVerifier::verify( + msg(), key.span(), with_replaced(valid_signature.span(), 0, Q_POW_9).span(), + ), + ); + assert!( + !Falcon512ShakeVerifier::verify( + msg(), key.span(), with_replaced(valid_signature.span(), 31, Q_POW_9).span(), + ), + ); + assert!( + !Falcon512ShakeVerifier::verify( + msg(), key.span(), with_replaced(valid_signature.span(), 29, TWO_POW_160).span(), + ), + ); + assert!( + !Falcon512ShakeVerifier::verify( + msg(), key.span(), with_replaced(valid_signature.span(), 30, TWO_POW_160).span(), + ), + ); +} + +#[test] +fn test_hint_verifier_rejects_every_authenticated_input_tamper() { + let key = public_key(); + let valid_signature = signature(); + + assert!(!Falcon512ShakeVerifier::verify(msg() + 1, key.span(), valid_signature.span())); + assert!( + !Falcon512ShakeVerifier::verify( + msg(), with_swapped(key.span(), 0, 1).span(), valid_signature.span(), + ), + ); + assert!( + !Falcon512ShakeVerifier::verify( + msg(), key.span(), with_swapped(valid_signature.span(), 0, 1).span(), + ), + ); + assert!( + !Falcon512ShakeVerifier::verify( + msg(), + key.span(), + with_replaced(valid_signature.span(), 29, *valid_signature.at(29) + 1).span(), + ), + ); + assert!( + !Falcon512ShakeVerifier::verify( + msg(), + key.span(), + with_replaced(valid_signature.span(), 31, *valid_signature.at(31) + 1).span(), + ), + ); +} + +#[test] +fn test_direct_verifier_rejects_all_malformed_layout_branches() { + let key = public_key(); + let valid_signature = direct_signature(); + + assert!( + !Falcon512ShakeDirectVerifier::verify( + msg(), key.span().slice(0, PUBLIC_KEY_FELTS - 1), valid_signature.span(), + ), + ); + assert!( + !Falcon512ShakeDirectVerifier::verify( + msg(), with_appended(key.span(), 0).span(), valid_signature.span(), + ), + ); + assert!( + !Falcon512ShakeDirectVerifier::verify( + msg(), key.span(), valid_signature.span().slice(0, DIRECT_SIGNATURE_FELTS - 1), + ), + ); + assert!( + !Falcon512ShakeDirectVerifier::verify( + msg(), key.span(), with_appended(valid_signature.span(), 0).span(), + ), + ); + assert!( + !Falcon512ShakeDirectVerifier::verify( + msg(), with_replaced(key.span(), 0, Q_POW_9).span(), valid_signature.span(), + ), + ); + assert!( + !Falcon512ShakeDirectVerifier::verify( + msg(), key.span(), with_replaced(valid_signature.span(), 0, Q_POW_9).span(), + ), + ); + assert!( + !Falcon512ShakeDirectVerifier::verify( + msg(), key.span(), with_replaced(valid_signature.span(), 29, TWO_POW_160).span(), + ), + ); + assert!( + !Falcon512ShakeDirectVerifier::verify( + msg(), key.span(), with_replaced(valid_signature.span(), 30, TWO_POW_160).span(), + ), + ); +} + +#[test] +fn test_direct_verifier_rejects_every_authenticated_input_tamper() { + let key = public_key(); + let valid_signature = direct_signature(); + + assert!(!Falcon512ShakeDirectVerifier::verify(msg() + 1, key.span(), valid_signature.span())); + assert!( + !Falcon512ShakeDirectVerifier::verify( + msg(), with_swapped(key.span(), 0, 1).span(), valid_signature.span(), + ), + ); + assert!( + !Falcon512ShakeDirectVerifier::verify( + msg(), key.span(), with_swapped(valid_signature.span(), 0, 1).span(), + ), + ); + assert!( + !Falcon512ShakeDirectVerifier::verify( + msg(), + key.span(), + with_replaced(valid_signature.span(), 29, *valid_signature.at(29) + 1).span(), + ), + ); +} + +#[test] +fn test_hint_tampering_does_not_change_direct_signature() { + let key = public_key(); + let valid_signature = signature(); + let tampered = with_replaced(valid_signature.span(), 31, *valid_signature.at(31) + 1); + + assert!(!Falcon512ShakeVerifier::verify(msg(), key.span(), tampered.span())); + assert!( + Falcon512ShakeDirectVerifier::verify( + msg(), key.span(), copy_prefix(tampered.span(), DIRECT_SIGNATURE_FELTS).span(), + ), + ); +} + +#[test] +fn test_accounts_expose_exact_public_key_and_interfaces() { + let (hint_address, _) = deploy_account("Falcon512ShakeAccount"); + let (direct_address, _) = deploy_account("Falcon512ShakeDirectAccount"); + + for address in [hint_address, direct_address].span() { + let key_dispatcher = IFeltArrayPublicKeyDispatcher { contract_address: *address }; + assert_eq!(key_dispatcher.get_public_key().span(), public_key().span()); + + let src5 = ISRC5Dispatcher { contract_address: *address }; + assert!(src5.supports_interface(ISRC5_ID)); + assert!(src5.supports_interface(ISRC6_ID)); + assert!(!src5.supports_interface(0x123456)); + } +} + +#[test] +fn test_accounts_is_valid_signature_return_convention() { + let (hint_address, _) = deploy_account("Falcon512ShakeAccount"); + let (direct_address, _) = deploy_account("Falcon512ShakeDirectAccount"); + let hint = ISRC6Dispatcher { contract_address: hint_address }; + let direct = ISRC6Dispatcher { contract_address: direct_address }; + + assert_eq!(hint.is_valid_signature(msg(), signature()), starknet::VALIDATED); + assert!(hint.is_valid_signature(msg() + 1, signature()).is_zero()); + assert!(hint.is_valid_signature(msg(), direct_signature()).is_zero()); + + assert_eq!(direct.is_valid_signature(msg(), direct_signature()), starknet::VALIDATED); + assert!(direct.is_valid_signature(msg() + 1, direct_signature()).is_zero()); + assert!(direct.is_valid_signature(msg(), signature()).is_zero()); +} + +#[test] +fn test_all_validation_entrypoints_accept_hint_account_signature() { + let (address, class_hash) = deploy_account("Falcon512ShakeAccount"); + setup_transaction(address, msg(), signature(), MIN_TRANSACTION_VERSION); + + let src6 = ISRC6Dispatcher { contract_address: address }; + let declarer = IDeclarerDispatcher { contract_address: address }; + let deployable = IFeltArrayDeployableDispatcher { contract_address: address }; + assert_eq!(src6.__validate__(array![]), starknet::VALIDATED); + assert_eq!(declarer.__validate_declare__(class_hash), starknet::VALIDATED); + assert_eq!(deployable.__validate_deploy__(class_hash, SALT, public_key()), starknet::VALIDATED); +} + +#[test] +fn test_all_validation_entrypoints_accept_direct_account_signature() { + let (address, class_hash) = deploy_account("Falcon512ShakeDirectAccount"); + setup_transaction(address, msg(), direct_signature(), MIN_TRANSACTION_VERSION); + + let src6 = ISRC6Dispatcher { contract_address: address }; + let declarer = IDeclarerDispatcher { contract_address: address }; + let deployable = IFeltArrayDeployableDispatcher { contract_address: address }; + assert_eq!(src6.__validate__(array![]), starknet::VALIDATED); + assert_eq!(declarer.__validate_declare__(class_hash), starknet::VALIDATED); + assert_eq!(deployable.__validate_deploy__(class_hash, SALT, public_key()), starknet::VALIDATED); +} + +#[test] +#[should_panic(expected: 'Account: invalid signature')] +fn test_hint_validate_rejects_invalid_signature() { + let (address, _) = deploy_account("Falcon512ShakeAccount"); + setup_transaction(address, msg() + 1, signature(), MIN_TRANSACTION_VERSION); + ISRC6Dispatcher { contract_address: address }.__validate__(array![]); +} + +#[test] +#[should_panic(expected: 'Account: invalid signature')] +fn test_direct_validate_rejects_invalid_signature() { + let (address, _) = deploy_account("Falcon512ShakeDirectAccount"); + setup_transaction(address, msg() + 1, direct_signature(), MIN_TRANSACTION_VERSION); + ISRC6Dispatcher { contract_address: address }.__validate__(array![]); +} + +#[test] +#[should_panic(expected: 'Account: invalid signature')] +fn test_validate_declare_rejects_invalid_signature() { + let (address, class_hash) = deploy_account("Falcon512ShakeAccount"); + setup_transaction(address, msg() + 1, signature(), MIN_TRANSACTION_VERSION); + IDeclarerDispatcher { contract_address: address }.__validate_declare__(class_hash); +} + +#[test] +#[should_panic(expected: 'Account: invalid signature')] +fn test_validate_deploy_rejects_invalid_signature() { + let (address, class_hash) = deploy_account("Falcon512ShakeAccount"); + setup_transaction(address, msg() + 1, signature(), MIN_TRANSACTION_VERSION); + IFeltArrayDeployableDispatcher { contract_address: address } + .__validate_deploy__(class_hash, SALT, public_key()); +} + +#[test] +#[should_panic] +fn test_constructor_rejects_wrong_public_key_length() { + let contract_class = utils::declare_class("Falcon512ShakeAccount"); + let mut calldata = array![]; + copy_prefix(public_key().span(), PUBLIC_KEY_FELTS - 1).serialize(ref calldata); + utils::deploy(contract_class, calldata); +} + +#[test] +#[should_panic] +fn test_constructor_rejects_noncanonical_public_key() { + let contract_class = utils::declare_class("Falcon512ShakeDirectAccount"); + let mut calldata = array![]; + with_replaced(public_key().span(), 0, Q_POW_9).serialize(ref calldata); + utils::deploy(contract_class, calldata); +} + +#[test] +fn test_execute_empty_single_multicall_and_supported_versions() { + let (address, _) = deploy_account("Falcon512ShakeAccount"); + setup_transaction(address, msg(), signature(), MIN_TRANSACTION_VERSION); + let account = ISRC6Dispatcher { contract_address: address }; + account.__execute__(array![]); + + let target = utils::declare_and_deploy("SimpleMock", array![]); + let simple = ISimpleMockDispatcher { contract_address: target }; + execute_increase(account, target, 1, MIN_TRANSACTION_VERSION); + execute_increase(account, target, 2, MIN_TRANSACTION_VERSION + 1); + execute_increase(account, target, 4, QUERY_VERSION); + execute_increase(account, target, 8, QUERY_VERSION + 1); + + start_cheat_transaction_version_global(MIN_TRANSACTION_VERSION); + let calldata_a = array![16]; + let calldata_b = array![32]; + let call_a = Call { + to: target, selector: selector!("increase_balance"), calldata: calldata_a.span(), + }; + let call_b = Call { + to: target, selector: selector!("increase_balance"), calldata: calldata_b.span(), + }; + account.__execute__(array![call_a, call_b]); + assert_eq!(simple.get_balance(), 63); +} + +#[test] +#[should_panic(expected: 'Account: invalid tx version')] +fn test_execute_rejects_zero_version() { + let (address, _) = deploy_account("Falcon512ShakeAccount"); + setup_transaction(address, msg(), signature(), MIN_TRANSACTION_VERSION - 1); + ISRC6Dispatcher { contract_address: address }.__execute__(array![]); +} + +#[test] +#[should_panic(expected: 'Account: invalid tx version')] +fn test_execute_rejects_query_offset_without_version() { + let (address, _) = deploy_account("Falcon512ShakeAccount"); + setup_transaction(address, msg(), signature(), QUERY_OFFSET); + ISRC6Dispatcher { contract_address: address }.__execute__(array![]); +} + +#[test] +#[should_panic(expected: 'Account: invalid caller')] +fn test_execute_rejects_contract_caller() { + let (address, _) = deploy_account("Falcon512ShakeAccount"); + setup_transaction(address, msg(), signature(), MIN_TRANSACTION_VERSION); + start_cheat_caller_address(address, CALLER); + ISRC6Dispatcher { contract_address: address }.__execute__(array![]); +} diff --git a/packages/account/src/tests/test_falcon_512_ntt.cairo b/packages/account/src/tests/test_falcon_512_ntt.cairo new file mode 100644 index 000000000..2374ae39b --- /dev/null +++ b/packages/account/src/tests/test_falcon_512_ntt.cairo @@ -0,0 +1,153 @@ +use crate::falcon_512::ntt::engine::{intt, ntt}; +#[cfg(feature: 'falcon_fast_tests')] +use crate::falcon_512::ntt::falcon512::PRODUCT_BITS; +use crate::falcon_512::ntt::falcon512::{ + PRODUCT_BOUND_FELT, REDUCED_BITS, config, config_for_degree, +}; +#[cfg(feature: 'falcon_fast_tests')] +use crate::falcon_512::ntt::falcon512_fast::{ + ntt_falcon512_fast_u16_unchecked, ntt_falcon512_fast_unchecked, +}; +use crate::falcon_512::ntt::roots_felt::get_even_roots_felt; +use crate::falcon_512::ntt::roots_scaled::get_scaled_inv_roots; + +fn as_felts(mut values: Span) -> Array { + let mut output = array![]; + while let Some(value) = values.pop_front() { + output.append((*value).into()); + } + output +} + +fn zeros() -> Array { + let mut values = array![]; + while values.len() != 512 { + values.append(0); + } + values +} + +fn max_values() -> Array { + let mut values = array![]; + while values.len() != 512 { + values.append(12288); + } + values +} + +fn basis(index: u32) -> Array { + let mut values = array![]; + let mut current = 0; + while current != 512 { + if current == index { + values.append(1); + } else { + values.append(0); + } + current += 1; + } + values +} + +fn pseudorandom_values(seed: u64) -> Array { + let mut values = array![]; + let mut state = seed; + while values.len() != 512 { + state = (state * 1664525 + 1013904223) % 0x100000000; + values.append((state % 12289).try_into().unwrap()); + } + values +} + +#[cfg(feature: 'falcon_fast_tests')] +fn assert_fast_matches_generic(values: Span) { + let input = as_felts(values); + let generic = ntt(input.span(), @config()); + let fast_u16 = ntt_falcon512_fast_u16_unchecked(values); + let fast_felt = ntt_falcon512_fast_unchecked(input.span()); + assert_eq!(generic.span(), as_felts(fast_u16.span()).span()); + assert_eq!(generic.span(), fast_felt.span()); +} + +#[cfg(feature: 'falcon_fast_tests')] +#[test] +fn test_fast_ntt_matches_generic_reference_on_adversarial_inputs() { + assert_fast_matches_generic(zeros().span()); + assert_fast_matches_generic(max_values().span()); + assert_fast_matches_generic(basis(0).span()); + assert_fast_matches_generic(basis(511).span()); + assert_fast_matches_generic(pseudorandom_values(7).span()); +} + +#[cfg(feature: 'falcon_fast_tests')] +#[test] +fn test_fast_ntt_inverse_roundtrip() { + let values = pseudorandom_values(19); + let transformed = ntt_falcon512_fast_u16_unchecked(values.span()); + let transformed = as_felts(transformed.span()); + let recovered = intt(transformed.span(), REDUCED_BITS, 12289, @config()); + assert_eq!(recovered.span(), as_felts(values.span()).span()); +} + +#[cfg(feature: 'falcon_fast_tests')] +#[test] +fn test_ntt_negacyclic_product() { + let left = ntt_falcon512_fast_u16_unchecked(basis(1).span()); + let right = ntt_falcon512_fast_u16_unchecked(basis(511).span()); + let mut products = array![]; + let mut left = left.span(); + let mut right = right.span(); + while let Some(a) = left.pop_front() { + let a: felt252 = (*a).into(); + let b: felt252 = (*right.pop_front().unwrap()).into(); + products.append(a * b); + } + + let product = intt(products.span(), PRODUCT_BITS, PRODUCT_BOUND_FELT, @config()); + let mut expected = array![12288]; + while expected.len() != 512 { + expected.append(0); + } + assert_eq!(product.span(), expected.span()); +} + +#[test] +#[should_panic(expected: 'intt: bad input length')] +fn test_intt_rejects_wrong_length() { + intt(array![].span(), REDUCED_BITS, 12289, @config()); +} + +#[test] +#[should_panic(expected: 'intt: input too large')] +fn test_intt_rejects_excessive_input_bound() { + let values = as_felts(zeros().span()); + intt(values.span(), 120, PRODUCT_BOUND_FELT, @config()); +} + +#[test] +fn test_degree_four_roundtrip_exercises_reduction_tail() { + let values = array![1, 2, 3, 4]; + let cfg = config_for_degree(4, 2); + let transformed = ntt(values.span(), @cfg); + let recovered = intt(transformed.span(), REDUCED_BITS, 12289, @cfg); + assert_eq!(recovered.span(), values.span()); +} + +#[test] +fn test_intt_reduces_before_last_level() { + let values = array![0, 0, 0, 0]; + let recovered = intt(values.span(), 111, 1, @config_for_degree(4, 2)); + assert_eq!(recovered.span(), values.span()); +} + +#[test] +#[should_panic] +fn test_forward_roots_reject_unsupported_degree() { + get_even_roots_felt(3); +} + +#[test] +#[should_panic] +fn test_inverse_roots_reject_unsupported_degree() { + get_scaled_inv_roots(3); +} diff --git a/packages/interfaces/README.md b/packages/interfaces/README.md index 024264ed5..3a48af440 100644 --- a/packages/interfaces/README.md +++ b/packages/interfaces/README.md @@ -16,6 +16,8 @@ List of standardized interfaces, ABI traits and dispatchers. - `ISRC6CamelOnly` - `IDeclarer` - `IDeployable` +- `IFeltArrayDeployable` +- `IFeltArrayPublicKey` - `IPublicKey` - `IPublicKeyCamel` - `IEthDeployable` diff --git a/packages/interfaces/src/account/accounts.cairo b/packages/interfaces/src/account/accounts.cairo index d5d2cbc22..e1b90f8be 100644 --- a/packages/interfaces/src/account/accounts.cairo +++ b/packages/interfaces/src/account/accounts.cairo @@ -49,6 +49,20 @@ pub trait IDeployable { ) -> felt252; } +/// Deployment validation for accounts whose public key is encoded as a felt array. +#[starknet::interface] +pub trait IFeltArrayDeployable { + /// Validates a deploy-account transaction before deployment. + /// + /// Returns the short string 'VALID' if valid, otherwise it reverts. + fn __validate_deploy__( + self: @TState, + class_hash: felt252, + contract_address_salt: felt252, + public_key: Array, + ) -> felt252; +} + #[starknet::interface] pub trait IPublicKey { /// Returns the current public key of the account. @@ -60,6 +74,13 @@ pub trait IPublicKey { fn set_public_key(ref self: TState, new_public_key: felt252, signature: Span); } +/// Read access to a public key encoded as a felt array. +#[starknet::interface] +pub trait IFeltArrayPublicKey { + /// Returns the current public key of the account. + fn get_public_key(self: @TState) -> Array; +} + /// Adds camelCase support for `ISRC6`. #[starknet::interface] pub trait ISRC6CamelOnly { From bdd723e9d13f8bd7e7bab6fe66058b20ba7f1191 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Fri, 7 Aug 2026 16:03:19 +0200 Subject: [PATCH 2/6] feat: apply generic improvements --- LICENSE | 9 - packages/account/README.md | 26 +- packages/account/src/falcon_512.cairo | 9 +- packages/account/src/falcon_512/account.cairo | 235 +++++++++++++++++- packages/account/src/falcon_512/falcon.cairo | 2 +- .../src/falcon_512/falcon_512_shake.cairo | 22 +- .../falcon_512/falcon_512_shake_direct.cairo | 24 +- .../falcon_512/hashing/hash_to_point.cairo | 2 +- .../account/src/falcon_512/ntt/engine.cairo | 4 +- .../src/falcon_512/ntt/falcon512.cairo | 5 +- packages/account/src/tests.cairo | 1 + .../src/tests/falcon_512_fixture.cairo | 6 +- .../tests/falcon_512_rotation_fixture.cairo | 195 +++++++++++++++ .../account/src/tests/test_falcon_512.cairo | 214 +++++++++++++++- packages/interfaces/README.md | 6 +- .../interfaces/src/account/accounts.cairo | 51 +++- 16 files changed, 734 insertions(+), 77 deletions(-) create mode 100644 packages/account/src/tests/falcon_512_rotation_fixture.cairo diff --git a/LICENSE b/LICENSE index edef7d6fb..cdbf7bcad 100644 --- a/LICENSE +++ b/LICENSE @@ -19,12 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ---- - -Third-party notices - -Portions of this software (the NTT engine and parts of the Falcon-512 verifier) are -ported from s2morrow (https://github.com/feltroidprime/s2morrow and -https://github.com/starkware-bitcoin/s2morrow), -Copyright (c) 2025 StarkWare Industries Ltd., licensed under the MIT License. diff --git a/packages/account/README.md b/packages/account/README.md index 4b80df626..2b5b623a9 100644 --- a/packages/account/README.md +++ b/packages/account/README.md @@ -10,19 +10,33 @@ This crate provides components to implement account contracts that can be used f - `EthAccount` validates transactions from signatures over the [Secp256k1 curve](https://en.bitcoin.it/wiki/Secp256k1). -- `Falcon512ShakeAccount` validates legacy Falcon-512 submission-algorithm signatures - using SHAKE-256 hash-to-point and a verifier-bound product hint. +- `Falcon512ShakeAccount` validates the Falcon-512 verification relation from the + FALCON submission selected by NIST, using SHAKE-256 hash-to-point. Its + contract-specific signature encoding includes a verifier-checked polynomial-product + hint to reduce on-chain execution cost. -- `Falcon512ShakeDirectAccount` validates the corresponding hint-free signatures by +- `Falcon512ShakeDirectAccount` validates the same relation without the hint by recomputing the polynomial product on-chain. -> **WARNING:** The Falcon accounts use immutable keys and a contract-specific felt encoding. -> They do not implement a finalized NIST FN-DSA standard. Key loss, key compromise, or a -> verifier revision requires migration to a new account address. +> **WARNING:** These accounts implement the verification relation and SHAKE-256 hash-to-point +> used by FALCON as submitted to NIST. Their felt encodings are contract-specific, and the +> accounts do not claim conformance with FN-DSA/FIPS 206, which remains in development. + +Both Falcon accounts support owner-authorized key rotation by executing a self-call to +`set_public_key` or `setPublicKey`. As with the STARK-curve and Secp256k1 accounts, the +current key authorizes the outer account transaction and the new key signs a +domain-separated ownership-acceptance message. Rotation keeps the same account address. +This is not lost-key recovery: if the current private key is unavailable, these +entrypoints cannot rotate it, and the contracts include no independent recovery mechanism. +Changing the verifier code is a separate class-upgrade concern; these concrete contracts +do not embed an upgrade component. ### Interfaces - [`ISRC6`](https://docs.openzeppelin.com/contracts-cairo/3.x/api/account#ISRC6) +- `IFeltArrayDeployable` +- `IFeltArrayPublicKey` +- `IFeltArrayPublicKeyCamel` - [`ISRC9_V2`](https://docs.openzeppelin.com/contracts-cairo/3.x/api/account#ISRC9_V2) ### Components diff --git a/packages/account/src/falcon_512.cairo b/packages/account/src/falcon_512.cairo index 40e505f2b..cd36afff0 100644 --- a/packages/account/src/falcon_512.cairo +++ b/packages/account/src/falcon_512.cairo @@ -3,9 +3,12 @@ //! Falcon-512 SHAKE account contracts and their verification implementation. //! -//! These accounts implement the legacy Falcon submission algorithm with SHAKE-256 -//! hash-to-point. Their packed felt public keys and signatures are contract-specific; -//! they are not the encoding or finalized behavior of a NIST FN-DSA standard. +//! Both accounts implement the Falcon-512 verification relation from the FALCON submission +//! selected by NIST, using SHAKE-256 hash-to-point. The hint variant adds a verifier-checked +//! polynomial-product witness to its contract-specific signature encoding to reduce on-chain +//! execution cost; the direct variant recomputes that product on-chain. Their packed felt public +//! keys and signature encodings are contract-specific, and neither account claims conformance +//! with FN-DSA/FIPS 206. pub(crate) mod account; pub(crate) mod falcon; diff --git a/packages/account/src/falcon_512/account.cairo b/packages/account/src/falcon_512/account.cairo index 0ba3dfd68..56a05ead1 100644 --- a/packages/account/src/falcon_512/account.cairo +++ b/packages/account/src/falcon_512/account.cairo @@ -12,16 +12,22 @@ pub(crate) trait Falcon512SignatureVerifier { fn is_valid_public_key(public_key: Span) -> bool; } -/// Account component for immutable, array-encoded Falcon-512 public keys. +/// Account component for array-encoded Falcon-512 public keys. #[starknet::component] pub(crate) mod Falcon512AccountComponent { + use core::hash::{HashStateExTrait, HashStateTrait}; use core::num::traits::Zero; + use core::poseidon::{PoseidonTrait, poseidon_hash_span}; use openzeppelin_interfaces::accounts as interface; use openzeppelin_introspection::src5::SRC5Component; - use openzeppelin_introspection::src5::SRC5Component::InternalTrait as SRC5InternalTrait; + use openzeppelin_introspection::src5::SRC5Component::{ + InternalTrait as SRC5InternalTrait, SRC5Impl, + }; use openzeppelin_utils::execution::execute_single_call; use starknet::account::Call; - use starknet::storage::{MutableVecTrait, StoragePointerReadAccess, Vec, VecTrait}; + use starknet::storage::{ + MutableVecTrait, StoragePointerReadAccess, StoragePointerWriteAccess, Vec, VecTrait, + }; use crate::utils::is_tx_version_valid; use super::Falcon512SignatureVerifier; @@ -32,13 +38,29 @@ pub(crate) mod Falcon512AccountComponent { #[event] #[derive(Drop, Debug, PartialEq, starknet::Event)] - pub enum Event {} + pub enum Event { + OwnerAdded: OwnerAdded, + OwnerRemoved: OwnerRemoved, + } + + #[derive(Drop, Debug, PartialEq, starknet::Event)] + pub struct OwnerAdded { + #[key] + pub new_owner_guid: felt252, + } + + #[derive(Drop, Debug, PartialEq, starknet::Event)] + pub struct OwnerRemoved { + #[key] + pub removed_owner_guid: felt252, + } pub mod Errors { pub const INVALID_CALLER: felt252 = 'Account: invalid caller'; pub const INVALID_PUBLIC_KEY: felt252 = 'Account: invalid public key'; pub const INVALID_SIGNATURE: felt252 = 'Account: invalid signature'; pub const INVALID_TX_VERSION: felt252 = 'Account: invalid tx version'; + pub const UNAUTHORIZED: felt252 = 'Account: unauthorized'; } #[embeddable_as(SRC6Impl)] @@ -120,14 +142,157 @@ pub(crate) mod Falcon512AccountComponent { #[embeddable_as(PublicKeyImpl)] impl PublicKey< TContractState, + impl Verifier: Falcon512SignatureVerifier, +HasComponent, +SRC5Component::HasComponent, +Drop, > of interface::IFeltArrayPublicKey> { - /// Returns the immutable packed Falcon-512 public key. + /// Returns the current packed Falcon-512 public key. fn get_public_key(self: @ComponentState) -> Array { self.read_public_key() } + + /// Sets the public key to a canonical packed Falcon-512 public key. + /// + /// Requirements: + /// + /// - The caller must be the contract itself. + /// - The new public key must use the canonical 29-felt encoding. + /// - The signature must prove acceptance by the new owner. + /// + /// Emits both an `OwnerRemoved` and an `OwnerAdded` event. + fn set_public_key( + ref self: ComponentState, + new_public_key: Array, + signature: Span, + ) { + self.assert_only_self(); + assert( + Verifier::is_valid_public_key(new_public_key.span()), Errors::INVALID_PUBLIC_KEY, + ); + + let current_public_key = self.read_public_key(); + let removed_owner_guid = poseidon_hash_span(current_public_key.span()); + let current_owner = current_public_key.span(); + let new_owner = new_public_key.span(); + self.assert_valid_new_owner::(current_owner, new_owner, signature); + + self.emit(OwnerRemoved { removed_owner_guid }); + self._set_public_key(new_public_key); + } + } + + /// Adds camelCase support for `ISRC6`. + #[embeddable_as(SRC6CamelOnlyImpl)] + impl SRC6CamelOnly< + TContractState, + impl Verifier: Falcon512SignatureVerifier, + +HasComponent, + +SRC5Component::HasComponent, + +Drop, + > of interface::ISRC6CamelOnly> { + fn isValidSignature( + self: @ComponentState, hash: felt252, signature: Array, + ) -> felt252 { + SRC6::is_valid_signature(self, hash, signature) + } + } + + /// Adds camelCase support for `IFeltArrayPublicKey`. + #[embeddable_as(PublicKeyCamelImpl)] + impl PublicKeyCamel< + TContractState, + impl Verifier: Falcon512SignatureVerifier, + +HasComponent, + +SRC5Component::HasComponent, + +Drop, + > of interface::IFeltArrayPublicKeyCamel> { + fn getPublicKey(self: @ComponentState) -> Array { + self.read_public_key() + } + + fn setPublicKey( + ref self: ComponentState, + newPublicKey: Array, + signature: Span, + ) { + PublicKey::set_public_key(ref self, newPublicKey, signature); + } + } + + #[embeddable_as(Falcon512AccountMixinImpl)] + impl Falcon512AccountMixin< + TContractState, + impl Verifier: Falcon512SignatureVerifier, + +HasComponent, + impl SRC5: SRC5Component::HasComponent, + +Drop, + > of interface::FeltArrayAccountABI> { + fn __execute__(self: @ComponentState, calls: Array) { + SRC6::__execute__(self, calls) + } + + fn __validate__(self: @ComponentState, calls: Array) -> felt252 { + SRC6::__validate__(self, calls) + } + + fn is_valid_signature( + self: @ComponentState, hash: felt252, signature: Array, + ) -> felt252 { + SRC6::is_valid_signature(self, hash, signature) + } + + fn isValidSignature( + self: @ComponentState, hash: felt252, signature: Array, + ) -> felt252 { + SRC6CamelOnly::isValidSignature(self, hash, signature) + } + + fn __validate_declare__( + self: @ComponentState, class_hash: felt252, + ) -> felt252 { + Declarer::__validate_declare__(self, class_hash) + } + + fn __validate_deploy__( + self: @ComponentState, + class_hash: felt252, + contract_address_salt: felt252, + public_key: Array, + ) -> felt252 { + Deployable::__validate_deploy__(self, class_hash, contract_address_salt, public_key) + } + + fn get_public_key(self: @ComponentState) -> Array { + PublicKey::get_public_key(self) + } + + fn set_public_key( + ref self: ComponentState, + new_public_key: Array, + signature: Span, + ) { + PublicKey::set_public_key(ref self, new_public_key, signature); + } + + fn getPublicKey(self: @ComponentState) -> Array { + PublicKeyCamel::getPublicKey(self) + } + + fn setPublicKey( + ref self: ComponentState, + newPublicKey: Array, + signature: Span, + ) { + PublicKeyCamel::setPublicKey(ref self, newPublicKey, signature); + } + + fn supports_interface( + self: @ComponentState, interface_id: felt252, + ) -> bool { + let src5 = get_dep_component!(self, SRC5); + src5.supports_interface(interface_id) + } } #[generate_trait] @@ -137,7 +302,9 @@ pub(crate) mod Falcon512AccountComponent { impl SRC5: SRC5Component::HasComponent, +Drop, > of InternalTrait { - /// Validates and stores the immutable packed public key and registers SRC6 support. + /// Validates and stores the packed public key and registers SRC6 support. + /// + /// Emits an `OwnerAdded` event. fn initializer( ref self: ComponentState, public_key: Array, ) { @@ -146,9 +313,33 @@ pub(crate) mod Falcon512AccountComponent { let mut src5_component = get_dep_component_mut!(ref self, SRC5); src5_component.register_interface(interface::ISRC6_ID); - for felt in public_key { - self.public_key.push(felt); - } + self._set_public_key(public_key); + } + + /// Validates that the caller is the account itself. Otherwise it reverts. + fn assert_only_self(self: @ComponentState) { + let caller = starknet::get_caller_address(); + let self = starknet::get_contract_address(); + assert(self == caller, Errors::UNAUTHORIZED); + } + + /// Validates that the new owner accepted ownership of the account. + /// + /// WARNING: This function assumes that `current_owner` is the account's current owner. + fn assert_valid_new_owner( + self: @ComponentState, + current_owner: Span, + new_owner: Span, + signature: Span, + ) { + let message_hash = PoseidonTrait::new() + .update_with('StarkNet Message') + .update_with('accept_ownership') + .update_with(starknet::get_contract_address()) + .update_with(poseidon_hash_span(current_owner)) + .finalize(); + + assert(Verifier::verify(message_hash, new_owner, signature), Errors::INVALID_SIGNATURE); } /// Validates the transaction hash against the transaction signature. @@ -165,6 +356,32 @@ pub(crate) mod Falcon512AccountComponent { starknet::VALIDATED } + /// Sets the public key without validating the caller or key encoding. + /// The usage of this method outside the initializer and `set_public_key` is discouraged. + /// + /// Emits an `OwnerAdded` event. + fn _set_public_key( + ref self: ComponentState, new_public_key: Array, + ) { + let new_owner_guid = poseidon_hash_span(new_public_key.span()); + let stored_len = self.public_key.len(); + + if stored_len == 0 { + for felt in new_public_key { + self.public_key.push(felt); + } + } else { + assert(stored_len == new_public_key.len().into(), Errors::INVALID_PUBLIC_KEY); + let mut index = 0; + for felt in new_public_key { + self.public_key.at(index).write(felt); + index += 1; + } + } + + self.emit(OwnerAdded { new_owner_guid }); + } + /// Reads the stored public key into its 29-felt packed representation. fn read_public_key(self: @ComponentState) -> Array { let mut public_key = array![]; diff --git a/packages/account/src/falcon_512/falcon.cairo b/packages/account/src/falcon_512/falcon.cairo index 7b4687f05..071456f61 100644 --- a/packages/account/src/falcon_512/falcon.cairo +++ b/packages/account/src/falcon_512/falcon.cairo @@ -32,7 +32,7 @@ use super::ntt::falcon512_fast::ntt_falcon512_fast_u16_unchecked; use super::ntt::falcon512_fast::ntt_falcon512_fast_u16_unchecked; use super::zq::{Q32, center_sq, centered_difference_sq}; -/// Maximum allowed `||s0||^2 + ||s1||^2` for the legacy Falcon-512 submission algorithm. +/// Maximum allowed `||s0||^2 + ||s1||^2` for FALCON-512 as submitted to NIST. pub const SIG_BOUND_512: u64 = 34034726; /// The Falcon modulus as a u32 divisor. diff --git a/packages/account/src/falcon_512/falcon_512_shake.cairo b/packages/account/src/falcon_512/falcon_512_shake.cairo index 6bb13c2c5..358d67f4c 100644 --- a/packages/account/src/falcon_512/falcon_512_shake.cairo +++ b/packages/account/src/falcon_512/falcon_512_shake.cairo @@ -3,10 +3,10 @@ /// # Falcon-512 SHAKE Account /// -/// Immutable account using the legacy Falcon-512 submission algorithm's SHAKE-256 -/// hash-to-point and a 60-felt signature with a signer-supplied, verifier-bound product -/// hint. The felt encoding is specific to this contract and is not a standardized FN-DSA -/// signature format. +/// Account validating the Falcon-512 verification relation from the FALCON submission selected +/// by NIST, using SHAKE-256 hash-to-point. Its contract-specific 60-felt signature encoding +/// includes a signer-supplied polynomial-product hint that is checked on-chain to reduce +/// execution cost. This account does not claim conformance with FN-DSA/FIPS 206. #[starknet::contract(account)] pub mod Falcon512ShakeAccount { use openzeppelin_introspection::src5::SRC5Component; @@ -17,18 +17,8 @@ pub mod Falcon512ShakeAccount { component!(path: SRC5Component, storage: src5, event: SRC5Event); #[abi(embed_v0)] - impl SRC6Impl = - Falcon512AccountComponent::SRC6Impl; - #[abi(embed_v0)] - impl DeclarerImpl = - Falcon512AccountComponent::DeclarerImpl; - #[abi(embed_v0)] - impl DeployableImpl = - Falcon512AccountComponent::DeployableImpl; - #[abi(embed_v0)] - impl PublicKeyImpl = Falcon512AccountComponent::PublicKeyImpl; - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; + impl AccountMixinImpl = + Falcon512AccountComponent::Falcon512AccountMixinImpl; impl AccountInternalImpl = Falcon512AccountComponent::InternalImpl; diff --git a/packages/account/src/falcon_512/falcon_512_shake_direct.cairo b/packages/account/src/falcon_512/falcon_512_shake_direct.cairo index ead722759..9bf16d80a 100644 --- a/packages/account/src/falcon_512/falcon_512_shake_direct.cairo +++ b/packages/account/src/falcon_512/falcon_512_shake_direct.cairo @@ -4,10 +4,10 @@ /// # Falcon-512 SHAKE Direct Account /// -/// Immutable account using the legacy Falcon-512 submission algorithm's SHAKE-256 -/// hash-to-point and a 31-felt signature. It recomputes the polynomial product on-chain -/// instead of accepting a signer-supplied hint. The felt encoding is specific to this -/// contract and is not a standardized FN-DSA signature format. +/// Account validating the same Falcon-512 verification relation as `Falcon512ShakeAccount`, from +/// the FALCON submission selected by NIST. It uses SHAKE-256 hash-to-point and a contract-specific +/// 31-felt signature encoding, and recomputes the polynomial product on-chain instead of +/// accepting a signer-supplied hint. This account does not claim conformance with FN-DSA/FIPS 206. #[starknet::contract(account)] pub mod Falcon512ShakeDirectAccount { use openzeppelin_introspection::src5::SRC5Component; @@ -18,18 +18,10 @@ pub mod Falcon512ShakeDirectAccount { component!(path: SRC5Component, storage: src5, event: SRC5Event); #[abi(embed_v0)] - impl SRC6Impl = - Falcon512AccountComponent::SRC6Impl; - #[abi(embed_v0)] - impl DeclarerImpl = - Falcon512AccountComponent::DeclarerImpl; - #[abi(embed_v0)] - impl DeployableImpl = - Falcon512AccountComponent::DeployableImpl; - #[abi(embed_v0)] - impl PublicKeyImpl = Falcon512AccountComponent::PublicKeyImpl; - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; + impl AccountMixinImpl = + Falcon512AccountComponent::Falcon512AccountMixinImpl< + ContractState, Falcon512ShakeDirectVerifier, + >; impl AccountInternalImpl = Falcon512AccountComponent::InternalImpl; diff --git a/packages/account/src/falcon_512/hashing/hash_to_point.cairo b/packages/account/src/falcon_512/hashing/hash_to_point.cairo index 73e54d35c..00c482d9f 100644 --- a/packages/account/src/falcon_512/hashing/hash_to_point.cairo +++ b/packages/account/src/falcon_512/hashing/hash_to_point.cairo @@ -2,7 +2,7 @@ // OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 // (account/src/falcon_512/hashing/hash_to_point.cairo) -//! SHAKE-256 hash-to-point for the legacy Falcon-512 submission algorithm. +//! SHAKE-256 hash-to-point used by FALCON-512 as submitted to NIST. //! //! The function absorbs a 40-byte salt followed by the message hash's 32-byte //! little-endian representation. It reads the SHAKE-256 output as big-endian u16 words diff --git a/packages/account/src/falcon_512/ntt/engine.cairo b/packages/account/src/falcon_512/ntt/engine.cairo index bdde9fb54..0cbb222c7 100644 --- a/packages/account/src/falcon_512/ntt/engine.cairo +++ b/packages/account/src/falcon_512/ntt/engine.cairo @@ -5,8 +5,8 @@ //! //! # Algorithm //! -//! This computes exactly the recursive split/merge NTT of tprest/falcon.py (the -//! convention s2morrow and the Falcon-512 verifier use), reformulated iteratively: +//! This computes the recursive split/merge NTT of tprest/falcon.py, reformulated +//! iteratively for the Falcon-512 verifier: //! //! - **Forward** ([`ntt`]): permute the input into recursion-leaf order (bit reversal), //! then run merge levels bottom-up. The level merging blocks into size `2h` reads two diff --git a/packages/account/src/falcon_512/ntt/falcon512.cairo b/packages/account/src/falcon_512/ntt/falcon512.cairo index 9a6cd72ad..165e4f4f1 100644 --- a/packages/account/src/falcon_512/ntt/falcon512.cairo +++ b/packages/account/src/falcon_512/ntt/falcon512.cairo @@ -1,9 +1,8 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/ntt/falcon512.cairo) -//! Falcon parameter set for the NTT engine: q = 12289, negacyclic (x^n + 1), -//! tprest/falcon.py evaluation order — interoperable with the s2morrow convention and -//! the falcon.py reference signer. +//! Falcon parameter set for the NTT engine: q = 12289, negacyclic (x^n + 1), using +//! tprest/falcon.py evaluation order for interoperability with the reference signer. use super::bitrev::bitrev_512; use super::engine::NttConfig; diff --git a/packages/account/src/tests.cairo b/packages/account/src/tests.cairo index 4ed994f9b..21d83d450 100644 --- a/packages/account/src/tests.cairo +++ b/packages/account/src/tests.cairo @@ -1,6 +1,7 @@ mod extensions; mod falcon_512_fixture; +mod falcon_512_rotation_fixture; mod test_account; mod test_eth_account; mod test_falcon_512; diff --git a/packages/account/src/tests/falcon_512_fixture.cairo b/packages/account/src/tests/falcon_512_fixture.cairo index 17cf0435a..217d1005c 100644 --- a/packages/account/src/tests/falcon_512_fixture.cairo +++ b/packages/account/src/tests/falcon_512_fixture.cairo @@ -1,8 +1,8 @@ -//! Generated legacy Falcon-512 submission-algorithm test vector — do not edit by hand. +//! Locally generated FALCON-512 submission-algorithm fixture — do not edit by hand. //! -//! The fixture contains a genuine tprest/falcon.py keypair and signature over +//! The fixture contains a keypair and signature generated with `tprest/falcon.py` over //! `BENCH_MSG`, using falcon.py's SHAKE-256 hash-to-point. Its felt encoding is specific -//! to these Cairo accounts and is not the serialized signature format of a NIST standard. +//! to these Cairo accounts and does not claim conformance with FN-DSA/FIPS 206. //! salt = 0x4ad23197d576f705594ac172f4fca80e3338e991b8b55e269d421aa1522cd0aa2490034127651fc2. /// The benchmark message hash ('BENCH_MSG'). diff --git a/packages/account/src/tests/falcon_512_rotation_fixture.cairo b/packages/account/src/tests/falcon_512_rotation_fixture.cairo new file mode 100644 index 000000000..6ec8d4aae --- /dev/null +++ b/packages/account/src/tests/falcon_512_rotation_fixture.cairo @@ -0,0 +1,195 @@ +//! Deterministic Falcon-512 ownership-acceptance test vector — do not edit by hand. +//! +//! The public key comes from the benchmark-only demo key. The signature authenticates +//! `accept_ownership_hash()` and uses a deterministic salt and sampling seed so the +//! fixture remains reproducible. These values are test material and must never be used +//! for production keys. + +/// Fixed address used to domain-separate the ownership-acceptance fixture. +pub fn account_address() -> starknet::ContractAddress { + 'FALCON_ACCOUNT'.try_into().unwrap() +} + +/// Poseidon('StarkNet Message', 'accept_ownership', account, current-key GUID). +pub fn accept_ownership_hash() -> felt252 { + 0x4e62665f5de5d234888da2b01cedcd1c3ed0b70149a6697d6c881b38021c0a +} + +/// Poseidon hash of the original fixture's packed public key. +pub fn current_owner_guid() -> felt252 { + 0x1c017f6368c49d94e2fee2cebcac6def11fd9d75dd9042216f630dac0376c4 +} + +/// Poseidon hash of `new_public_key()`. +pub fn new_owner_guid() -> felt252 { + 0x17705763a1277b0c72cc319dc0015046911575a5f00c2c4b8cde3936383f329 +} + +/// Canonical packed NTT-domain public key accepted by the new owner (29 felts). +pub fn new_public_key() -> Array { + array![ + 0x9b070736af5cc06617e666684c2c7a010403b7d0d19dcd9cfb81189549805a, + 0x3d105fa9ae5ebb1b395b209f52d2573037aaf140a400f46abd8dd88dd6bdf7a, + 0x2694f8a86e7b5f43eb110a59677216700618c8a3a24b3da4a136121a564d51f, + 0x4c5b14d67f4e0ae3284ffe2cf65677104377bac0d860555878e6ef1b5583b1d, + 0x29821d15055be2309a227e2cfe5fb85009a17b780fa8d28df54501b358df83f, + 0x3e89a91ad7b8c9eff39374bd52a35af04860b1e7c93b895d23a1d46691aa38e, + 0x3a0a0e611b4b5b5649299d1598ee6a700038a82c8b1ab80dc941940c3a9b2a2, + 0x10c18aa9ac68d713ef720a31f5da2ec0095ca428486417acb8c8e6167201c0d, + 0x15f41c81e8885ac8dc01893ca65f02f0012a1eb6c967961eb0c945ad748ff50, + 0x4618beaf0197e17ec3689c5d36748c60318ee55414b85514a60dfb56eeeb4fa, + 0x30b879e8c0410e99f348217c18e22ff02afa0e5dbbd08207a5ccfa79a3ba625, + 0xaf0fb6daf59003db42f43df88cc0c001f679d7b48f67daa7d356a4bf336496, + 0x304d57e2461c9696b912a8206b978e03259bc45470546e4dcb6a9ce8eae372, + 0x4c43cc880b89ce551b791d3ffe6c42f009226ebf2cda7e1589ddb048ef3e285, + 0xded1a3261b45f5cd48b998fb4bb7730039227a60c74e3642f6d804088059ad, + 0x24738b37ff4970f0ebb3413c2ba80aa007537f5375e541b5de331bd24b7964b, + 0x635f5e913d93fe5fe967be72cb5ffe00f597198816fe0e7bc6f593e8b2f289, + 0x3bc65b96f0e1895aec8212134d4df4e00e6c187e4e5662b17c5617ea2c879d8, + 0x31953229396e87504f366de96ff81aa049eb9685294b798510c9dc6f33f2ead, + 0x4ad02aa854a2437211d0d932218492a041509375f9aa07aa048bb1f026fcb47, + 0x228372a2c3a4aa5ad081c14e1e59c9401d9cf3b670a57b74fe0db6212101354, + 0x21c5182aae8a80ee8120ac5b28da11302462b1cfd8cb8d9bd365d91679746ed, + 0x9142b61620f1a9aa1559d64dc947080439cfc2ed631f67e0c6fb24c89f45c1, + 0x2aff191772041447ab79b5119d20dce032df5801359ae786d30a36a58720526, + 0x5c960381a0f084fd1ee7ce1857eb67033be15d10c22561eb8a64c5bfe11316, + 0x21cb99b175511bae8b10c81fb384a5c0200267514bd3e78f1994f2e84145fd7, + 0x954ba16dc3cac58842d592273f5b3c0475157535e4e9968a4871791acb7386, + 0xc01619ea6b19c34f0902ea4cc8b069038f4b28106b81377424a847f2085a17, + 0xde26e4af2b41471a826a610f23, + ] +} + +/// New-owner acceptance signature: s1 (29) || salt (2) || checked product hint (29). +pub fn accept_ownership_signature() -> Array { + array![ + 0x4bd8ec4b708072cf5362a321179003104c4c50f2f986221418a96705d433e21, + 0x682d1dd0c5881eafd35aad9c5a05004c3c4b52b7899b42010ff2db7a09ea7, + 0x4bcf491b89f52b5761724a3ed302d3c04a9d2a827e2d772b1dba258c109e061, + 0x4cb7b76eaed906a150d9823d31e401a000f0789e016141e0e61d630e037620c, + 0x4ca479be1a133af9811cf86a5c8419804cafb98fbc2fe39f3ffd0227ae0bfd5, + 0x8843b018a692af92a60d55ef6c0f404c45ebbcd7200c8fcde396048d60f7b, + 0x3684c7f6455c3ebedc50b87e6e3100002b4b459193d1fbe8018ce82d07205, + 0x15d6b684829414103090f0acfa244004c22a91f8cbfce474932d0b0c977d30, + 0x4bb8eb0f19aeab46b0ab070e25d9eee0018716d703d665410dd1b7e06b3e33d, + 0x20051b97b1f6401e7370a9a856f8c04bf8fb1e92fe2f3e34d6ebd0520410c, + 0x4c6f99cf11fa6e33d2dcd6af8f1ef680015096fff3b3843a2344bf73282e10a, + 0x4bff61f774e0dfb3f7ba80db4d45e0800024e47d9a97b6e5d5553418b900f3b, + 0x4cb2efb086aa5a742ef1c4b974c1f93000dd36a707a965312c194d0248201c3, + 0x1b58fecee0992b9f0186698ec08dda04cd961526e4dd80ffddc3efb6e4102f, + 0x670cc2ab87dbc109614e10c1216d000199ec2f99bb899210e0d8aa887dca, + 0x1121559d16b57083725a7c4450ff1d04b2efc2e37bef4ce4e025a4b5d3bdac, + 0x10d4b09495173bef015ace4c4742d100133c5597b3536d964f822a9ab5920e, + 0x14a2c0beb5f158385759e697b3007700031abd747c7673a628ab03204840d2, + 0x4d3c35bfc4f8715a7f410d1f7fb700190b9ca868f4293e5c45d6486952bc, + 0x4c32b8d5115b731c281a64faf771f41000581f4f296cf908ab2824d9994dd10, + 0x4cbc868e17e7b77d10c1b48c396fe0a0016a3ee08207552926538c060cc6edb, + 0x4c5c578270640554c6cb3ab47f89e2e0009d1a09de5d7c6bc748f92e382f280, + 0x148939e89bd4db9d0112f86248ef9d000bebd61ee851f36798bf21c546e024, + 0x1b8ca5c92eb2579225e1d8b97473b604c00fb06ee864994613ce35ea88fe38, + 0x4cd7ce67945ef3495c008b3a6f772d00000cc8c0900072370feea11b30c2e57, + 0x4cc15557cd85bfc94965eff5ff3702004b3a463771c9ca43dc9a1def3da301f, + 0x2349b1df9e95f8f4dc72c0f485e6f0002818f34ad3cba8aaf05594b569f55, + 0x2ceafa0c7661bbbce90a817b93f4004c1ddee8c80545132781b09382f909d, + 0x2ac0257769127e5984194dffa7, 0xbd04ce759c492d07b19bef74d71ee6fe2c6395c5, + 0x91d81fd4340aa00431b4cb75587540af14d8cd73, + 0x1051c709c87ed0445c766e7cb5ce8d102f5c042846f698e5fc38c5e857fd1fd, + 0x33a47a815b695b237c57076db0c52a200004480aaf9dcc50aa38773f164f4dc, + 0x4390c80587c953957fcefa0e96dcc850440e3d33c5583abb53202486512e0a4, + 0x3c8efd9742450d383641fd61b3d7a02049633f66b708801f9532f736f391f20, + 0x16107b9eb461c7ff459a0ad99e6ac220457638e26fd7e221362aa67342d9826, + 0x3f8260d83f17fd3b24a27aa8a6959910468a44bdffb5c5d64c9f931b7988be3, + 0x40dcb437664f302fad3f828a550872704a05df1259b31c4c233e4044c1356e6, + 0xde82db0bc2ed4ff11f929b77c2e7f200835825cfda5cf4f03898bb0019247f, + 0x412b08e780e1f7ce2e8d0a50ec37587029252efc28f902ec0fba7d47848e8a3, + 0x62c7a63f8b9a78fa9eb0492dac515c046c1f24110314f4077f6a6a9d77e25e, + 0x479886cb05345c99f609266a1d8569b01ca8ebc4be002168db76c630b929778, + 0x165f97ff7005bee86cbc640c08125da0116dee775768d8433a85bcca04f6e25, + 0x3707f6ac5cc25764e1f0757d62ef37d0031c625e8e57d997e71eac2dc20afce, + 0x455a010b4d551da4227f72a230bb870012fd860df39a89a1cac1237bffb3e1a, + 0x2fbb2ee1ee08124eb7084684d1c369a002c38bd7ccd46459727513fd77b15bb, + 0x21a74007a1dff2da04d072b76d1add20372e72f5ed1a02bda91535ade25edcd, + 0xc00f854df5ecd9dd59049957e61fde01b991a7eac5d5123fb99890eb3352d5, + 0x23b2f5f09b53b3a601ee8b403f163750195079f2d3e4270bd141c7d999ade24, + 0x198ddc376c9a5a0648588085f81c5600483d075e2a001f9974e300d587fcbc3, + 0x8ad58028cdd0578a90382530b80be20189a0a470f9e99d937b509012dd9d5c, + 0x5954442e51ac54bb521a781d4e792c00fffc93bc98ab740780e791e080101f, + 0x3189c3e2ef880bd48ebda2c66f4231e020f638f7dd8749cc6cf00aff4901fa5, + 0x1a75769c1e41cee70b2312b20b67258035a064e284fad5933762f822c7273b5, + 0xb5d36ab5affdbda6cffc2e601a645f0399e06eaabb0ad78e291a855104e00e, + 0x28a06c02fef981a16947d57bf3faaf4008b85a687096d718050986984c421cd, + 0xdb421cb4f9bdd7862d60e0fe301e57000932b740701f2c68bb4314ec7c9154, + 0x70d7172e53b99f0af6f88646f278cc0271114547953f07d1de373a5502741c, + 0x1dd222aacd445fdb26c808596ac28a701b9c25fc954b15c6794e9aa9ee1ee0f, + 0xcbd8a83727b237f7924603d6f19, + ] +} +/// Ownership-acceptance hash after the first rotation, when the demo key is current. +pub fn second_accept_ownership_hash() -> felt252 { + 0x2ab9ca182c4cf47c5a03e1d9a614497b148d52814e8837f308fb0aa02355dc8 +} + +/// Acceptance signature for repeating the rotation while the demo key is current. +pub fn second_accept_ownership_signature() -> Array { + array![ + 0xf6d56a03695a92ffe10775fa7611a000a51ff4f283bb2847933b6b46f5eea, + 0x4c52c22e48128d903d26dc818aa4ff9001308679147aa1b50c818d6f7960081, + 0x1cda19343fb81d5c2a6054167effe04c2de6d6aebf21e18215c7bd14111fd, + 0x4ceb06a7628ff5c6409704547ceb22f04cdb083d3464003540961d43df3527e, + 0x4afbb4b4e2f119f4a8e971ddc3eaaf804bfa98275513f2cfd8375ffd6210e53, + 0x123b5f938153dd7902054277d0215a04cd62c9889dbdf0bc58f394bac6af46, + 0x14ddee0c94522c499966e1c2091ce00190bacb3802a97a92036e39040f0c5, + 0x123ba47f5485065bdff8eae8d26dce000435101956a0b4900a88390881c1e2, + 0x4cc7ca72d7fc275a8cb0ee4ce1850a60006e9a3d902c7d37295d0a3ed071f3a, + 0x4cc7bce46ff6df3ccfc83dab3a08f300000337f7035642fd85e7fb3fd3ecec5, + 0x4be8f7d4a0f93f61133f0441bdcab900013ef309b7f513071a7733cb7bdff2a, + 0x1d0defcd31553708a3f593dd830452000dec56dd02d514ba9a2b4c5e9e1149, + 0x4c0f6541464f2e84c82e2fd68b83eba0007ea1d620956d88167b99facaf8ef9, + 0x435413ba2c43207cfbf6bebaccf4e001cd9d5f6576b6401d33c64a98972e4, + 0x1a8b693f6eeabfa3d47182b4f28ecd00044e639ff42a16b8fb6958d5307d65, + 0x19bec6c21a4f0e20e689608003d1860007d0712b9f2b61fb972c2e1466c057, + 0x9046111643fbfe65be14273eaa1fb04bdc1d5030de30442f17fd79009df32, + 0x4bc41be307f769a3c05aeab5c4973e204ca2e5b932f65c33e953d8cd012e1c6, + 0x4c8adabefbdc970c0481de0e1cb9caa0001cdf3d790d63e620974f7aea32101, + 0x4bad994f2bc99764172ead9fcae9e1804cf168caf55b79f2af8d5c9c6b6def2, + 0x4c6dfaa37616f3124baaff11d0f3f4204c05c983c15209ae3911844ab541eba, + 0x4cd494f00a89cdf59d25c9c4db8be4004bb26fe0ba360f33d5462c90997ad99, + 0x21c2b6d53bd45d793420de8849105b04bda867d88bdd6dba4b6cf233ab707e, + 0x4c12a00ed61d48357079ee45b0bdf79000119eda7b00337f972f2d5b65b8f4d, + 0xf3a2ecf437af67a652d6c8b7fa2e000102156f4fde082d083625a15b0651d, + 0x4c1c340499152699b95a9ea8c14c04804bd752551c5c7635ed809c79309ae55, + 0x89d8e03437810f4f415d546c9910b04b98d058fc077f3f2be07e327a70d3d, + 0x4c779a6b3a8957a87a28b191b146f4004c6ac2791b07cee011241595bfec002, + 0x3bdf29415cc9d5cb3be966024, 0xd60ab6224f871c2728630186e556c5a368417228, + 0x2f1dc6f52c185b6fadb36c8b7ccbc2b4fb8768a4, + 0x2d434c43fa92349a139c616b7f836e202606f3858fb183df65884be39c9ec60, + 0x1905f098212a5f47a15a742176fcc7a0238f7d9f2864917b92643d27fc57485, + 0x268e77449f3f343b765f09d69ec5d24049d08677fad8a5dca0a42bf9ea34e5c, + 0x47cc402067f87b327b4769e5d8bd10e02f9cba4965f5ce37d20adb0a91389e4, + 0x3bb6230a52112dd5a9d9c28d7f364f802729533298307a4e01b00452c7a395d, + 0x42cb68d1a7bf40213dc9d3a3d8f4bea0016b6f4d7e2c114a836a513023cfc03, + 0x6545893784155df10281b3917ba2aa023832c31b682917bf38a9089a418531, + 0x4c28826494541bb9256069dc7cabf8700f9c08e290584cbfa7d63d196145b3b, + 0xedbca298596adff6b29858b9dabec20418f2e1bfcb2de3f1d51acf264e395c, + 0x96a610c80c21bade198c8067d27a1c025afdda83b9b9e589b73a68a0476eb0, + 0xd079e08398ba8dec496fd23107fb04cba3e3ec170207d2927e13500743ea, + 0x155e559c66e2e6c61ce66cb3024d57f034a0161959ee88aa2277d0369b5b9e7, + 0x238f54118a964f827bfbf5fc7c3102f04478f428498dd170fb970e7bd1cf581, + 0x1d719db1c7ee057e314ad8b7de1cece02896b5174f59aa612631e1e5fef0e0b, + 0x4fa2c0d8474e2fcc4b8915cbc9724002681edca710d94538af3a39f7988ccc, + 0x2f8416195d0eeee50f141ff5d8c33fc04b71b2e65d4ca630649f52bfe427f15, + 0x2f7a46a098ec70436533ce0ede3992503935467de00794c5bd62f7b6953b4b3, + 0x30a2d971f3604e1ddde683e990c984003478852047ba4a92fa2759e3266e0b4, + 0xd43ee472abffe62e92ba4f59da84e004c875f3ddebb93f5b5902b837d51b94, + 0x1fb55ca37e0372da3e8d6b825fc72d400476552effa9fc733cbf2a8cd1efdce, + 0x395a989d73d2749f3eee8c98b7955ca009c610e68ed1faecfce5bba311f9ed7, + 0x34fc98fc328e7ce0024752cdcbaa89d0002fd82676b3e32b1b5b130216fdf4f, + 0x32e1417294a818ad2925f44a03cb0f049d00eddff29e50ed00eb24e250b96c, + 0x19b1fa1777484a4605991448e8e627701565723c76e1596f688372d2501b092, + 0x3b59dca4e54aaa3cc995db1ee63146f02937aedf1a4c8fc3e0486e5a8e30165, + 0x1df8ca24fb6ec85eda1bbd1049413db03d95e67b49fd1f98247b16f32adde84, + 0x2c6c8582db3ab172621fc31949c694e014888d6dcaa3278230f90cd9a19910b, + 0x39678a33f5573e81e8aa9317e88624701b6c81616be373d7f5be12868926d92, + 0x141b32eb53245d40045054b63367, + ] +} diff --git a/packages/account/src/tests/test_falcon_512.cairo b/packages/account/src/tests/test_falcon_512.cairo index 3bddd69e9..d43f2caba 100644 --- a/packages/account/src/tests/test_falcon_512.cairo +++ b/packages/account/src/tests/test_falcon_512.cairo @@ -1,8 +1,9 @@ use core::num::traits::Zero; use openzeppelin_interfaces::accounts::{ - IDeclarerDispatcher, IDeclarerDispatcherTrait, IFeltArrayDeployableDispatcher, - IFeltArrayDeployableDispatcherTrait, IFeltArrayPublicKeyDispatcher, - IFeltArrayPublicKeyDispatcherTrait, ISRC6Dispatcher, ISRC6DispatcherTrait, ISRC6_ID, + FeltArrayAccountABIDispatcher, FeltArrayAccountABIDispatcherTrait, IDeclarerDispatcher, + IDeclarerDispatcherTrait, IFeltArrayDeployableDispatcher, IFeltArrayDeployableDispatcherTrait, + IFeltArrayPublicKeyDispatcher, IFeltArrayPublicKeyDispatcherTrait, ISRC6Dispatcher, + ISRC6DispatcherTrait, ISRC6_ID, }; use openzeppelin_interfaces::introspection::{ISRC5Dispatcher, ISRC5DispatcherTrait, ISRC5_ID}; use openzeppelin_test_common::mocks::simple::{ISimpleMockDispatcher, ISimpleMockDispatcherTrait}; @@ -10,9 +11,11 @@ use openzeppelin_testing as utils; use openzeppelin_testing::constants::{ CALLER, MIN_TRANSACTION_VERSION, QUERY_OFFSET, QUERY_VERSION, SALT, ZERO, }; +use openzeppelin_testing::{EventSpyExt, ExpectedEvent, spy_events}; use snforge_std::{ - start_cheat_caller_address, start_cheat_signature_global, start_cheat_transaction_hash_global, - start_cheat_transaction_version_global, + CheatSpan, cheat_caller_address, start_cheat_caller_address, start_cheat_signature_global, + start_cheat_transaction_hash_global, start_cheat_transaction_version_global, + stop_cheat_caller_address, }; use starknet::ContractAddress; use starknet::account::Call; @@ -21,6 +24,10 @@ use crate::falcon_512::{ SIGNATURE_FELTS, }; use super::falcon_512_fixture::{msg, public_key, signature}; +use super::falcon_512_rotation_fixture::{ + accept_ownership_hash, accept_ownership_signature, account_address, current_owner_guid, + new_owner_guid, new_public_key, second_accept_ownership_signature, +}; const Q_POW_9: felt252 = 6392178558614694273495691177456939009; const TWO_POW_160: felt252 = 0x10000000000000000000000000000000000000000; @@ -68,6 +75,10 @@ fn direct_signature() -> Array { copy_prefix(signature().span(), DIRECT_SIGNATURE_FELTS) } +fn direct_accept_ownership_signature() -> Array { + copy_prefix(accept_ownership_signature().span(), DIRECT_SIGNATURE_FELTS) +} + fn deploy_account(contract_name: ByteArray) -> (ContractAddress, felt252) { let contract_class = utils::declare_class(contract_name); let mut calldata = array![]; @@ -76,6 +87,37 @@ fn deploy_account(contract_name: ByteArray) -> (ContractAddress, felt252) { (contract_address, contract_class.class_hash.into()) } +fn deploy_account_at( + contract_name: ByteArray, contract_address: ContractAddress, +) -> FeltArrayAccountABIDispatcher { + let contract_class = utils::declare_class(contract_name); + let mut calldata = array![]; + public_key().serialize(ref calldata); + utils::deploy_at(contract_class, contract_address, calldata); + FeltArrayAccountABIDispatcher { contract_address } +} + +fn execute_key_rotation( + account: FeltArrayAccountABIDispatcher, + new_key: Array, + acceptance_signature: Array, + setter_selector: felt252, +) { + let mut calldata = array![]; + new_key.serialize(ref calldata); + acceptance_signature.serialize(ref calldata); + account + .__execute__( + array![ + Call { + to: account.contract_address, + selector: setter_selector, + calldata: calldata.span(), + }, + ], + ); +} + fn setup_transaction( contract_address: ContractAddress, tx_hash: felt252, @@ -313,6 +355,168 @@ fn test_accounts_is_valid_signature_return_convention() { assert!(direct.is_valid_signature(msg(), signature()).is_zero()); } +#[test] +fn test_hint_account_rotates_key_via_authenticated_execute() { + let address = account_address(); + let account = deploy_account_at("Falcon512ShakeAccount", address); + setup_transaction(address, msg(), signature(), MIN_TRANSACTION_VERSION); + + // Protocol validation authenticates the outer invoke with the current owner. + assert_eq!(account.__validate__(array![]), starknet::VALIDATED); + stop_cheat_caller_address(address); + // Cheat only the outer entrypoint call. The nested self-call keeps its real caller. + cheat_caller_address(address, ZERO, CheatSpan::TargetCalls(1)); + + let mut spy = spy_events(); + execute_key_rotation( + account, new_public_key(), accept_ownership_signature(), selector!("set_public_key"), + ); + + spy + .assert_emitted_single( + address, ExpectedEvent::new().key(selector!("OwnerRemoved")).key(current_owner_guid()), + ); + spy + .assert_only_event( + address, ExpectedEvent::new().key(selector!("OwnerAdded")).key(new_owner_guid()), + ); + + let account = FeltArrayAccountABIDispatcher { contract_address: address }; + assert_eq!(account.get_public_key().span(), new_public_key().span()); + assert_eq!(account.getPublicKey().span(), new_public_key().span()); +} + +#[test] +fn test_hint_rotated_key_accepts_new_owner_and_rejects_old_owner() { + let address = account_address(); + let account = deploy_account_at("Falcon512ShakeAccount", address); + let acceptance_signature = accept_ownership_signature(); + start_cheat_caller_address(address, address); + account.set_public_key(new_public_key(), acceptance_signature.span()); + + assert_eq!( + account.is_valid_signature(accept_ownership_hash(), accept_ownership_signature()), + starknet::VALIDATED, + ); + assert!(account.is_valid_signature(msg(), signature()).is_zero()); +} + +#[test] +fn test_repeated_key_rotation_keeps_exact_storage_length() { + let address = account_address(); + let account = deploy_account_at("Falcon512ShakeAccount", address); + start_cheat_caller_address(address, address); + let first_signature = accept_ownership_signature(); + account.set_public_key(new_public_key(), first_signature.span()); + + // A second rotation overwrites the 29 stored felts instead of growing the Vec. + let second_signature = second_accept_ownership_signature(); + account.set_public_key(new_public_key(), second_signature.span()); + assert_eq!(account.get_public_key().len(), PUBLIC_KEY_FELTS); +} + +#[test] +fn test_direct_account_rotates_key_through_camel_case_abi() { + let address = account_address(); + let account = deploy_account_at("Falcon512ShakeDirectAccount", address); + let acceptance_signature = direct_accept_ownership_signature(); + start_cheat_caller_address(address, address); + + let mut spy = spy_events(); + account.setPublicKey(new_public_key(), acceptance_signature.span()); + + spy + .assert_emitted_single( + address, ExpectedEvent::new().key(selector!("OwnerRemoved")).key(current_owner_guid()), + ); + spy + .assert_only_event( + address, ExpectedEvent::new().key(selector!("OwnerAdded")).key(new_owner_guid()), + ); + + assert_eq!(account.getPublicKey().span(), new_public_key().span()); + assert_eq!(account.get_public_key().span(), new_public_key().span()); +} + +#[test] +fn test_direct_account_camel_case_signature_alias() { + let address = account_address(); + let account = deploy_account_at("Falcon512ShakeDirectAccount", address); + + assert_eq!(account.isValidSignature(msg(), direct_signature()), starknet::VALIDATED); +} + +#[test] +fn test_constructor_emits_owner_added_guid() { + let address = account_address(); + let mut spy = spy_events(); + deploy_account_at("Falcon512ShakeAccount", address); + + spy + .assert_only_event( + address, ExpectedEvent::new().key(selector!("OwnerAdded")).key(current_owner_guid()), + ); +} + +#[test] +#[should_panic(expected: 'Account: unauthorized')] +fn test_key_rotation_rejects_non_self_caller() { + let address = account_address(); + let account = deploy_account_at("Falcon512ShakeAccount", address); + start_cheat_caller_address(address, CALLER); + + account.set_public_key(new_public_key(), array![].span()); +} + +#[test] +#[should_panic(expected: 'Account: invalid public key')] +fn test_key_rotation_rejects_malformed_public_key_before_signature() { + let address = account_address(); + let account = deploy_account_at("Falcon512ShakeDirectAccount", address); + start_cheat_caller_address(address, address); + + account + .set_public_key( + copy_prefix(new_public_key().span(), PUBLIC_KEY_FELTS - 1), array![].span(), + ); +} + +#[test] +#[should_panic(expected: 'Account: invalid signature')] +fn test_key_rotation_rejects_invalid_new_owner_proof() { + let address = account_address(); + let account = deploy_account_at("Falcon512ShakeAccount", address); + start_cheat_caller_address(address, address); + let valid_signature = accept_ownership_signature(); + let invalid_signature = with_replaced(valid_signature.span(), 0, *valid_signature.at(0) + 1); + + account.set_public_key(new_public_key(), invalid_signature.span()); +} + +#[test] +#[should_panic(expected: 'Account: invalid signature')] +fn test_key_rotation_proof_cannot_be_replayed_to_another_account() { + let address = CALLER; + let account = deploy_account_at("Falcon512ShakeDirectAccount", address); + start_cheat_caller_address(address, address); + let acceptance_signature = direct_accept_ownership_signature(); + + account.set_public_key(new_public_key(), acceptance_signature.span()); +} + +#[test] +#[should_panic(expected: 'Account: invalid signature')] +fn test_key_rotation_proof_cannot_be_replayed_after_rotation() { + let address = account_address(); + let account = deploy_account_at("Falcon512ShakeDirectAccount", address); + start_cheat_caller_address(address, address); + let acceptance_signature = direct_accept_ownership_signature(); + + account.set_public_key(new_public_key(), acceptance_signature.span()); + // The current-owner GUID changed, so the first acceptance proof is no longer valid. + account.set_public_key(new_public_key(), acceptance_signature.span()); +} + #[test] fn test_all_validation_entrypoints_accept_hint_account_signature() { let (address, class_hash) = deploy_account("Falcon512ShakeAccount"); diff --git a/packages/interfaces/README.md b/packages/interfaces/README.md index 3a48af440..8396f4f21 100644 --- a/packages/interfaces/README.md +++ b/packages/interfaces/README.md @@ -1,8 +1,8 @@ ## Interfaces -List of standardized interfaces, ABI traits and dispatchers. +List of interfaces, ABI traits and dispatchers. -### Standardized Interfaces +### Interfaces - [`IAccessControlDefaultAdminRules`](https://docs.openzeppelin.com/contracts-cairo/3.x/api/access#IAccessControlDefaultAdminRules) - [`IAccessControl`](https://docs.openzeppelin.com/contracts-cairo/3.x/api/access#IAccessControl) @@ -18,6 +18,7 @@ List of standardized interfaces, ABI traits and dispatchers. - `IDeployable` - `IFeltArrayDeployable` - `IFeltArrayPublicKey` +- `IFeltArrayPublicKeyCamel` - `IPublicKey` - `IPublicKeyCamel` - `IEthDeployable` @@ -75,6 +76,7 @@ List of standardized interfaces, ABI traits and dispatchers. - `OwnableABI` - `OwnableTwoStepABI` - `AccountABI` +- `FeltArrayAccountABI` - `EthAccountABI` - `TimelockABI` - `VotesABI` diff --git a/packages/interfaces/src/account/accounts.cairo b/packages/interfaces/src/account/accounts.cairo index e1b90f8be..3c3098819 100644 --- a/packages/interfaces/src/account/accounts.cairo +++ b/packages/interfaces/src/account/accounts.cairo @@ -74,11 +74,16 @@ pub trait IPublicKey { fn set_public_key(ref self: TState, new_public_key: felt252, signature: Span); } -/// Read access to a public key encoded as a felt array. +/// Access to a public key encoded as a felt array. #[starknet::interface] pub trait IFeltArrayPublicKey { /// Returns the current public key of the account. fn get_public_key(self: @TState) -> Array; + + /// Sets the public key of the account to `new_public_key`. + /// + /// Emits both an `OwnerRemoved` and an `OwnerAdded` event. + fn set_public_key(ref self: TState, new_public_key: Array, signature: Span); } /// Adds camelCase support for `ISRC6`. @@ -94,6 +99,13 @@ pub trait IPublicKeyCamel { fn setPublicKey(ref self: TState, newPublicKey: felt252, signature: Span); } +/// Adds camelCase support for `IFeltArrayPublicKey`. +#[starknet::interface] +pub trait IFeltArrayPublicKeyCamel { + fn getPublicKey(self: @TState) -> Array; + fn setPublicKey(ref self: TState, newPublicKey: Array, signature: Span); +} + // // Account ABI // @@ -128,6 +140,43 @@ pub trait AccountABI { fn setPublicKey(ref self: TState, newPublicKey: felt252, signature: Span); } +// +// FeltArrayAccount ABI +// + +#[starknet::interface] +pub trait FeltArrayAccountABI { + // ISRC6 + fn __execute__(self: @TState, calls: Array); + fn __validate__(self: @TState, calls: Array) -> felt252; + fn is_valid_signature(self: @TState, hash: felt252, signature: Array) -> felt252; + + // ISRC5 + fn supports_interface(self: @TState, interface_id: felt252) -> bool; + + // IDeclarer + fn __validate_declare__(self: @TState, class_hash: felt252) -> felt252; + + // IFeltArrayDeployable + fn __validate_deploy__( + self: @TState, + class_hash: felt252, + contract_address_salt: felt252, + public_key: Array, + ) -> felt252; + + // IFeltArrayPublicKey + fn get_public_key(self: @TState) -> Array; + fn set_public_key(ref self: TState, new_public_key: Array, signature: Span); + + // ISRC6CamelOnly + fn isValidSignature(self: @TState, hash: felt252, signature: Array) -> felt252; + + // IFeltArrayPublicKeyCamel + fn getPublicKey(self: @TState) -> Array; + fn setPublicKey(ref self: TState, newPublicKey: Array, signature: Span); +} + // // EthAccount // From 96110a6b09cdbf462ce0101050b2b956d1fa3e01 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Mon, 10 Aug 2026 15:56:53 +0200 Subject: [PATCH 3/6] feat: re-structure based on conisstency --- .github/workflows/test.yml | 14 +- CHANGELOG.md | 4 + Scarb.lock | 12 +- Scarb.toml | 2 +- docs/modules/ROOT/pages/api/account.adoc | 478 ++++++++++++++ docs/modules/ROOT/pages/presets.adoc | 12 + .../ROOT/pages/utils/_class_hashes.adoc | 21 +- packages/account/README.md | 70 ++- packages/account/Scarb.toml | 4 +- packages/account/src/falcon_512.cairo | 106 +--- packages/account/src/falcon_512/account.cairo | 101 +-- packages/account/src/falcon_512/falcon.cairo | 63 +- .../src/falcon_512/falcon_512_shake.cairo | 47 -- .../falcon_512/falcon_512_shake_direct.cairo | 50 -- .../falcon_512/hashing/hash_to_point.cairo | 13 +- .../src/falcon_512/hashing/shake256.cairo | 57 +- .../account/src/falcon_512/ntt/bitrev.cairo | 8 +- .../account/src/falcon_512/ntt/engine.cairo | 83 +-- .../src/falcon_512/ntt/falcon512.cairo | 14 +- .../src/falcon_512/ntt/falcon512_fast.cairo | 31 +- .../src/falcon_512/ntt/roots_felt.cairo | 11 +- .../src/falcon_512/ntt/roots_scaled.cairo | 11 +- packages/account/src/falcon_512/packing.cairo | 41 +- .../account/src/falcon_512/verifier.cairo | 11 + .../src/falcon_512/verifier_impls.cairo | 83 +++ packages/account/src/falcon_512/zq.cairo | 35 +- packages/account/src/lib.cairo | 5 +- packages/account/src/tests.cairo | 3 +- .../src/tests/falcon_512_account_mocks.cairo | 77 +++ .../account/src/tests/test_falcon_512.cairo | 106 ++-- .../src/tests/test_falcon_512_ntt.cairo | 18 +- packages/corelib_imports/README.md | 8 + packages/corelib_imports/Scarb.toml | 24 + packages/corelib_imports/src/lib.cairo | 10 + packages/interfaces/README.md | 3 +- .../interfaces/src/account/accounts.cairo | 3 +- packages/presets/README.md | 10 + packages/presets/Scarb.toml | 4 + .../src/falcon_512_shake_account.cairo | 78 +++ .../src/falcon_512_shake_direct_account.cairo | 80 +++ packages/presets/src/interfaces.cairo | 5 + .../src/interfaces/falcon_512_account.cairo | 45 ++ packages/presets/src/lib.cairo | 20 + packages/presets/src/tests.cairo | 2 + .../src/tests/test_falcon_512_accounts.cairo | 213 +++++++ packages/test_common/src/falcon_512.cairo | 2 + .../src/falcon_512/fixture.cairo} | 12 +- .../src/falcon_512/rotation_fixture.cairo} | 94 ++- packages/test_common/src/lib.cairo | 1 + scripts/falcon_512/generate_ntt.py | 583 ++++++++++++++++++ scripts/get_hashes_page.py | 4 +- 51 files changed, 2238 insertions(+), 554 deletions(-) delete mode 100644 packages/account/src/falcon_512/falcon_512_shake.cairo delete mode 100644 packages/account/src/falcon_512/falcon_512_shake_direct.cairo create mode 100644 packages/account/src/falcon_512/verifier.cairo create mode 100644 packages/account/src/falcon_512/verifier_impls.cairo create mode 100644 packages/account/src/tests/falcon_512_account_mocks.cairo create mode 100644 packages/corelib_imports/README.md create mode 100644 packages/corelib_imports/Scarb.toml create mode 100644 packages/corelib_imports/src/lib.cairo create mode 100644 packages/presets/src/falcon_512_shake_account.cairo create mode 100644 packages/presets/src/falcon_512_shake_direct_account.cairo create mode 100644 packages/presets/src/interfaces/falcon_512_account.cairo create mode 100644 packages/presets/src/tests/test_falcon_512_accounts.cairo create mode 100644 packages/test_common/src/falcon_512.cairo rename packages/{account/src/tests/falcon_512_fixture.cairo => test_common/src/falcon_512/fixture.cairo} (93%) rename packages/{account/src/tests/falcon_512_rotation_fixture.cairo => test_common/src/falcon_512/rotation_fixture.cairo} (67%) create mode 100644 scripts/falcon_512/generate_ntt.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8100335d1..2fa7d9bf2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -44,14 +44,24 @@ jobs: *.md !PULL_REQUEST_TEMPLATE.md + - name: Verify generated Falcon NTT sources + run: | + python3 scripts/falcon_512/generate_ntt.py --write + git diff --exit-code -- packages/account/src/falcon_512/ntt + - name: Cairo lint run: scarb fmt --check --workspace - name: Run tests run: snforge test --workspace --features fuzzing --fuzzer-runs 200 - - name: Run Falcon production-path tests - run: snforge test -p openzeppelin_account --release --features falcon_fast_tests falcon_512 + - name: Run Falcon release-profile tests + run: | + snforge test -p openzeppelin_account --release --features falcon_fast_tests falcon_512 --max-n-steps 100000000 + snforge test -p openzeppelin_presets --release --features falcon_presets_tests falcon_512 --max-n-steps 100000000 + + - name: Build release-profile presets + run: scarb --release build -p openzeppelin_presets - name: Run tests and generate coverage report run: snforge test --workspace --coverage diff --git a/CHANGELOG.md b/CHANGELOG.md index f7925ec18..4a29b97dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- `Falcon512AccountComponent`, SHAKE-256 hint and direct verifier strategies, + `Falcon512ShakeAccountUpgradeable` and `Falcon512ShakeDirectAccountUpgradeable` presets with SRC9 + and class upgrades, and felt-array deployment and public-key interfaces for Falcon-512 accounts + (#1730) - `SafeERC20DispatcherTrait` in `openzeppelin_token::erc20::utils` with `assert_transfer`, `assert_transfer_from`, `assert_increase_allowance`, and `assert_decrease_allowance` (#1683) ### Changed (Breaking) diff --git a/Scarb.lock b/Scarb.lock index 1f56f879b..cc3ff6a41 100644 --- a/Scarb.lock +++ b/Scarb.lock @@ -1,12 +1,6 @@ # Code generated by scarb DO NOT EDIT. version = 1 -[[package]] -name = "corelib_imports" -version = "0.1.3" -source = "registry+https://scarbs.xyz/" -checksum = "sha256:1dbf8982484a6a085a143f2352dfbc716261c50c60551eb03bff5f0167260ad9" - [[package]] name = "openzeppelin" version = "4.0.0-alpha.1" @@ -45,7 +39,7 @@ dependencies = [ name = "openzeppelin_account" version = "4.0.0-alpha.1" dependencies = [ - "corelib_imports", + "openzeppelin_corelib_imports", "openzeppelin_interfaces", "openzeppelin_introspection", "openzeppelin_test_common", @@ -54,6 +48,10 @@ dependencies = [ "snforge_std", ] +[[package]] +name = "openzeppelin_corelib_imports" +version = "4.0.0-alpha.1" + [[package]] name = "openzeppelin_finance" version = "4.0.0-alpha.1" diff --git a/Scarb.toml b/Scarb.toml index 5c623f7ed..1d0a86a5a 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -2,6 +2,7 @@ members = [ "packages/access", "packages/account", + "packages/corelib_imports", "packages/finance", "packages/interfaces", "packages/governance", @@ -44,7 +45,6 @@ keywords = [ [workspace.dependencies] assert_macros = "2.18.0" -corelib_imports = "0.1.2" starknet = "2.18.0" snforge_std = "0.62.1" diff --git a/docs/modules/ROOT/pages/api/account.adoc b/docs/modules/ROOT/pages/api/account.adoc index 941515eda..e34f55729 100644 --- a/docs/modules/ROOT/pages/api/account.adoc +++ b/docs/modules/ROOT/pages/api/account.adoc @@ -66,6 +66,87 @@ Validates whether a signature is valid or not for the given message hash. Returns the short string `'VALID'` if valid, otherwise it reverts. +[.contract] +[[IFeltArrayDeployable]] +=== `++IFeltArrayDeployable++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v4.0.0-alpha.1/packages/interfaces/src/account/accounts.cairo[{github-icon},role=heading-link] + +```cairo +use openzeppelin_interfaces::accounts::IFeltArrayDeployable; +``` + +Deployment validation interface for accounts whose public key is encoded as an array of felts. + +[.contract-index] +.Functions +-- +* xref:#IFeltArrayDeployable-\\__validate_deploy__[`++__validate_deploy__(class_hash, contract_address_salt, public_key)++`] +-- + +[#IFeltArrayDeployable-Functions] +==== Functions + +[.contract-item] +[[IFeltArrayDeployable-__validate_deploy__]] +==== `[.contract-item-name]#++__validate_deploy__++#++(class_hash: felt252, contract_address_salt: felt252, public_key: Array) → felt252++` [.item-kind]#external# + +Validates a deploy-account transaction. + +This function is used by the protocol to verify `deploy_account` transactions. + +Returns the short string `'VALID'` if valid, otherwise it reverts. + +[.contract] +[[IFeltArrayPublicKey]] +=== `++IFeltArrayPublicKey++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v4.0.0-alpha.1/packages/interfaces/src/account/accounts.cairo[{github-icon},role=heading-link] + +```cairo +use openzeppelin_interfaces::accounts::IFeltArrayPublicKey; +``` + +Public-key management interface for accounts whose key is encoded as an array of felts. + +[.contract-index] +.Functions +-- +* xref:#IFeltArrayPublicKey-get_public_key[`++get_public_key()++`] +* xref:#IFeltArrayPublicKey-set_public_key[`++set_public_key(new_public_key, signature)++`] +-- + +[#IFeltArrayPublicKey-Functions] +==== Functions + +[.contract-item] +[[IFeltArrayPublicKey-get_public_key]] +==== `[.contract-item-name]#++get_public_key++#++() → Array++` [.item-kind]#external# + +Returns the account's current public key. + +[.contract-item] +[[IFeltArrayPublicKey-set_public_key]] +==== `[.contract-item-name]#++set_public_key++#++(new_public_key: Array, signature: Span)++` [.item-kind]#external# + +Sets the public key of the account to `new_public_key`. + +[.contract] +[[IFeltArrayPublicKeyCamel]] +=== `++IFeltArrayPublicKeyCamel++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v4.0.0-alpha.1/packages/interfaces/src/account/accounts.cairo[{github-icon},role=heading-link] + +```cairo +use openzeppelin_interfaces::accounts::IFeltArrayPublicKeyCamel; +``` + +Camel-case aliases `getPublicKey` and `setPublicKey` for xref:IFeltArrayPublicKey[`IFeltArrayPublicKey`]. + +[.contract] +[[FeltArrayAccountABI]] +=== `++FeltArrayAccountABI++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v4.0.0-alpha.1/packages/interfaces/src/account/accounts.cairo[{github-icon},role=heading-link] + +```cairo +use openzeppelin_interfaces::accounts::FeltArrayAccountABI; +``` + +Aggregate ABI trait combining `ISRC6`, `ISRC5`, `IDeclarer`, xref:IFeltArrayDeployable[`IFeltArrayDeployable`], xref:IFeltArrayPublicKey[`IFeltArrayPublicKey`], and their camel-case aliases. + [.contract] [[ISRC9_V2]] === `++ISRC9_V2++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v4.0.0-alpha.1/packages/interfaces/src/account/src9.cairo[{github-icon},role=heading-link] @@ -621,6 +702,267 @@ Emitted when a `public_key` is added. Emitted when a `public_key` is removed. +[.contract] +[[Falcon512AccountComponent]] +=== `++Falcon512AccountComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v4.0.0-alpha.1/packages/account/src/falcon_512/account.cairo[{github-icon},role=heading-link] + +:FalconOwnerAdded: xref:Falcon512AccountComponent-OwnerAdded[OwnerAdded] +:FalconOwnerRemoved: xref:Falcon512AccountComponent-OwnerRemoved[OwnerRemoved] + +```cairo +use openzeppelin_account::{ + Falcon512AccountComponent, Falcon512ShakeDirectVerifier, Falcon512ShakeVerifier, + Falcon512SignatureVerifier, +}; +``` + +Account component for canonical packed Falcon-512 public keys. The component is generic over an +implementation of `Falcon512SignatureVerifier`; use `Falcon512ShakeVerifier` for the +verifier-checked product-hint signature or `Falcon512ShakeDirectVerifier` to recompute the +polynomial product on-chain. + +[WARNING] +==== +The supplied verifiers target the verification relation and SHAKE-256 hash-to-point from the +FALCON submission selected by NIST. Their public-key and signature encodings are contract-specific, +and they are not FN-DSA (FIPS 206) implementations. +==== + +NOTE: {src5-component-required-note} + +[.contract-index#Falcon512AccountComponent-Embeddable-Mixin-Impl] +.{mixin-impls} +-- +.Falcon512AccountMixinImpl + +* xref:#Falcon512AccountComponent-Embeddable-Impls-SRC6Impl[`++SRC6Impl++`] +* xref:#Falcon512AccountComponent-Embeddable-Impls-DeclarerImpl[`++DeclarerImpl++`] +* xref:#Falcon512AccountComponent-Embeddable-Impls-DeployableImpl[`++DeployableImpl++`] +* xref:#Falcon512AccountComponent-Embeddable-Impls-PublicKeyImpl[`++PublicKeyImpl++`] +* xref:#Falcon512AccountComponent-Embeddable-Impls-SRC6CamelOnlyImpl[`++SRC6CamelOnlyImpl++`] +* xref:#Falcon512AccountComponent-Embeddable-Impls-PublicKeyCamelImpl[`++PublicKeyCamelImpl++`] +* xref:api/introspection.adoc#SRC5Component-Embeddable-Impls[`++SRC5Impl++`] +-- + +[.contract-index#Falcon512AccountComponent-Embeddable-Impls] +.Embeddable Implementations +-- +[.sub-index#Falcon512AccountComponent-Embeddable-Impls-SRC6Impl] +.SRC6Impl + +* xref:#Falcon512AccountComponent-\\__execute__[`++__execute__(self, calls)++`] +* xref:#Falcon512AccountComponent-\\__validate__[`++__validate__(self, calls)++`] +* xref:#Falcon512AccountComponent-is_valid_signature[`++is_valid_signature(self, hash, signature)++`] + +[.sub-index#Falcon512AccountComponent-Embeddable-Impls-DeclarerImpl] +.DeclarerImpl + +* xref:#Falcon512AccountComponent-\\__validate_declare__[`++__validate_declare__(self, class_hash)++`] + +[.sub-index#Falcon512AccountComponent-Embeddable-Impls-DeployableImpl] +.DeployableImpl + +* xref:#Falcon512AccountComponent-\\__validate_deploy__[`++__validate_deploy__(self, class_hash, contract_address_salt, public_key)++`] + +[.sub-index#Falcon512AccountComponent-Embeddable-Impls-PublicKeyImpl] +.PublicKeyImpl + +* xref:#Falcon512AccountComponent-get_public_key[`++get_public_key(self)++`] +* xref:#Falcon512AccountComponent-set_public_key[`++set_public_key(self, new_public_key, signature)++`] + +[.sub-index#Falcon512AccountComponent-Embeddable-Impls-SRC6CamelOnlyImpl] +.SRC6CamelOnlyImpl + +* xref:#Falcon512AccountComponent-isValidSignature[`++isValidSignature(self, hash, signature)++`] + +[.sub-index#Falcon512AccountComponent-Embeddable-Impls-PublicKeyCamelImpl] +.PublicKeyCamelImpl + +* xref:#Falcon512AccountComponent-getPublicKey[`++getPublicKey(self)++`] +* xref:#Falcon512AccountComponent-setPublicKey[`++setPublicKey(self, newPublicKey, signature)++`] +-- + +[.contract-index] +.Internal Implementations +-- +.InternalImpl + +* xref:#Falcon512AccountComponent-initializer[`++initializer(self, public_key)++`] +* xref:#Falcon512AccountComponent-assert_only_self[`++assert_only_self(self)++`] +* xref:#Falcon512AccountComponent-assert_valid_new_owner[`++assert_valid_new_owner(self, current_owner, new_owner, signature)++`] +* xref:#Falcon512AccountComponent-validate_transaction[`++validate_transaction(self)++`] +* xref:#Falcon512AccountComponent-_set_public_key[`++_set_public_key(self, new_public_key)++`] +* xref:#Falcon512AccountComponent-read_public_key[`++read_public_key(self)++`] +-- + +[.contract-index] +.Events +-- +* xref:#Falcon512AccountComponent-OwnerAdded[`++OwnerAdded(new_owner_guid)++`] +* xref:#Falcon512AccountComponent-OwnerRemoved[`++OwnerRemoved(removed_owner_guid)++`] +-- + +==== Verifier strategies + +`Falcon512SignatureVerifier` defines the compile-time verification strategy used by the component. +Its `verify` function authenticates a message hash and its `is_valid_public_key` function validates +the packed public-key encoding. + +`Falcon512ShakeVerifier` accepts a 60-felt signature containing 29 felts for `s1`, 2 salt felts, and +a 29-felt polynomial-product hint. `Falcon512ShakeDirectVerifier` accepts 29 felts for `s1` and 2 +salt felts, and recomputes the polynomial product on-chain. + +==== Felt encoding + +Each 29-felt polynomial represents 512 coefficients in `Z_q`, where `q = 12289`. Each of the first +28 felts stores 18 coefficients in two 128-bit halves. The low half contains the first nine +coefficients and the high half contains the next nine, with each half encoded as +`c0 + q(c1 + q(c2 + ... + q*c8))`. The final felt stores eight coefficients in its low half and +requires a zero high half. Public keys use the verifier's NTT-domain coefficient order. + +Each signature salt felt must be smaller than `2^160`. The two felts encode the 40-byte Falcon salt +as two consecutive 20-byte little-endian limbs. Hash-to-point absorbs those limbs followed by the +message hash encoded as 32 little-endian bytes. + +IMPORTANT: Build the deployable Falcon presets from this repository with +`scarb --release build -p openzeppelin_presets`. The workspace dev profile produces Falcon Sierra +artifacts that cannot be lowered to CASM. Only the workspace's `target/release` Falcon preset +artifacts are suitable for declaration. Consumer projects that embed the component must likewise +use a declaration profile with inlining enabled. + +[#Falcon512AccountComponent-Embeddable-Functions] +==== Embeddable functions + +[.contract-item] +[[Falcon512AccountComponent-__execute__]] +==== `[.contract-item-name]#++__execute__++#++(self: @ContractState, calls: Array)++` [.item-kind]#external# + +Executes the list of calls after protocol validation succeeds. + +[.contract-item] +[[Falcon512AccountComponent-__validate__]] +==== `[.contract-item-name]#++__validate__++#++(self: @ContractState, calls: Array) → felt252++` [.item-kind]#external# + +Validates an invoke transaction with the selected verifier strategy. + +[.contract-item] +[[Falcon512AccountComponent-is_valid_signature]] +==== `[.contract-item-name]#++is_valid_signature++#++(self: @ContractState, hash: felt252, signature: Array) → felt252++` [.item-kind]#external# + +Returns the short string `'VALID'` when `signature` authenticates `hash` under the current public +key, and returns `0` otherwise. + +[.contract-item] +[[Falcon512AccountComponent-__validate_declare__]] +==== `[.contract-item-name]#++__validate_declare__++#++(self: @ContractState, class_hash: felt252) → felt252++` [.item-kind]#external# + +Validates a declare transaction with the selected verifier strategy. + +[.contract-item] +[[Falcon512AccountComponent-__validate_deploy__]] +==== `[.contract-item-name]#++__validate_deploy__++#++(self: @ContractState, class_hash: felt252, contract_address_salt: felt252, public_key: Array) → felt252++` [.item-kind]#external# + +Validates a deploy-account transaction with the selected verifier strategy. + +[.contract-item] +[[Falcon512AccountComponent-get_public_key]] +==== `[.contract-item-name]#++get_public_key++#++(self: @ContractState) → Array++` [.item-kind]#external# + +Returns the current packed public key. + +[.contract-item] +[[Falcon512AccountComponent-set_public_key]] +==== `[.contract-item-name]#++set_public_key++#++(ref self: ContractState, new_public_key: Array, signature: Span)++` [.item-kind]#external# + +Sets the current key to `new_public_key`. + +Requirements: + +- The caller must be the account contract itself. +- `new_public_key` must use the canonical 29-felt encoding. +- `signature` must authenticate the ownership-acceptance message under `new_public_key`. + +Emits both a {FalconOwnerRemoved} and a {FalconOwnerAdded} event. + +[.contract-item] +[[Falcon512AccountComponent-isValidSignature]] +==== `[.contract-item-name]#++isValidSignature++#++(self: @ContractState, hash: felt252, signature: Array) → felt252++` [.item-kind]#external# + +See xref:Falcon512AccountComponent-is_valid_signature[is_valid_signature]. + +[.contract-item] +[[Falcon512AccountComponent-getPublicKey]] +==== `[.contract-item-name]#++getPublicKey++#++(self: @ContractState) → Array++` [.item-kind]#external# + +See xref:Falcon512AccountComponent-get_public_key[get_public_key]. + +[.contract-item] +[[Falcon512AccountComponent-setPublicKey]] +==== `[.contract-item-name]#++setPublicKey++#++(ref self: ContractState, newPublicKey: Array, signature: Span)++` [.item-kind]#external# + +See xref:Falcon512AccountComponent-set_public_key[set_public_key]. + +[#Falcon512AccountComponent-Internal-Functions] +==== Internal functions + +[.contract-item] +[[Falcon512AccountComponent-initializer]] +==== `[.contract-item-name]#++initializer++#++(ref self: ComponentState, public_key: Array)++` [.item-kind]#internal# + +Validates and stores a canonical packed public key and registers the `ISRC6` interface ID. + +Emits a {FalconOwnerAdded} event. + +[.contract-item] +[[Falcon512AccountComponent-assert_only_self]] +==== `[.contract-item-name]#++assert_only_self++#++(self: @ComponentState)++` [.item-kind]#internal# + +Validates that the caller is the account contract itself. + +[.contract-item] +[[Falcon512AccountComponent-assert_valid_new_owner]] +==== `[.contract-item-name]#++assert_valid_new_owner++#++(self: @ComponentState, current_owner: Span, new_owner: Span, signature: Span)++` [.item-kind]#internal# + +Validates that `new_owner` signed the domain-separated ownership-acceptance message. + +CAUTION: The caller must pass the account's current public key as `current_owner`. + +[.contract-item] +[[Falcon512AccountComponent-validate_transaction]] +==== `[.contract-item-name]#++validate_transaction++#++(self: @ComponentState) → felt252++` [.item-kind]#internal# + +Validates the current transaction signature and returns the short string `'VALID'`. + +[.contract-item] +[[Falcon512AccountComponent-_set_public_key]] +==== `[.contract-item-name]#++_set_public_key++#++(ref self: ComponentState, new_public_key: Array)++` [.item-kind]#internal# + +Stores `new_public_key` and emits a {FalconOwnerAdded} event. Callers are responsible for validating +authorization and key encoding before using this function. + +[.contract-item] +[[Falcon512AccountComponent-read_public_key]] +==== `[.contract-item-name]#++read_public_key++#++(self: @ComponentState) → Array++` [.item-kind]#internal# + +Returns the stored key in its 29-felt packed representation. + +[#Falcon512AccountComponent-Events] +==== Events + +The event GUID is the Poseidon hash of the packed public key. + +[.contract-item] +[[Falcon512AccountComponent-OwnerAdded]] +==== `[.contract-item-name]#++OwnerAdded++#++(new_owner_guid: felt252)++` [.item-kind]#event# + +Emitted when a public key is added. + +[.contract-item] +[[Falcon512AccountComponent-OwnerRemoved]] +==== `[.contract-item-name]#++OwnerRemoved++#++(removed_owner_guid: felt252)++` [.item-kind]#event# + +Emitted when a public key is removed. + == Extensions [.contract] @@ -828,3 +1170,139 @@ Requirements: - The caller is the account contract itself. - `new_class_hash` cannot be zero. + +[WARNING] +==== +The Falcon presets use contract-specific public-key and signature encodings for the FALCON +submission verification relation. They are not FN-DSA (FIPS 206) implementations. +==== + +[.contract] +[[Falcon512ShakeAccountUpgradeable]] +=== `++Falcon512ShakeAccountUpgradeable++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v4.0.0-alpha.1/packages/presets/src/falcon_512_shake_account.cairo[{github-icon},role=heading-link] + +```cairo +use openzeppelin_presets::Falcon512ShakeAccountUpgradeable; +``` + +Upgradeable Falcon-512 account using SHAKE-256 hash-to-point and a 60-felt signature with a +verifier-checked polynomial-product hint. It supports owner-authorized key rotation and outside +execution through xref:#SRC9Component[SRC9]. + +include::../utils/_class_hashes.adoc[] + +[.contract-index] +.{presets-page} +-- +{Falcon512ShakeAccountUpgradeable-class-hash} +-- + +[.contract-index] +.Constructor +-- +* xref:#Falcon512ShakeAccountUpgradeable-constructor[`++constructor(self, public_key)++`] +-- + +[.contract-index] +.Embedded Implementations +-- +.Falcon512AccountComponent +* xref:#Falcon512AccountComponent-Embeddable-Mixin-Impl[`++Falcon512AccountMixinImpl++`] + +.SRC9Component +* xref:#SRC9Component-Embeddable-Impls-OutsideExecutionV2Impl[`++OutsideExecutionV2Impl++`] +-- + +[.contract-index] +.External Functions +-- +* xref:#Falcon512ShakeAccountUpgradeable-upgrade[`++upgrade(self, new_class_hash)++`] +-- + +[#Falcon512ShakeAccountUpgradeable-constructor-section] +==== Constructor + +[.contract-item] +[[Falcon512ShakeAccountUpgradeable-constructor]] +==== `[.contract-item-name]#++constructor++#++(ref self: ContractState, public_key: Array)++` [.item-kind]#constructor# + +Sets a canonical 29-felt NTT-domain public key and registers the interfaces the contract supports. + +[#Falcon512ShakeAccountUpgradeable-external-functions] +==== External functions + +[.contract-item] +[[Falcon512ShakeAccountUpgradeable-upgrade]] +==== `[.contract-item-name]#++upgrade++#++(ref self: ContractState, new_class_hash: ClassHash)++` [.item-kind]#external# + +Upgrades the account implementation to `new_class_hash`. + +Requirements: + +- The caller must be the account contract itself. +- `new_class_hash` cannot be zero. + +[.contract] +[[Falcon512ShakeDirectAccountUpgradeable]] +=== `++Falcon512ShakeDirectAccountUpgradeable++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v4.0.0-alpha.1/packages/presets/src/falcon_512_shake_direct_account.cairo[{github-icon},role=heading-link] + +```cairo +use openzeppelin_presets::Falcon512ShakeDirectAccountUpgradeable; +``` + +Upgradeable Falcon-512 account using SHAKE-256 hash-to-point and a 31-felt signature. It recomputes +the polynomial product on-chain and supports owner-authorized key rotation and outside execution +through xref:#SRC9Component[SRC9]. + +include::../utils/_class_hashes.adoc[] + +[.contract-index] +.{presets-page} +-- +{Falcon512ShakeDirectAccountUpgradeable-class-hash} +-- + +[.contract-index] +.Constructor +-- +* xref:#Falcon512ShakeDirectAccountUpgradeable-constructor[`++constructor(self, public_key)++`] +-- + +[.contract-index] +.Embedded Implementations +-- +.Falcon512AccountComponent +* xref:#Falcon512AccountComponent-Embeddable-Mixin-Impl[`++Falcon512AccountMixinImpl++`] + +.SRC9Component +* xref:#SRC9Component-Embeddable-Impls-OutsideExecutionV2Impl[`++OutsideExecutionV2Impl++`] +-- + +[.contract-index] +.External Functions +-- +* xref:#Falcon512ShakeDirectAccountUpgradeable-upgrade[`++upgrade(self, new_class_hash)++`] +-- + +[#Falcon512ShakeDirectAccountUpgradeable-constructor-section] +==== Constructor + +[.contract-item] +[[Falcon512ShakeDirectAccountUpgradeable-constructor]] +==== `[.contract-item-name]#++constructor++#++(ref self: ContractState, public_key: Array)++` [.item-kind]#constructor# + +Sets a canonical 29-felt NTT-domain public key and registers the interfaces the contract supports. + +[#Falcon512ShakeDirectAccountUpgradeable-external-functions] +==== External functions + +[.contract-item] +[[Falcon512ShakeDirectAccountUpgradeable-upgrade]] +==== `[.contract-item-name]#++upgrade++#++(ref self: ContractState, new_class_hash: ClassHash)++` [.item-kind]#external# + +Upgrades the account implementation to `new_class_hash`. + +Requirements: + +- The caller must be the account contract itself. +- `new_class_hash` cannot be zero. diff --git a/docs/modules/ROOT/pages/presets.adoc b/docs/modules/ROOT/pages/presets.adoc index 6437590f5..7a0511b8e 100644 --- a/docs/modules/ROOT/pages/presets.adoc +++ b/docs/modules/ROOT/pages/presets.adoc @@ -3,6 +3,8 @@ :erc721-upgradeable: xref:/api/erc721.adoc#ERC721Upgradeable[ERC721Upgradeable] :erc1155-upgradeable: xref:/api/erc1155.adoc#ERC1155Upgradeable[ERC1155Upgradeable] :eth-account-upgradeable: xref:/api/account.adoc#EthAccountUpgradeable[EthAccountUpgradeable] +:falcon512-shake-account-upgradeable: xref:/api/account.adoc#Falcon512ShakeAccountUpgradeable[Falcon512ShakeAccountUpgradeable] +:falcon512-shake-direct-account-upgradeable: xref:/api/account.adoc#Falcon512ShakeDirectAccountUpgradeable[Falcon512ShakeDirectAccountUpgradeable] :udc: xref:/api/udc.adoc#UniversalDeployer[UniversalDeployer] :vesting-wallet: xref:/api/finance.adoc#VestingWallet[VestingWallet] :sierra-class-hashes: https://docs.starknet.io/architecture-and-concepts/smart-contracts/class-hash/[Sierra class hashes] @@ -48,6 +50,12 @@ CAUTION: Before version 4.0.0-alpha.1, class hashes were computed using the `sca | `{eth-account-upgradeable}` | `{EthAccountUpgradeable-class-hash}` +| `{falcon512-shake-account-upgradeable}` +| `{Falcon512ShakeAccountUpgradeable-class-hash}` + +| `{falcon512-shake-direct-account-upgradeable}` +| `{Falcon512ShakeDirectAccountUpgradeable-class-hash}` + | `{udc}` | `{UniversalDeployer-class-hash}` @@ -57,6 +65,10 @@ CAUTION: Before version 4.0.0-alpha.1, class hashes were computed using the `sca TIP: {starkli} class-hash command can be used to compute the class hash from a Sierra artifact. +IMPORTANT: Build Falcon account preset artifacts with +`scarb --release build -p openzeppelin_presets`. Their verification paths require compiler +inlining, so use the artifacts under `target/release` for declaration. + == Usage :setup_project: xref:index.adoc#set_up_your_project[setting up a project] diff --git a/docs/modules/ROOT/pages/utils/_class_hashes.adoc b/docs/modules/ROOT/pages/utils/_class_hashes.adoc index 2396a7ea8..2d8fa2315 100644 --- a/docs/modules/ROOT/pages/utils/_class_hashes.adoc +++ b/docs/modules/ROOT/pages/utils/_class_hashes.adoc @@ -1,16 +1,17 @@ // Version -:class-hash-cairo-version: https://crates.io/crates/cairo-lang-compiler/2.17.0[cairo 2.17.0] +:class-hash-cairo-version: https://crates.io/crates/cairo-lang-compiler/2.18.0[cairo 2.18.0] // Class Hashes -:ERC20Upgradeable-class-hash: 0x02306d411d9061591d2a661f177ea92e53918a474803c33a901bace86dfaea6c -:ERC721Upgradeable-class-hash: 0x0735400adfb617a215a0eadd009e84ecebf10b258b7ce7583d4131f7fa793bf1 -:ERC1155Upgradeable-class-hash: 0x02f81498862c308981a544e7da3e65c715c375c97a881da2d157b2f3a3dcdd93 -:AccountUpgradeable-class-hash: 0x0342f3c683f708fd920e625e425b77726aa4c834da47ec92b4da5c207f025207 -:EthAccountUpgradeable-class-hash: 0x0216f6773aef13b25e5f8ecc989c4a4668d45ba118b41bbdca10d08fc6fda878 -:UniversalDeployer-class-hash: 0x00ce766f9026176e6796a13720059301c4caa1b24cc55d964afb62bed07b0158 -:MetaTransactionV0-class-hash: 0x051d6576154ab74933e521cd0d8e3367d14ff1713fd2b2ce982af1f555564134 -:VestingWallet-class-hash: 0x051be0e6c0a6904b496c77f991f423e766ff465752cb18e93b6eb5ca525cf219 +:ERC20Upgradeable-class-hash: 0x06d081c70145f14c3e0d05eacbfaa01620a27fde89430e6c227a8f4a573e3727 +:ERC721Upgradeable-class-hash: 0x05f3d201fc5c9de757ca6c42ec753d514ab1ea011b60bd139b6167cd069109d2 +:ERC1155Upgradeable-class-hash: 0x075829cba5eea49a998988967e6a97de68824830118f7f008f3ac07d407cb99a +:AccountUpgradeable-class-hash: 0x03013bf2edcf93ac575367eee41cd07bb50e809c78925817b61878db29826144 +:EthAccountUpgradeable-class-hash: 0x000be1cf8839442d664572712fd36554c4d259f86549ad02e22353d10729b900 +:Falcon512ShakeAccountUpgradeable-class-hash: 0x00a6a8a5766d093d4f21c6f913417b03f85427438833149e3ca34f60fff8de4c +:Falcon512ShakeDirectAccountUpgradeable-class-hash: 0x056159818224483320514f4d5fbfb1e2e374cf50a4a97175b6ad18504837c3a9 +:UniversalDeployer-class-hash: 0x05412c7a3e145fdc79862e65df612324d83b0169c071dd564021155bd8cfd29e +:MetaTransactionV0-class-hash: 0x059b5a7a062db4a9fa1ec4b96ea74cac3d55477971d68ac9fba877c087e59808 +:VestingWallet-class-hash: 0x05613fb6c8f729fdcbef50d25c9dd72260762543d48254c59e6851dd3d46595a // Presets page :presets-page: xref:presets.adoc[Sierra class hash] - diff --git a/packages/account/README.md b/packages/account/README.md index 2b5b623a9..eb80ba001 100644 --- a/packages/account/README.md +++ b/packages/account/README.md @@ -2,45 +2,71 @@ > **NOTE:** This document is better viewed at [https://docs.openzeppelin.com/contracts-cairo/api/account](https://docs.openzeppelin.com/contracts-cairo/3.x/api/account) -This crate provides components to implement account contracts that can be used for interacting with the network. +This crate provides components for building account contracts that interact with the network. -- `Account` validates transactions from signatures over the -[STARK Curve](https://docs.starknet.io/architecture-and-concepts/cryptography/#the_stark_curve). +- `AccountComponent` validates transactions from signatures over the + [STARK Curve](https://docs.starknet.io/architecture-and-concepts/cryptography/#the_stark_curve). -- `EthAccount` validates transactions from signatures over the -[Secp256k1 curve](https://en.bitcoin.it/wiki/Secp256k1). +- `EthAccountComponent` validates transactions from signatures over the + [Secp256k1 curve](https://en.bitcoin.it/wiki/Secp256k1). -- `Falcon512ShakeAccount` validates the Falcon-512 verification relation from the - FALCON submission selected by NIST, using SHAKE-256 hash-to-point. Its - contract-specific signature encoding includes a verifier-checked polynomial-product - hint to reduce on-chain execution cost. +- `Falcon512AccountComponent` provides felt-array public-key management and account behavior for + canonical Falcon-512 public keys. It is generic over `Falcon512SignatureVerifier` and supports + two supplied SHAKE-256 strategies: -- `Falcon512ShakeDirectAccount` validates the same relation without the hint by - recomputing the polynomial product on-chain. + - `Falcon512ShakeVerifier` validates a 60-felt signature containing a verifier-checked + polynomial-product hint to reduce on-chain execution cost. -> **WARNING:** These accounts implement the verification relation and SHAKE-256 hash-to-point -> used by FALCON as submitted to NIST. Their felt encodings are contract-specific, and the -> accounts do not claim conformance with FN-DSA/FIPS 206, which remains in development. + - `Falcon512ShakeDirectVerifier` validates a 31-felt signature and recomputes the polynomial + product on-chain. -Both Falcon accounts support owner-authorized key rotation by executing a self-call to +> **WARNING:** The supplied Falcon verifiers target the verification relation and SHAKE-256 +> hash-to-point from the FALCON submission selected by NIST. Their public-key and signature +> encodings are contract-specific, and they are not FN-DSA (FIPS 206) implementations. + +The Falcon component implements invoke, declare, and deploy-account validation, +signature validation, and owner-authorized felt-array public-key management. Contracts embed it +with `SRC5Component` and one of the supplied verifier strategies. Ready-to-deploy variants with +SRC9 outside execution and class upgrades are provided by the `openzeppelin_presets` package. + +Falcon accounts support owner-authorized key rotation by executing a self-call to `set_public_key` or `setPublicKey`. As with the STARK-curve and Secp256k1 accounts, the current key authorizes the outer account transaction and the new key signs a domain-separated ownership-acceptance message. Rotation keeps the same account address. -This is not lost-key recovery: if the current private key is unavailable, these -entrypoints cannot rotate it, and the contracts include no independent recovery mechanism. -Changing the verifier code is a separate class-upgrade concern; these concrete contracts -do not embed an upgrade component. +Because the current key authorizes the outer transaction, rotating a lost or unavailable key +requires an independent recovery mechanism. The upgradeable presets can adopt verifier changes at +the same account address through a self-authorized class upgrade. + +### Build profile + +Build deployable Falcon preset artifacts from this repository with +`scarb --release build -p openzeppelin_presets`. The workspace dev profile produces Falcon Sierra +artifacts that cannot be lowered to CASM. Only the workspace's `target/release` Falcon preset +artifacts are suitable for declaration. A consuming project that embeds the component must +likewise use a declaration profile with inlining enabled. + +### Maintainer workflow: generated Falcon NTT sources + +The Falcon root tables, bit-reversal table, and unrolled production transform are derived and +checked by `scripts/falcon_512/generate_ntt.py`. Regenerate them from the repository root and verify +the checked-in output with: + +```sh +python3 scripts/falcon_512/generate_ntt.py --write +git diff --exit-code -- packages/account/src/falcon_512/ntt +``` ### Interfaces - [`ISRC6`](https://docs.openzeppelin.com/contracts-cairo/3.x/api/account#ISRC6) -- `IFeltArrayDeployable` -- `IFeltArrayPublicKey` -- `IFeltArrayPublicKeyCamel` +- [`IFeltArrayDeployable`](https://docs.openzeppelin.com/contracts-cairo/3.x/api/account#IFeltArrayDeployable) +- [`IFeltArrayPublicKey`](https://docs.openzeppelin.com/contracts-cairo/3.x/api/account#IFeltArrayPublicKey) +- [`IFeltArrayPublicKeyCamel`](https://docs.openzeppelin.com/contracts-cairo/3.x/api/account#IFeltArrayPublicKeyCamel) - [`ISRC9_V2`](https://docs.openzeppelin.com/contracts-cairo/3.x/api/account#ISRC9_V2) ### Components - [`AccountComponent`](https://docs.openzeppelin.com/contracts-cairo/3.x/api/account#AccountComponent) - [`EthAccountComponent`](https://docs.openzeppelin.com/contracts-cairo/3.x/api/account#EthAccountComponent) +- [`Falcon512AccountComponent`](https://docs.openzeppelin.com/contracts-cairo/3.x/api/account#Falcon512AccountComponent) - [`SRC9Component`](https://docs.openzeppelin.com/contracts-cairo/3.x/api/account#SRC9Component) diff --git a/packages/account/Scarb.toml b/packages/account/Scarb.toml index 008d9d360..de50c8916 100644 --- a/packages/account/Scarb.toml +++ b/packages/account/Scarb.toml @@ -23,8 +23,8 @@ fmt.workspace = true scarb.workspace = true [dependencies] -corelib_imports.workspace = true starknet.workspace = true +openzeppelin_corelib_imports = { path = "../corelib_imports" } openzeppelin_introspection = { path = "../introspection" } openzeppelin_interfaces = { path = "../interfaces" } openzeppelin_utils = { path = "../utils" } @@ -37,7 +37,7 @@ openzeppelin_test_common = { path = "../test_common" } [features] fuzzing = [] -# Executes the generated production NTT path; requires the release compiler profile. +# Enables tests for the generated production NTT path; requires the release compiler profile. falcon_fast_tests = [] [lib] diff --git a/packages/account/src/falcon_512.cairo b/packages/account/src/falcon_512.cairo index cd36afff0..9bd950335 100644 --- a/packages/account/src/falcon_512.cairo +++ b/packages/account/src/falcon_512.cairo @@ -1,102 +1,26 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512.cairo) -//! Falcon-512 SHAKE account contracts and their verification implementation. +//! Falcon-512 SHAKE account component and verifier strategies. //! -//! Both accounts implement the Falcon-512 verification relation from the FALCON submission -//! selected by NIST, using SHAKE-256 hash-to-point. The hint variant adds a verifier-checked -//! polynomial-product witness to its contract-specific signature encoding to reduce on-chain -//! execution cost; the direct variant recomputes that product on-chain. Their packed felt public -//! keys and signature encodings are contract-specific, and neither account claims conformance -//! with FN-DSA/FIPS 206. +//! `Falcon512ShakeVerifier` validates the FALCON submission relation with a verifier-checked +//! polynomial-product hint that reduces on-chain execution cost. `Falcon512ShakeDirectVerifier` +//! recomputes the product on-chain. Both strategies use SHAKE-256 hash-to-point and +//! contract-specific felt encodings; they are not FN-DSA (FIPS 206) implementations. +//! +//! `Falcon512AccountComponent` provides SRC6 account behavior and owner-authorized key rotation +//! for canonical packed Falcon-512 public keys. Ready-to-deploy compositions are provided by the +//! `openzeppelin_presets` package. -pub(crate) mod account; +pub mod account; pub(crate) mod falcon; -pub mod falcon_512_shake; -pub mod falcon_512_shake_direct; pub(crate) mod hashing; pub(crate) mod ntt; pub(crate) mod packing; +pub mod verifier; +pub mod verifier_impls; pub(crate) mod zq; -use account::Falcon512SignatureVerifier; - -pub use falcon_512_shake::Falcon512ShakeAccount; -pub use falcon_512_shake_direct::Falcon512ShakeDirectAccount; -use hashing::hash_to_point::hash_to_point_shake_512; - -/// Number of felts in a packed Falcon-512 public key. -pub(crate) const PUBLIC_KEY_FELTS: u32 = 29; - -/// Number of felts in a Falcon-512 signature carrying a product hint. -pub(crate) const SIGNATURE_FELTS: u32 = 60; - -/// Number of felts in a hint-free Falcon-512 signature. -pub(crate) const DIRECT_SIGNATURE_FELTS: u32 = 31; - -/// Verifier for the 60-felt SHAKE-256 signature carrying a polynomial-product hint. -pub(crate) impl Falcon512ShakeVerifier of Falcon512SignatureVerifier { - fn verify(message_hash: felt252, public_key: Span, signature: Span) -> bool { - if public_key.len() != PUBLIC_KEY_FELTS || signature.len() != SIGNATURE_FELTS { - return false; - } - let h_ntt = match packing::unpack_512_u16(public_key) { - Some(value) => value, - None => { return false; }, - }; - let s1 = match packing::unpack_512_u16(signature.slice(0, 29)) { - Some(value) => value, - None => { return false; }, - }; - let salt_a = *signature.at(29); - let salt_b = *signature.at(30); - let mul_hint = match packing::unpack_512_u16(signature.slice(31, 29)) { - Some(value) => value, - None => { return false; }, - }; - let message_point = match hash_to_point_shake_512(message_hash, salt_a, salt_b) { - Some(value) => value, - None => { return false; }, - }; - falcon::verify_512_with_hint_u16( - s1.span(), h_ntt.span(), mul_hint.span(), message_point.span(), - ) - } - - fn is_valid_public_key(public_key: Span) -> bool { - if public_key.len() != PUBLIC_KEY_FELTS { - return false; - } - match packing::unpack_512_u16(public_key) { - Some(_) => true, - None => false, - } - } -} - -/// Verifier for the 31-felt SHAKE-256 signature that recomputes the product on-chain. -pub(crate) impl Falcon512ShakeDirectVerifier of Falcon512SignatureVerifier { - fn verify(message_hash: felt252, public_key: Span, signature: Span) -> bool { - if public_key.len() != PUBLIC_KEY_FELTS || signature.len() != DIRECT_SIGNATURE_FELTS { - return false; - } - let h_ntt = match packing::unpack_512_u16(public_key) { - Some(value) => value, - None => { return false; }, - }; - let s1 = match packing::unpack_512_u16(signature.slice(0, 29)) { - Some(value) => value, - None => { return false; }, - }; - let salt_a = *signature.at(29); - let salt_b = *signature.at(30); - let message_point = match hash_to_point_shake_512(message_hash, salt_a, salt_b) { - Some(value) => value, - None => { return false; }, - }; - falcon::verify_512_direct_u16(s1.span(), h_ntt.span(), message_point.span()) - } - fn is_valid_public_key(public_key: Span) -> bool { - Falcon512ShakeVerifier::is_valid_public_key(public_key) - } -} +pub use account::Falcon512AccountComponent; +pub use verifier::Falcon512SignatureVerifier; +pub use verifier_impls::{Falcon512ShakeDirectVerifier, Falcon512ShakeVerifier}; diff --git a/packages/account/src/falcon_512/account.cairo b/packages/account/src/falcon_512/account.cairo index 56a05ead1..9a8746052 100644 --- a/packages/account/src/falcon_512/account.cairo +++ b/packages/account/src/falcon_512/account.cairo @@ -1,20 +1,15 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/account.cairo) -//! Shared account component for the Falcon-512 SHAKE account contracts. +use super::verifier::Falcon512SignatureVerifier; -/// Verification surface implemented by each Falcon-512 account variant. -pub(crate) trait Falcon512SignatureVerifier { - /// Returns whether `signature` authenticates `message_hash` under `public_key`. - fn verify(message_hash: felt252, public_key: Span, signature: Span) -> bool; - - /// Returns whether the packed public key has the required canonical encoding. - fn is_valid_public_key(public_key: Span) -> bool; -} - -/// Account component for array-encoded Falcon-512 public keys. +/// # Falcon512Account Component +/// +/// The Falcon512Account component enables contracts to behave as accounts using canonical +/// 29-felt NTT-domain Falcon-512 public keys. Embedding contracts select an implementation of +/// `Falcon512SignatureVerifier`. #[starknet::component] -pub(crate) mod Falcon512AccountComponent { +pub mod Falcon512AccountComponent { use core::hash::{HashStateExTrait, HashStateTrait}; use core::num::traits::Zero; use core::poseidon::{PoseidonTrait, poseidon_hash_span}; @@ -33,7 +28,7 @@ pub(crate) mod Falcon512AccountComponent { #[storage] pub struct Storage { - pub public_key: Vec, + pub Falcon512Account_public_key: Vec, } #[event] @@ -56,13 +51,17 @@ pub(crate) mod Falcon512AccountComponent { } pub mod Errors { - pub const INVALID_CALLER: felt252 = 'Account: invalid caller'; - pub const INVALID_PUBLIC_KEY: felt252 = 'Account: invalid public key'; - pub const INVALID_SIGNATURE: felt252 = 'Account: invalid signature'; - pub const INVALID_TX_VERSION: felt252 = 'Account: invalid tx version'; - pub const UNAUTHORIZED: felt252 = 'Account: unauthorized'; + pub const INVALID_CALLER: felt252 = 'Falcon512: invalid caller'; + pub const INVALID_PUBLIC_KEY: felt252 = 'Falcon512: invalid public key'; + pub const INVALID_SIGNATURE: felt252 = 'Falcon512: invalid signature'; + pub const INVALID_TX_VERSION: felt252 = 'Falcon512: invalid tx version'; + pub const UNAUTHORIZED: felt252 = 'Falcon512: unauthorized'; } + // + // External + // + #[embeddable_as(SRC6Impl)] impl SRC6< TContractState, @@ -71,7 +70,13 @@ pub(crate) mod Falcon512AccountComponent { +SRC5Component::HasComponent, +Drop, > of interface::ISRC6> { - /// Executes calls forwarded by the account after protocol validation succeeds. + /// Executes a list of calls from the account. + /// + /// Requirements: + /// + /// - The transaction version must be greater than or equal to `MIN_TRANSACTION_VERSION`. + /// - If the transaction is a simulation (version >= `QUERY_OFFSET`), it must be + /// greater than or equal to `QUERY_OFFSET` + `MIN_TRANSACTION_VERSION`. fn __execute__(self: @ComponentState, calls: Array) { let sender = starknet::get_caller_address(); assert(sender.is_zero(), Errors::INVALID_CALLER); @@ -82,13 +87,13 @@ pub(crate) mod Falcon512AccountComponent { } } - /// Validates an invoke transaction with the current transaction signature. + /// Verifies the validity of the signature for the current transaction. + /// This function is used by the protocol to verify `invoke` transactions. fn __validate__(self: @ComponentState, calls: Array) -> felt252 { - let _ = calls; self.validate_transaction::() } - /// Verifies a signature for an arbitrary message hash. + /// Verifies that the given signature is valid for the given hash. fn is_valid_signature( self: @ComponentState, hash: felt252, signature: Array, ) -> felt252 { @@ -108,11 +113,11 @@ pub(crate) mod Falcon512AccountComponent { +SRC5Component::HasComponent, +Drop, > of interface::IDeclarer> { - /// Validates a declare transaction with the current transaction signature. + /// Verifies the validity of the signature for the current transaction. + /// This function is used by the protocol to verify `declare` transactions. fn __validate_declare__( self: @ComponentState, class_hash: felt252, ) -> felt252 { - let _ = class_hash; self.validate_transaction::() } } @@ -125,16 +130,14 @@ pub(crate) mod Falcon512AccountComponent { +SRC5Component::HasComponent, +Drop, > of interface::IFeltArrayDeployable> { - /// Validates a deploy-account transaction with the current transaction signature. + /// Verifies the validity of the signature for the current transaction. + /// This function is used by the protocol to verify `deploy_account` transactions. fn __validate_deploy__( self: @ComponentState, class_hash: felt252, contract_address_salt: felt252, public_key: Array, ) -> felt252 { - let _ = class_hash; - let _ = contract_address_salt; - let _ = public_key; self.validate_transaction::() } } @@ -228,6 +231,7 @@ pub(crate) mod Falcon512AccountComponent { impl SRC5: SRC5Component::HasComponent, +Drop, > of interface::FeltArrayAccountABI> { + // ISRC6 fn __execute__(self: @ComponentState, calls: Array) { SRC6::__execute__(self, calls) } @@ -242,18 +246,21 @@ pub(crate) mod Falcon512AccountComponent { SRC6::is_valid_signature(self, hash, signature) } + // ISRC6CamelOnly fn isValidSignature( self: @ComponentState, hash: felt252, signature: Array, ) -> felt252 { SRC6CamelOnly::isValidSignature(self, hash, signature) } + // IDeclarer fn __validate_declare__( self: @ComponentState, class_hash: felt252, ) -> felt252 { Declarer::__validate_declare__(self, class_hash) } + // IFeltArrayDeployable fn __validate_deploy__( self: @ComponentState, class_hash: felt252, @@ -263,6 +270,7 @@ pub(crate) mod Falcon512AccountComponent { Deployable::__validate_deploy__(self, class_hash, contract_address_salt, public_key) } + // IFeltArrayPublicKey fn get_public_key(self: @ComponentState) -> Array { PublicKey::get_public_key(self) } @@ -275,6 +283,7 @@ pub(crate) mod Falcon512AccountComponent { PublicKey::set_public_key(ref self, new_public_key, signature); } + // IFeltArrayPublicKeyCamel fn getPublicKey(self: @ComponentState) -> Array { PublicKeyCamel::getPublicKey(self) } @@ -287,6 +296,7 @@ pub(crate) mod Falcon512AccountComponent { PublicKeyCamel::setPublicKey(ref self, newPublicKey, signature); } + // ISRC5 fn supports_interface( self: @ComponentState, interface_id: felt252, ) -> bool { @@ -295,6 +305,10 @@ pub(crate) mod Falcon512AccountComponent { } } + // + // Internal + // + #[generate_trait] pub impl InternalImpl< TContractState, @@ -302,7 +316,8 @@ pub(crate) mod Falcon512AccountComponent { impl SRC5: SRC5Component::HasComponent, +Drop, > of InternalTrait { - /// Validates and stores the packed public key and registers SRC6 support. + /// Initializes the account with a canonical packed Falcon-512 public key and registers + /// the ISRC6 interface ID. /// /// Emits an `OwnerAdded` event. fn initializer( @@ -323,9 +338,14 @@ pub(crate) mod Falcon512AccountComponent { assert(self == caller, Errors::UNAUTHORIZED); } - /// Validates that the new owner accepted ownership of the account. + /// Validates that `new_owner` accepted ownership of the account. + /// + /// WARNING: This function assumes that `current_owner` is the account's current owner + /// and does not validate this assumption. + /// + /// Requirements: /// - /// WARNING: This function assumes that `current_owner` is the account's current owner. + /// - The signature must be valid for `new_owner`. fn assert_valid_new_owner( self: @ComponentState, current_owner: Span, @@ -342,7 +362,8 @@ pub(crate) mod Falcon512AccountComponent { assert(Verifier::verify(message_hash, new_owner, signature), Errors::INVALID_SIGNATURE); } - /// Validates the transaction hash against the transaction signature. + /// Validates the signature for the current transaction. + /// Returns the short string `VALID` if valid, otherwise it reverts. fn validate_transaction( self: @ComponentState, ) -> felt252 { @@ -356,25 +377,23 @@ pub(crate) mod Falcon512AccountComponent { starknet::VALIDATED } - /// Sets the public key without validating the caller or key encoding. - /// The usage of this method outside the initializer and `set_public_key` is discouraged. - /// - /// Emits an `OwnerAdded` event. + /// Stores `new_public_key` and emits an `OwnerAdded` event. + /// Callers are responsible for enforcing the applicable encoding and authorization checks. fn _set_public_key( ref self: ComponentState, new_public_key: Array, ) { let new_owner_guid = poseidon_hash_span(new_public_key.span()); - let stored_len = self.public_key.len(); + let stored_len = self.Falcon512Account_public_key.len(); if stored_len == 0 { for felt in new_public_key { - self.public_key.push(felt); + self.Falcon512Account_public_key.push(felt); } } else { assert(stored_len == new_public_key.len().into(), Errors::INVALID_PUBLIC_KEY); let mut index = 0; for felt in new_public_key { - self.public_key.at(index).write(felt); + self.Falcon512Account_public_key.at(index).write(felt); index += 1; } } @@ -385,10 +404,10 @@ pub(crate) mod Falcon512AccountComponent { /// Reads the stored public key into its 29-felt packed representation. fn read_public_key(self: @ComponentState) -> Array { let mut public_key = array![]; - let len = self.public_key.len(); + let len = self.Falcon512Account_public_key.len(); let mut index = 0; while index != len { - public_key.append(self.public_key.at(index).read()); + public_key.append(self.Falcon512Account_public_key.at(index).read()); index += 1; } public_key diff --git a/packages/account/src/falcon_512/falcon.cairo b/packages/account/src/falcon_512/falcon.cairo index 071456f61..340846c1c 100644 --- a/packages/account/src/falcon_512/falcon.cairo +++ b/packages/account/src/falcon_512/falcon.cairo @@ -3,22 +3,16 @@ //! Falcon-512 signature verification. //! -//! Hint variant: verifies `s1 * h == mul_hint` via two forward NTTs and a pointwise -//! product check (the NTT is a bijection, so the check binds `mul_hint` to exactly -//! `s1 * h mod (q, phi)`), then accepts iff -//! `||msg_point - mul_hint||^2 + ||s1||^2 <= SIG_BOUND_512` over centered -//! representatives. Since `msg_point - s1*h = s0`, this is the Falcon verification -//! equation with the polynomial multiplication delegated to a signer-supplied hint; -//! a bad hint yields `false`. Both transforms use the generated fixed-parameter -//! Falcon-512 path, and the pointwise check tests `s1n*h == hn (mod q)`. +//! The hint variant accepts a signer-supplied product and verifies +//! `mul_hint = s1 * h` in `Z_q[x]/(x^512 + 1)` using two forward NTTs and a pointwise +//! equality check. It then accepts when +//! `||msg_point - mul_hint||^2 + ||s1||^2 <= SIG_BOUND_512` over centered representatives. +//! NTT bijectivity binds the accepted hint to the product determined by `s1` and `h`. //! -//! Direct variant: computes `s1 * h` on-chain as `INTT(NTT(s1) ∘ h_ntt)` — the -//! generated forward transform's reduced pointwise products feed the generic inverse -//! engine with their exact bound. +//! The direct variant derives `s1 * h` on-chain as `INTT(NTT(s1) ∘ h_ntt)`. //! //! The verifier path decodes packed coefficients into canonical `u16` values and keeps -//! that representation through the generated NTT and norm calculation. The public core -//! functions also accept validated felt coefficients for standalone callers. +//! that representation through the generated NTT and norm calculation. use super::ntt::engine::intt; #[cfg(test)] @@ -32,10 +26,10 @@ use super::ntt::falcon512_fast::ntt_falcon512_fast_u16_unchecked; use super::ntt::falcon512_fast::ntt_falcon512_fast_u16_unchecked; use super::zq::{Q32, center_sq, centered_difference_sq}; -/// Maximum allowed `||s0||^2 + ||s1||^2` for FALCON-512 as submitted to NIST. +/// Maximum squared-norm bound for the Falcon-512 submission algorithm. pub const SIG_BOUND_512: u64 = 34034726; -/// The Falcon modulus as a u32 divisor. +/// The Falcon modulus as a `u32` divisor. const Q32_NZ: NonZero = 12289; #[cfg(not(test))] @@ -51,10 +45,10 @@ fn forward_ntt(values: Span) -> Array { ntt_falcon512_fast_u16_unchecked(values) } -// The repository's coverage profile disables inlining. The universal Sierra compiler -// cannot currently lower the generated 512-value straight-line transform in that profile, -// so contract tests use the equivalent generic engine. The generated production path is -// exercised in release mode with the `falcon_fast_tests` feature. +// The workspace dev profile disables inlining, and the universal Sierra compiler cannot lower +// the generated 512-value straight-line transform in that profile. Dev-profile tests use the +// equivalent generic engine; release-profile tests select the generated path with +// `falcon_fast_tests`. #[cfg(test)] #[cfg(not(feature: 'falcon_fast_tests'))] fn forward_ntt(mut values: Span) -> Array { @@ -71,6 +65,7 @@ fn forward_ntt(mut values: Span) -> Array { } #[inline(always)] +#[cfg(test)] fn felts_to_u16(mut values: Span) -> Array { let mut out: Array = array![]; while let Some(value) = values.pop_front() { @@ -94,9 +89,9 @@ fn product_as_felt(a: u16, b: u16) -> felt252 { (a * b).into() } -/// Verify with a signer-supplied product hint. All spans must have length 512 with -/// coefficients in `[0, Q)` — guaranteed by `packing::unpack_512` and -/// `hash_to_point::hash_to_point_512`. +/// Verifies a signer-supplied product hint through the test-facing `felt252` wrapper. All spans +/// must have length 512 with coefficients in `[0, Q)`. +#[cfg(test)] pub fn verify_512_with_hint( s1: Span, h_ntt: Span, mul_hint: Span, msg_point: Span, ) -> bool { @@ -111,7 +106,8 @@ pub fn verify_512_with_hint( verify_512_with_hint_u16(s1.span(), h_ntt.span(), mul_hint.span(), msg_point) } -/// Verify the hint equation over canonical `u16` coefficients. +/// Verifies the hint equation over canonical `u16` coefficients. Every span must contain exactly +/// 512 coefficients in `[0, Q)`. pub(crate) fn verify_512_with_hint_u16( s1: Span, h_ntt: Span, mul_hint: Span, msg_point: Span, ) -> bool { @@ -120,8 +116,8 @@ pub(crate) fn verify_512_with_hint_u16( assert(mul_hint.len() == 512, 'mul_hint must be 512 coeffs'); assert(msg_point.len() == 512, 'msg_point must be 512 coeffs'); - // Both spans came from canonical base-Q unpacking (or the public wrapper's documented - // canonical-coefficient precondition), so the generated unchecked NTT is sound here. + // Production inputs come from canonical base-Q unpacking; the test wrapper documents the same + // coefficient precondition. The generated unchecked NTT is sound for both call paths. let s1_ntt = forward_ntt(s1); let hint_ntt = forward_ntt(mul_hint); @@ -242,11 +238,10 @@ pub(crate) fn verify_512_with_hint_u16( norm <= SIG_BOUND_512 } -/// Verify without a hint (direct variant): compute `s1 * h` on-chain as -/// `INTT(NTT(s1) ∘ h_ntt)` — 1 NTT + 1 INTT because the public key is already stored in -/// the NTT domain — then accept iff `||msg_point - s1*h||^2 + ||s1||^2 <= SIG_BOUND_512`. -/// Same trust surface as the textbook equation (no signer-supplied hint), 29 fewer -/// signature felts than [`verify_512_with_hint`], at the cost of the INTT. +/// Verifies the direct variant by computing `s1 * h` on-chain as +/// `INTT(NTT(s1) ∘ h_ntt)`, then applying the Falcon squared-norm bound. Its signature omits the +/// 29-felt product hint and performs an inverse NTT on-chain. +#[cfg(test)] pub fn verify_512_direct(s1: Span, h_ntt: Span, msg_point: Span) -> bool { assert(s1.len() == 512, 's1 must be 512 coeffs'); assert(h_ntt.len() == 512, 'h_ntt must be 512 coeffs'); @@ -257,14 +252,14 @@ pub fn verify_512_direct(s1: Span, h_ntt: Span, msg_point: Spa verify_512_direct_u16(s1.span(), h_ntt.span(), msg_point) } -/// Verify the direct equation over canonical `u16` coefficients. +/// Verifies the direct equation over canonical `u16` coefficients. pub(crate) fn verify_512_direct_u16(s1: Span, h_ntt: Span, msg_point: Span) -> bool { assert(s1.len() == 512, 's1 must be 512 coeffs'); assert(h_ntt.len() == 512, 'h_ntt must be 512 coeffs'); assert(msg_point.len() == 512, 'msg_point must be 512 coeffs'); let cfg = config(); - // `s1` is canonical by the same verifier/wrapper precondition as the hint path. + // `s1` is canonical under the verifier and test-wrapper preconditions. let s1_ntt = forward_ntt(s1); // Pointwise products of two reduced inputs are below q^2. They feed the inverse @@ -369,7 +364,7 @@ mod tests { out } - /// Negacyclic product in Z_q[x]/(x^512 + 1) (test helper). + /// Negacyclic product in `Z_q[x]/(x^512 + 1)` (test helper). fn mul_zq(f: Span, g: Span) -> Array { let cfg = config(); let f_ntt = ntt(f, @cfg); @@ -409,7 +404,7 @@ mod tests { f } - /// msg_point = s0 + prod coefficient-wise, downcast to the norm side's u16 form. + /// `msg_point = s0 + prod` coefficient-wise, downcast to the norm side's `u16` form. fn add_points(s0: Span, prod: Span) -> Array { let mut out: Array = array![]; let mut s0_iter = to_u16(s0).span(); diff --git a/packages/account/src/falcon_512/falcon_512_shake.cairo b/packages/account/src/falcon_512/falcon_512_shake.cairo deleted file mode 100644 index 358d67f4c..000000000 --- a/packages/account/src/falcon_512/falcon_512_shake.cairo +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/falcon_512_shake.cairo) - -/// # Falcon-512 SHAKE Account -/// -/// Account validating the Falcon-512 verification relation from the FALCON submission selected -/// by NIST, using SHAKE-256 hash-to-point. Its contract-specific 60-felt signature encoding -/// includes a signer-supplied polynomial-product hint that is checked on-chain to reduce -/// execution cost. This account does not claim conformance with FN-DSA/FIPS 206. -#[starknet::contract(account)] -pub mod Falcon512ShakeAccount { - use openzeppelin_introspection::src5::SRC5Component; - use super::super::Falcon512ShakeVerifier; - use super::super::account::Falcon512AccountComponent; - - component!(path: Falcon512AccountComponent, storage: account, event: AccountEvent); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - - #[abi(embed_v0)] - impl AccountMixinImpl = - Falcon512AccountComponent::Falcon512AccountMixinImpl; - - impl AccountInternalImpl = Falcon512AccountComponent::InternalImpl; - - #[storage] - pub struct Storage { - #[substorage(v0)] - pub account: Falcon512AccountComponent::Storage, - #[substorage(v0)] - pub src5: SRC5Component::Storage, - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - AccountEvent: Falcon512AccountComponent::Event, - #[flat] - SRC5Event: SRC5Component::Event, - } - - /// Initializes the account with a canonical 29-felt, NTT-domain public key. - #[constructor] - pub fn constructor(ref self: ContractState, public_key: Array) { - self.account.initializer::(public_key); - } -} diff --git a/packages/account/src/falcon_512/falcon_512_shake_direct.cairo b/packages/account/src/falcon_512/falcon_512_shake_direct.cairo deleted file mode 100644 index 9bf16d80a..000000000 --- a/packages/account/src/falcon_512/falcon_512_shake_direct.cairo +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 -// (account/src/falcon_512/falcon_512_shake_direct.cairo) - -/// # Falcon-512 SHAKE Direct Account -/// -/// Account validating the same Falcon-512 verification relation as `Falcon512ShakeAccount`, from -/// the FALCON submission selected by NIST. It uses SHAKE-256 hash-to-point and a contract-specific -/// 31-felt signature encoding, and recomputes the polynomial product on-chain instead of -/// accepting a signer-supplied hint. This account does not claim conformance with FN-DSA/FIPS 206. -#[starknet::contract(account)] -pub mod Falcon512ShakeDirectAccount { - use openzeppelin_introspection::src5::SRC5Component; - use super::super::Falcon512ShakeDirectVerifier; - use super::super::account::Falcon512AccountComponent; - - component!(path: Falcon512AccountComponent, storage: account, event: AccountEvent); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - - #[abi(embed_v0)] - impl AccountMixinImpl = - Falcon512AccountComponent::Falcon512AccountMixinImpl< - ContractState, Falcon512ShakeDirectVerifier, - >; - - impl AccountInternalImpl = Falcon512AccountComponent::InternalImpl; - - #[storage] - pub struct Storage { - #[substorage(v0)] - pub account: Falcon512AccountComponent::Storage, - #[substorage(v0)] - pub src5: SRC5Component::Storage, - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - AccountEvent: Falcon512AccountComponent::Event, - #[flat] - SRC5Event: SRC5Component::Event, - } - - /// Initializes the account with a canonical 29-felt, NTT-domain public key. - #[constructor] - pub fn constructor(ref self: ContractState, public_key: Array) { - self.account.initializer::(public_key); - } -} diff --git a/packages/account/src/falcon_512/hashing/hash_to_point.cairo b/packages/account/src/falcon_512/hashing/hash_to_point.cairo index 00c482d9f..a772b89e5 100644 --- a/packages/account/src/falcon_512/hashing/hash_to_point.cairo +++ b/packages/account/src/falcon_512/hashing/hash_to_point.cairo @@ -2,21 +2,22 @@ // OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 // (account/src/falcon_512/hashing/hash_to_point.cairo) -//! SHAKE-256 hash-to-point used by FALCON-512 as submitted to NIST. +//! SHAKE-256 hash-to-point for the Falcon-512 submission algorithm. //! //! The function absorbs a 40-byte salt followed by the message hash's 32-byte -//! little-endian representation. It reads the SHAKE-256 output as big-endian u16 words -//! and rejection-samples each word into Z_q until it has 512 coefficients. +//! little-endian representation. It reads the SHAKE-256 output as big-endian `u16` words +//! and rejection-samples each word into `Z_q` until it has 512 coefficients. use super::shake256::keccak_f1600; /// Rejection bound: the largest multiple of q = 12289 below 2^16. const REJECT_BOUND: u32 = 61445; -/// Each salt felt must fit in 20 bytes. +/// Exclusive upper bound for each 20-byte salt limb. const TWO_POW_160: u256 = 0x10000000000000000000000000000000000000000; -/// Hashes `(message_hash, salt)` to 512 coefficients in `[0, 12289)`. +/// Hashes the 40-byte salt encoded by `(salt_a, salt_b)` and `message_hash` to +/// 512 coefficients in `[0, 12289)`. /// /// Returns `None` if either salt felt exceeds 20 bytes. pub(crate) fn hash_to_point_shake_512( @@ -66,7 +67,7 @@ pub(crate) fn hash_to_point_shake_512( Some(coeffs) } -/// Reads one squeezed lane as four big-endian u16 candidate words. +/// Reads one squeezed lane as four big-endian `u16` candidate words. #[inline(always)] fn push_lane_words( lane: u128, two16: NonZero, b256: NonZero, q32: NonZero, ref coeffs: Array, diff --git a/packages/account/src/falcon_512/hashing/shake256.cairo b/packages/account/src/falcon_512/hashing/shake256.cairo index abe83da92..63cebfaf9 100644 --- a/packages/account/src/falcon_512/hashing/shake256.cairo +++ b/packages/account/src/falcon_512/hashing/shake256.cairo @@ -1,38 +1,35 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/hashing/shake256.cairo) -//! SHAKE-256 extendable-output function (FIPS 202), implemented in pure Cairo. +//! Pure-Cairo SHAKE-256 extendable-output function from FIPS 202. //! -//! Written from the FIPS 202 specification (Keccak-f[1600] sponge, rate 1088 bits = -//! 136 bytes, capacity 512, domain-separation `0x1F`, pad10*1). Starknet exposes no -//! SHAKE primitive and its `keccak` syscall bakes in keccak256's `0x01` padding and a -//! fixed 256-bit output, so neither is reusable here; the permutation is implemented -//! directly. A future `keccak_f1600` syscall (SNIP-32) would replace [`keccak_f1600`] -//! and collapse the cost. +//! This module implements the Keccak-f[1600] sponge with a 1088-bit rate, 512-bit capacity, +//! `0x1F` domain separation, and pad10*1 padding. Starknet's `keccak` syscall implements +//! Keccak-256 with `0x01` padding and a fixed 256-bit output, so this module supplies the +//! permutation required by SHAKE-256 directly. //! //! State: 25 lanes of 64 bits, `lane[x + 5y]`, bytes mapped little-endian per lane. //! //! The permutation dominates on-chain cost, so it is written flat: -//! - each round is a straight-line expression over 25 lane locals — no round-local -//! arrays, no index arithmetic, no table lookups; θ is fused into the ρ+π terms and -//! ι into the first χ lane; -//! - lanes are carried as `u128` — the bitwise builtin's native operand width — so -//! XOR/AND need no per-operation downcast. The `< 2^64` lane invariant is +//! - each round is a straight-line expression over 25 lane locals; θ is fused into the ρ+π +//! terms and ι into the first χ lane; +//! - lanes are carried as `u128`, the bitwise builtin's native operand width, so XOR/AND operate +//! directly on lane values. The `< 2^64` lane invariant is //! maintained structurally: XOR/AND of 64-bit values is 64-bit, complements are -//! `MASK64 - b` (exact on 64-bit values, no builtin), and rotations reassemble -//! disjoint bit ranges below bit 64; -//! - rotations are division-free: every rotation amount is baked in as its power of -//! two, so a left-rotate is one felt252 multiply and one u128 `div_rem` by 2^64 — -//! the wrapped high bits and the shifted low bits occupy disjoint positions, so +//! `MASK64 - b`, and rotations reassemble disjoint bit ranges below bit 64; +//! - rotations use baked-in powers of two, so a left-rotate is one `felt252` multiply and one +//! `u128` division by `2^64` via +//! `div_rem`. The wrapped high bits and the shifted low bits occupy disjoint positions, so //! their sum is the rotation. //! -//! [`keccak_f1600`] is shared with the hash-to-point squeezer -//! (`hash_to_point::hash_to_point_shake_512`), which drives the sponge lazily and -//! reads candidates straight from the rate lanes; the byte-oriented [`shake256`] here -//! is the generic XOF, pinned to the FIPS 202 known-answer vectors below. +//! `hash_to_point::hash_to_point_shake_512` uses [`keccak_f1600`] directly to consume candidates +//! from the sponge's rate lanes. A test-only byte-oriented XOF checks the permutation and sponge +//! construction against FIPS 202 known-answer vectors. const MASK64: u128 = 0xffffffffffffffff; +#[cfg(test)] const RATE_LANES: u32 = 17; // 136 bytes / 8 +#[cfg(test)] const RATE_BYTES: u32 = 136; /// Keccak-f[1600] round constants (FIPS 202, Table 1). @@ -45,7 +42,7 @@ const ROUND_CONSTANTS: [u128; 24] = [ 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008, ]; -/// Rotate a 64-bit lane left, given `pow = 2^r` with 0 < r < 64. The widened felt +/// Rotates a 64-bit lane left, given `pow = 2^r` with `0 < r < 64`. The widened felt /// product `x * 2^r < 2^127` splits at bit 64 into the bits that stay (`lo`) and the /// bits that wrap to the bottom (`hi`); they occupy disjoint positions, so `lo + hi` /// is the rotation. @@ -93,7 +90,7 @@ pub(crate) fn keccak_f1600(state: [u128; 25]) -> [u128; 25] { ] = s; - // theta: column parities and the per-column mixing term d[x] = c[x-1] ^ rotl(c[x+1], 1). + // θ: column parities and the per-column mixing term d[x] = c[x-1] ^ rotl(c[x+1], 1). let c0 = s00 ^ s05 ^ s10 ^ s15 ^ s20; let c1 = s01 ^ s06 ^ s11 ^ s16 ^ s21; let c2 = s02 ^ s07 ^ s12 ^ s17 ^ s22; @@ -105,7 +102,7 @@ pub(crate) fn keccak_f1600(state: [u128; 25]) -> [u128; 25] { let d3 = c2 ^ rotl(c4, 0x2, two64); let d4 = c3 ^ rotl(c0, 0x2, two64); - // theta (fused) + rho + pi, straight into destination order. + // θ (fused) + ρ + π, straight into destination order. let b00 = s00 ^ d0; let b01 = rotl(s06 ^ d1, 0x100000000000, two64); let b02 = rotl(s12 ^ d2, 0x80000000000, two64); @@ -132,7 +129,7 @@ pub(crate) fn keccak_f1600(state: [u128; 25]) -> [u128; 25] { let b23 = rotl(s15 ^ d0, 0x20000000000, two64); let b24 = rotl(s21 ^ d1, 0x4, two64); - // chi (complement as MASK64 - b), with iota folded into lane 0. + // χ (complement as MASK64 - b), with ι folded into lane 0. s = [ b00 ^ ((MASK64 - b01) & b02) ^ rc, b01 ^ ((MASK64 - b02) & b03), @@ -153,7 +150,8 @@ pub(crate) fn keccak_f1600(state: [u128; 25]) -> [u128; 25] { s } -/// SHAKE-256: absorb `input`, squeeze `out_bytes` bytes. +/// Absorbs `input` with SHAKE-256 and squeezes `out_bytes` bytes. +#[cfg(test)] pub(crate) fn shake256(input: Array, out_bytes: u32) -> Array { // Pad (pad10*1 with SHAKE domain 0x1F) to a multiple of the rate. let mut padded = input; @@ -234,7 +232,8 @@ pub(crate) fn shake256(input: Array, out_bytes: u32) -> Array { out } -/// Append up to 8 little-endian bytes of the lane `v` (< 2^64), stopping at `out_bytes`. +/// Appends up to 8 little-endian bytes of lane `v` (`v < 2^64`), stopping at `out_bytes`. +#[cfg(test)] fn emit_lane_le(v: u128, ref out: Array, out_bytes: u32) { let mut rem: u64 = v.try_into().unwrap(); let mut k = 0; @@ -245,7 +244,8 @@ fn emit_lane_le(v: u128, ref out: Array, out_bytes: u32) { } } -/// Rebuild `bytes` with `bytes[idx] |= 0x80` (that byte is < 0x80 here, so `+ 0x80`). +/// Rebuilds `bytes` with `bytes[idx] |= 0x80` (that byte is below `0x80`, so `+ 0x80`). +#[cfg(test)] fn pad_ored_msb(bytes: Array, idx: u32) -> Array { let mut out: Array = array![]; let span = bytes.span(); @@ -262,6 +262,7 @@ fn pad_ored_msb(bytes: Array, idx: u32) -> Array { } /// Little-endian 64-bit lane from 8 bytes at `off`. +#[cfg(test)] fn load_lane_le(bytes: @Array, off: u32) -> u128 { let span = bytes.span(); let mut acc: u128 = 0; diff --git a/packages/account/src/falcon_512/ntt/bitrev.cairo b/packages/account/src/falcon_512/ntt/bitrev.cairo index 4ff54a7e5..685ebcd84 100644 --- a/packages/account/src/falcon_512/ntt/bitrev.cairo +++ b/packages/account/src/falcon_512/ntt/bitrev.cairo @@ -1,10 +1,10 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/ntt/bitrev.cairo) -// Generated from the Falcon-512 NTT parameters — do not edit by hand. -// Bit-reversal permutation for n = 512 (self-inverse). The iterative -// engine permutes inputs into recursion-leaf order before its merge -// levels, and INTT outputs back to natural order. +// Generated by scripts/falcon_512/generate_ntt.py; do not edit by hand. +// Self-inverse bit-reversal permutation for n = 512, used to map between +// natural coefficient order and the recursive transform's leaf order. +// Regenerate with: python3 scripts/falcon_512/generate_ntt.py --write //! Bit-reversal permutation table (generated). diff --git a/packages/account/src/falcon_512/ntt/engine.cairo b/packages/account/src/falcon_512/ntt/engine.cairo index 0cbb222c7..8a12b0426 100644 --- a/packages/account/src/falcon_512/ntt/engine.cairo +++ b/packages/account/src/falcon_512/ntt/engine.cairo @@ -1,12 +1,12 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/ntt/engine.cairo) -//! Iterative NTT engine with felt252 lazy reduction. +//! Iterative NTT engine with `felt252` lazy reduction. //! //! # Algorithm //! -//! This computes the recursive split/merge NTT of tprest/falcon.py, reformulated -//! iteratively for the Falcon-512 verifier: +//! This engine computes the recursive split/merge transform used by the Falcon-512 verifier, +//! reformulated iteratively: //! //! - **Forward** ([`ntt`]): permute the input into recursion-leaf order (bit reversal), //! then run merge levels bottom-up. The level merging blocks into size `2h` reads two @@ -22,50 +22,42 @@ //! //! Generic levels with `h >= 8` run 8-way unrolled over `multi_pop_front` chunks. //! -//! # Lazy reduction (the Starknet optimization) +//! # Lazy modular reduction //! -//! Coefficients are carried as raw `felt252` integers WITHOUT per-operation modular -//! reduction: a butterfly is one field multiplication and a few field additions -//! (~1 step each), instead of the mul + divmod + range-check chain of eager `% q` -//! arithmetic. Subtractions are made non-negative by adding `off = bound · q` — a -//! multiple of q, so residues are unchanged. +//! Coefficients remain non-negative `felt252` integer representatives between scheduled modular +//! reduction passes. Subtractions add `off = bound · q`, a multiple of `q` that preserves each +//! residue. //! -//! Soundness of the delay: the engine tracks an exclusive upper `bound` on the -//! coefficient integers (exactly, as a felt) and its bit size (conservatively). -//! Per forward level `bound *= q+1`; per inverse level `bound *= 2q`. Before a level -//! that could push values to 2^126 or beyond, and once at the end, a reduction pass -//! maps every coefficient to `[0, q)` via u128 divmod. Since all intermediates stay -//! below 2^126 < P/2^125, felt252 arithmetic never wraps and every reduction input -//! fits u128. For q = 12289 this schedule costs exactly TWO passes per 512-point -//! transform. [`ntt_lazy`] additionally exposes the transform WITHOUT its final -//! reduction pass, returning the tracked `(bits, bound)` alongside the unreduced -//! values, so callers whose follow-up arithmetic tolerates the bound (a divisibility -//! check, a lazy-product INTT) can skip that pass entirely. -//! The generated tables and forward transform are tested against the recursive -//! reference on random and adversarial inputs. +//! The engine tracks an exact exclusive `bound` on coefficient integers and a conservative bit +//! bound. Per forward level `bound *= q + 1`; per inverse level `bound *= 2q`. Before a level that +//! could push values to `2^126` or beyond, and once at the end, a reduction pass maps every +//! coefficient to `[0, q)` via `u128` divmod. All intermediates remain below `2^126`, well below +//! the Starknet field prime, and every reduction input fits `u128`. For the reduced and +//! pointwise-product bounds used by Falcon-512, a 512-point transform performs one intermediate +//! reduction pass and one final reduction pass. use core::traits::DivRem; -/// Everything a parameter set provides to the engine. Spans make configs cheap to -/// build and copy; see `falcon512::config()` for the canonical instance. +/// Configuration for an NTT parameter set. `falcon512::config` returns the canonical Falcon-512 +/// configuration. #[derive(Copy, Drop)] pub struct NttConfig { /// Transform size (a power of two). pub n: u32, /// log2(n): number of merge/split levels. pub levels: u32, - /// The modulus, as a u128 divisor for reduction passes. + /// The modulus, as a `u128` divisor for reduction passes. pub q_nz: NonZero, /// The modulus as a felt (offset construction). pub q_felt: felt252, /// 2^-1 mod q (even branch of the inverse split). pub i2_felt: felt252, - /// Bit size of a reduced value: values in [0, q) fit `qbits` bits. + /// Bit size of a reduced value: values in `[0, q)` fit `qbits` bits. pub qbits: u32, - /// Per-forward-level bound growth, exact and in bits: bound *= q+1. + /// Per-forward-level bound growth, exact and in bits: `bound *= q + 1`. pub fwd_growth_felt: felt252, pub fwd_growth_bits: u32, - /// Per-inverse-level bound growth, exact and in bits: bound *= 2q. + /// Per-inverse-level bound growth, exact and in bits: `bound *= 2q`. pub inv_growth_felt: felt252, pub inv_growth_bits: u32, /// Leaf permutation (self-inverse bit reversal), entries < n. @@ -76,12 +68,12 @@ pub struct NttConfig { pub split_roots_scaled: Span>, } -/// Intermediates must stay below 2^126: felt252 arithmetic cannot wrap (2^126 ≪ P) -/// and reduction inputs fit u128. +/// Intermediates must stay below `2^126`, which is below the Starknet field prime, and reduction +/// inputs must fit `u128`. const SAFE_BITS: u32 = 126; -/// Reduce every coefficient to `[0, q)`. Inputs must be < 2^128 (guaranteed by the -/// engine's bound schedule). 8-way unrolled. +/// Reduces every coefficient to `[0, q)`. Inputs must be below `2^128`, as guaranteed by the +/// engine's bound schedule. The loop is 8-way unrolled. fn reduce_pass(mut vals: Span, q_nz: NonZero) -> Array { let mut out: Array = array![]; while let Some(chunk) = vals.multi_pop_front::<8>() { @@ -111,13 +103,6 @@ fn reduce_pass(mut vals: Span, q_nz: NonZero) -> Array { out } -/// Reduce a single lazily-computed value (< 2^128) to `[0, q)`. -pub fn reduce_felt(v: felt252, q_nz: NonZero) -> felt252 { - let vu: u128 = v.try_into().unwrap(); - let (_, rem) = DivRem::div_rem(vu, q_nz); - rem.into() -} - /// One generic merge level: blocks of output size `2h`; `f0`/`f1` are each block's /// half-spans, outputs interleave `f0[i] ± w[i]·f1[i]` (offset keeps them non-negative). #[inline] @@ -182,25 +167,15 @@ fn merge_level( out } -/// Forward NTT. `f` must hold `cfg.n` coefficients in `[0, q)`. Returns the transform -/// in the parameter set's evaluation order, reduced to `[0, q)`. +/// Computes the forward NTT. `f` must hold `cfg.n` coefficients in `[0, q)`. Returns the +/// transform in the parameter set's evaluation order, reduced to `[0, q)`. pub fn ntt(f: Span, cfg: @NttConfig) -> Array { let (out, _, _) = ntt_core(f, cfg); reduce_pass(out.span(), *cfg.q_nz) } -/// Forward NTT without the final reduction pass: the same transform as [`ntt`], with -/// outputs returned as unreduced integers congruent to the reduced transform mod q. -/// Returns `(values, bits, bound)` where `bound` is the exact exclusive upper bound on -/// the output integers under the engine's reduction schedule and `bits` its -/// conservative bit size. Callers fold the bound into their own arithmetic (e.g. a -/// divisibility check or a lazy-product INTT) instead of paying a full reduction pass. -pub fn ntt_lazy(f: Span, cfg: @NttConfig) -> (Array, u32, felt252) { - ntt_core(f, cfg) -} - -/// The merge levels shared by [`ntt`] and [`ntt_lazy`], inlined into both so neither -/// entry point pays an extra call boundary. +/// Executes the forward merge levels and returns the unreduced coefficients, their bit bound, and +/// their exclusive value bound. #[inline(always)] fn ntt_core(f: Span, cfg: @NttConfig) -> (Array, u32, felt252) { let n = *cfg.n; @@ -309,7 +284,7 @@ fn split_level( out } -/// Inverse NTT. `f` must hold `cfg.n` values below `2^input_bits` (as integers), with +/// Computes the inverse NTT. `f` must hold `cfg.n` values below `2^input_bits` (as integers), with /// `input_bound` their exact exclusive bound: pass `(cfg.qbits, cfg.q_felt)` for /// reduced inputs, or the product bound for unreduced pointwise products (the /// lazy-product path: `intt(a_ntt ∘ b_ntt)` without reducing the products first). diff --git a/packages/account/src/falcon_512/ntt/falcon512.cairo b/packages/account/src/falcon_512/ntt/falcon512.cairo index 165e4f4f1..87d59b6e7 100644 --- a/packages/account/src/falcon_512/ntt/falcon512.cairo +++ b/packages/account/src/falcon_512/ntt/falcon512.cairo @@ -1,8 +1,9 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/ntt/falcon512.cairo) -//! Falcon parameter set for the NTT engine: q = 12289, negacyclic (x^n + 1), using -//! tprest/falcon.py evaluation order for interoperability with the reference signer. +//! Falcon parameter set for the NTT engine: `q = 12289` and negacyclic ring `(x^n + 1)`. +//! The generated root tables use the evaluation order expected by the accounts' packed +//! NTT-domain public keys. use super::bitrev::bitrev_512; use super::engine::NttConfig; @@ -19,7 +20,8 @@ pub const I2_FELT: felt252 = 6145; pub const REDUCED_BITS: u32 = 14; /// Unreduced pointwise products of two reduced values are < q^2 < 2^28. pub const PRODUCT_BITS: u32 = 28; -/// q^2 as a felt: the exact bound matching [`PRODUCT_BITS`]. +/// Exclusive bound for unreduced products of two reduced values; each product fits +/// [`PRODUCT_BITS`] bits. pub const PRODUCT_BOUND_FELT: felt252 = 151019521; /// Engine configuration for the production size n = 512 (table-driven permutation). @@ -27,9 +29,9 @@ pub fn config() -> NttConfig { config_with_perm(512, 9, bitrev_512()) } -/// Engine configuration for any supported degree (4..512), computing the bit-reversal -/// permutation programmatically. Meant for tests and auxiliary tooling; production -/// callers use [`config`], which reads the pinned table instead. +/// Engine configuration for a supported power-of-two degree from 4 through 512, computing the +/// bit-reversal permutation programmatically. `levels` must equal `log2(n)`. Tests and auxiliary +/// tooling use this function; production callers use [`config`], which reads the pinned table. pub fn config_for_degree(n: u32, levels: u32) -> NttConfig { let mut perm: Array = array![]; let mut i: u32 = 0; diff --git a/packages/account/src/falcon_512/ntt/falcon512_fast.cairo b/packages/account/src/falcon_512/ntt/falcon512_fast.cairo index ab5a96646..ec0fcc16c 100644 --- a/packages/account/src/falcon_512/ntt/falcon512_fast.cairo +++ b/packages/account/src/falcon_512/ntt/falcon512_fast.cairo @@ -1,19 +1,20 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/ntt/falcon512_fast.cairo) -// Generated from the Falcon-512 NTT parameters — do not edit by hand. +// Generated by scripts/falcon_512/generate_ntt.py; do not edit by hand. // Fully unrolled Falcon-512 forward NTT for n = 512 and q = 12289. -// The operation graph and its bounds are derived from roots.cairo; each -// output is reduced once after the straight-line felt252 arithmetic. +// The generator checks exact integer bounds and reduces each output once. +// Regenerate with: python3 scripts/falcon_512/generate_ntt.py --write //! Generated Falcon-512 forward NTT. //! -//! Inputs are 512 coefficients in `[0, 12289)`. The unchecked public entry points -//! rely on callers to enforce that precondition; the fixed operation graph computes -//! the Falcon evaluation order and returns 512 reduced coefficients. +//! Inputs are 512 coefficients in `[0, 12289)`. The unchecked entry points rely +//! on callers to enforce that precondition. Outputs are reduced to the same range. -use corelib_imports::bounded_int::{BoundedInt, DivRemHelper, UnitInt, bounded_int_div_rem, upcast}; -use corelib_imports::integer::{U128sFromFelt252Result, u128s_from_felt252}; +use openzeppelin_corelib_imports::bounded_int::{ + BoundedInt, DivRemHelper, UnitInt, bounded_int_div_rem, upcast, +}; +use openzeppelin_corelib_imports::integer::{U128sFromFelt252Result, u128s_from_felt252}; type Falcon512Zq = BoundedInt<0, 12288>; type Falcon512Q = UnitInt<12289>; @@ -9516,12 +9517,8 @@ fn ntt_falcon512_fast_inner( ) } -/// Compute the Falcon-512 forward NTT for 512 reduced coefficients without -/// validating each coefficient. -/// -/// Inputs must be in `[0, 12289)`; violating that precondition may return an -/// invalid transform. Outputs are in the Falcon/falcon.py evaluation order and -/// reduced to the same range. +/// Test-only felt wrapper for the generated Falcon-512 forward NTT. +#[cfg(test)] pub fn ntt_falcon512_fast_unchecked(mut f: Span) -> Array { assert(f.len() == 512, 'fast NTT: bad length'); let boxed = f.multi_pop_front::<512>().unwrap(); @@ -11156,12 +11153,10 @@ pub fn ntt_falcon512_fast_unchecked(mut f: Span) -> Array { ] } -/// Compute the Falcon-512 forward NTT from canonical `u16` coefficients without -/// validating each coefficient. +/// Computes the Falcon-512 forward NTT from canonical `u16` coefficients. /// /// Inputs must be in `[0, 12289)`; violating that precondition may return an -/// invalid transform. Outputs are in the Falcon/falcon.py evaluation order and -/// reduced to the same range. +/// invalid transform. Outputs are reduced to the same range. pub fn ntt_falcon512_fast_u16_unchecked(mut f: Span) -> Array { assert(f.len() == 512, 'fast NTT: bad length'); let boxed = f.multi_pop_front::<512>().unwrap(); diff --git a/packages/account/src/falcon_512/ntt/roots_felt.cairo b/packages/account/src/falcon_512/ntt/roots_felt.cairo index e3fb9bc54..6c685cf2c 100644 --- a/packages/account/src/falcon_512/ntt/roots_felt.cairo +++ b/packages/account/src/falcon_512/ntt/roots_felt.cairo @@ -1,10 +1,10 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/ntt/roots_felt.cairo) -// Generated from the Falcon-512 NTT parameters — do not edit by hand. -// Forward merge-root tables as felt252 constants, value-identical to the -// u16 tables in roots.cairo; the engine multiplies roots into felt252 -// butterflies, so the constants are stored ready to consume. +// Generated by scripts/falcon_512/generate_ntt.py; do not edit by hand. +// Forward merge roots derived recursively from q = 12289 and sqrt(-1) = 1479, +// choosing the smaller representative of each modular square-root pair. +// Regenerate with: python3 scripts/falcon_512/generate_ntt.py --write //! Forward NTT root tables as felts (generated). @@ -54,8 +54,7 @@ const phi1024_roots_zq_felt: [felt252; 256] = [ 5868, 5735, 2655, 3315, 426, 4754, 1858, 1975, 3757, 2925, 347, ]; -/// Forward merge roots for the given degree, as felts (the values of -/// `roots::get_even_roots`). +/// Forward merge roots for a power-of-two transform degree from 2 through 512. pub fn get_even_roots_felt(degree: u32) -> Span { if degree == 2 { phi4_roots_zq_felt.span() diff --git a/packages/account/src/falcon_512/ntt/roots_scaled.cairo b/packages/account/src/falcon_512/ntt/roots_scaled.cairo index 34e92e89d..cf4fa84c1 100644 --- a/packages/account/src/falcon_512/ntt/roots_scaled.cairo +++ b/packages/account/src/falcon_512/ntt/roots_scaled.cairo @@ -1,10 +1,10 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/ntt/roots_scaled.cairo) -// Generated from the Falcon-512 NTT parameters — do not edit by hand. -// Inverse merge-root tables prescaled by I2 = 2^-1 mod q -// (tinv[i] = I2 * root_inv[i] mod q), derived from roots.cairo; the -// split levels of the INTT consume them as single felt252 multipliers. +// Generated by scripts/falcon_512/generate_ntt.py; do not edit by hand. +// Inverse roots derived from the deterministic forward roots and prescaled by +// I2 = 2^-1 mod q for the inverse transform's split butterflies. +// Regenerate with: python3 scripts/falcon_512/generate_ntt.py --write //! I2-scaled inverse NTT root tables (generated). @@ -59,8 +59,7 @@ const phi1024_roots_zq_inv_scaled: [felt252; 256] = [ 11962, 4362, 12261, 7744, 8909, 9686, ]; -/// I2-prescaled inverse roots for the split level of the given degree -/// (the output size of the merge it inverts). +/// I2-prescaled inverse roots for a power-of-two transform degree from 2 through 512. pub fn get_scaled_inv_roots(degree: u32) -> Span { if degree == 2 { phi4_roots_zq_inv_scaled.span() diff --git a/packages/account/src/falcon_512/packing.cairo b/packages/account/src/falcon_512/packing.cairo index b02f56037..2f83218bd 100644 --- a/packages/account/src/falcon_512/packing.cairo +++ b/packages/account/src/falcon_512/packing.cairo @@ -1,29 +1,31 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/packing.cairo) -//! Base-Q packing of 512 Z_q coefficients into 29 felt252 slots. +//! Base-`Q` packing of 512 `Z_q` coefficients into 29 `felt252` slots. //! -//! Each full slot carries two u128 halves; each half Horner-packs 9 coefficients in base -//! Q = 12289 (Q^9 < 2^128): `half = c0 + Q*(c1 + Q*(c2 + ...))`. 28 full slots hold 18 -//! coefficients each; the last slot holds the remaining 8 in its low half. +//! Each full slot carries two `u128` halves; each half Horner-packs 9 coefficients in base +//! `Q = 12289` (`Q^9 < 2^128`): `half = c0 + Q*(c1 + Q*(c2 + ...))`. 28 full slots +//! hold 18 coefficients each; the last slot holds the remaining 8 in its low half. //! -//! Unpacking is validating: every extracted base-Q digit is < Q by construction, and the -//! residual quotient after the last digit must be zero (each half < Q^9, the last slot -//! < Q^8). The accepted felt vectors are therefore in bijection with coefficient vectors -//! in [0, Q)^512 — no coefficient can be smuggled in a non-canonical encoding. +//! Unpacking accepts exactly canonical encodings: each full half is downcast to `[0, Q^9)`, the +//! final low half to `[0, Q^8)`, and the final high half must be zero. The accepted felt vectors +//! are therefore in bijection with coefficient vectors in `[0, Q)^512`. -use corelib_imports::bounded_int::{ +use openzeppelin_corelib_imports::bounded_int::{ BoundedInt, DivRemHelper, UnitInt, bounded_int_div_rem, downcast, upcast, }; +#[cfg(test)] use super::zq::Q; -/// Coefficients per full felt252 slot (two u128 halves of 9). +/// Coefficients per full `felt252` slot (two `u128` halves of 9). pub const VALS_PER_FELT: u32 = 18; -/// felt252 slots for 512 coefficients: 28 full slots + 1 slot with 8. +/// `felt252` slots for 512 coefficients: 28 full slots + 1 slot with 8. pub const PACKED_SLOTS: u32 = 29; /// Coefficients in the last slot: 512 - 28*18. +#[cfg(test)] const LAST_SLOT_VALS: u32 = 8; +#[cfg(test)] const TWO_POW_128: felt252 = 0x100000000000000000000000000000000; type PackingZq = BoundedInt<0, 12288>; @@ -79,9 +81,10 @@ impl PackingDivRemAcc8Impl of DivRemHelper { type RemT = PackingZq; } -/// Unpack 29 packed felts into 512 coefficients in [0, Q), as felts (each is a +/// Unpacks 29 packed felts into 512 coefficients in `[0, Q)`, as felts (each is a /// base-Q digit, so the range holds by construction). /// Returns `None` on wrong length or any non-canonical slot encoding. +#[cfg(test)] pub fn unpack_512(packed: Span) -> Option> { let coeffs_u16 = match unpack_512_u16(packed) { Some(v) => v, @@ -95,7 +98,7 @@ pub fn unpack_512(packed: Span) -> Option> { Some(coeffs) } -/// Unpack the canonical 29-slot encoding directly into the verifier's `u16` form. +/// Unpacks the canonical 29-slot encoding directly into the verifier's `u16` form. /// Returns `None` on wrong length or any non-canonical slot encoding. pub(crate) fn unpack_512_u16(packed: Span) -> Option> { if packed.len() != PACKED_SLOTS { @@ -128,7 +131,7 @@ fn unpack_full_slot_u16(felt: felt252, ref coeffs: Array) -> bool { unpack_half9_u16(value.low, ref coeffs) && unpack_half9_u16(value.high, ref coeffs) } -/// Extract nine base-Q digits from a u128 half. Values below Q^9 are canonical; +/// Extracts nine base-Q digits from a `u128` half. Values below `Q^9` are canonical; /// after eight divisions the remaining quotient is the ninth digit. #[inline(always)] fn unpack_half9_u16(value: u128, ref coeffs: Array) -> bool { @@ -156,7 +159,7 @@ fn unpack_half9_u16(value: u128, ref coeffs: Array) -> bool { true } -/// Extract the final slot's eight digits after checking the value is below Q^8. +/// Extracts the final slot's eight digits after checking the value is below `Q^8`. #[inline(always)] fn unpack_half8_u16(value: u128, ref coeffs: Array) -> bool { let value: Acc7 = match downcast(value) { @@ -181,8 +184,9 @@ fn unpack_half8_u16(value: u128, ref coeffs: Array) -> bool { true } -/// Pack 512 coefficients (each < Q) into 29 felts. Inverse of [`unpack_512`]; -/// used by tests and off-chain tooling mirrors. Panics on bad length or coefficient. +/// Packs 512 coefficients into 29 felts. Inverse of [`unpack_512`]; used by tests. +/// Panics unless `coeffs` contains exactly 512 coefficients in `[0, Q)`. +#[cfg(test)] pub fn pack_512(coeffs: Span) -> Array { assert(coeffs.len() == 512, 'pack: need 512 coeffs'); let mut packed: Array = array![]; @@ -197,7 +201,8 @@ pub fn pack_512(coeffs: Span) -> Array { packed } -/// Horner-encode up to 9 coefficients into a u128: c0 + Q*(c1 + Q*(c2 + ...)). +/// Horner-encodes up to 9 coefficients into a `u128`: c0 + Q*(c1 + Q*(c2 + ...)). +#[cfg(test)] fn pack_half(coeffs: Span) -> u128 { let mut acc: u128 = 0; let mut i = coeffs.len(); diff --git a/packages/account/src/falcon_512/verifier.cairo b/packages/account/src/falcon_512/verifier.cairo new file mode 100644 index 000000000..4b4035742 --- /dev/null +++ b/packages/account/src/falcon_512/verifier.cairo @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/verifier.cairo) + +/// Verification strategy used by `Falcon512AccountComponent`. +pub trait Falcon512SignatureVerifier { + /// Returns whether `signature` authenticates `message_hash` under `public_key`. + fn verify(message_hash: felt252, public_key: Span, signature: Span) -> bool; + + /// Returns whether the packed public key has the required canonical encoding. + fn is_valid_public_key(public_key: Span) -> bool; +} diff --git a/packages/account/src/falcon_512/verifier_impls.cairo b/packages/account/src/falcon_512/verifier_impls.cairo new file mode 100644 index 000000000..bf3a47fe3 --- /dev/null +++ b/packages/account/src/falcon_512/verifier_impls.cairo @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/verifier_impls.cairo) + +use super::hashing::hash_to_point::hash_to_point_shake_512; +use super::verifier::Falcon512SignatureVerifier; +use super::{falcon, packing}; + +/// Number of felts in a packed Falcon-512 public key. +pub(crate) const PUBLIC_KEY_FELTS: u32 = 29; + +/// Number of felts in a Falcon-512 signature carrying a product hint. +pub(crate) const SIGNATURE_FELTS: u32 = 60; + +/// Number of felts in a direct Falcon-512 signature. +pub(crate) const DIRECT_SIGNATURE_FELTS: u32 = 31; + +/// Verifier for the 60-felt SHAKE-256 signature carrying a polynomial-product hint. +pub impl Falcon512ShakeVerifier of Falcon512SignatureVerifier { + fn verify(message_hash: felt252, public_key: Span, signature: Span) -> bool { + if public_key.len() != PUBLIC_KEY_FELTS || signature.len() != SIGNATURE_FELTS { + return false; + } + let h_ntt = match packing::unpack_512_u16(public_key) { + Some(value) => value, + None => { return false; }, + }; + let s1 = match packing::unpack_512_u16(signature.slice(0, 29)) { + Some(value) => value, + None => { return false; }, + }; + let salt_a = *signature.at(29); + let salt_b = *signature.at(30); + let mul_hint = match packing::unpack_512_u16(signature.slice(31, 29)) { + Some(value) => value, + None => { return false; }, + }; + let message_point = match hash_to_point_shake_512(message_hash, salt_a, salt_b) { + Some(value) => value, + None => { return false; }, + }; + falcon::verify_512_with_hint_u16( + s1.span(), h_ntt.span(), mul_hint.span(), message_point.span(), + ) + } + + fn is_valid_public_key(public_key: Span) -> bool { + if public_key.len() != PUBLIC_KEY_FELTS { + return false; + } + match packing::unpack_512_u16(public_key) { + Some(_) => true, + None => false, + } + } +} + +/// Verifier for the 31-felt SHAKE-256 signature that recomputes the product on-chain. +pub impl Falcon512ShakeDirectVerifier of Falcon512SignatureVerifier { + fn verify(message_hash: felt252, public_key: Span, signature: Span) -> bool { + if public_key.len() != PUBLIC_KEY_FELTS || signature.len() != DIRECT_SIGNATURE_FELTS { + return false; + } + let h_ntt = match packing::unpack_512_u16(public_key) { + Some(value) => value, + None => { return false; }, + }; + let s1 = match packing::unpack_512_u16(signature.slice(0, 29)) { + Some(value) => value, + None => { return false; }, + }; + let salt_a = *signature.at(29); + let salt_b = *signature.at(30); + let message_point = match hash_to_point_shake_512(message_hash, salt_a, salt_b) { + Some(value) => value, + None => { return false; }, + }; + falcon::verify_512_direct_u16(s1.span(), h_ntt.span(), message_point.span()) + } + + fn is_valid_public_key(public_key: Span) -> bool { + Falcon512ShakeVerifier::is_valid_public_key(public_key) + } +} diff --git a/packages/account/src/falcon_512/zq.cairo b/packages/account/src/falcon_512/zq.cairo index 67f5a7940..e0901ab48 100644 --- a/packages/account/src/falcon_512/zq.cairo +++ b/packages/account/src/falcon_512/zq.cairo @@ -1,20 +1,21 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (account/src/falcon_512/zq.cairo) -//! Operations on the base ring Z_q (q = 12289). +//! Arithmetic helpers for `Z_q`, where `q = 12289`. //! -//! Coefficients are `u16` values in `[0, Q)` by construction — every function returns a -//! `% Q` result — with u32/u64 intermediates sized so the arithmetic cannot overflow. +//! Modular operands are canonical `u16` values in `[0, Q)`. The modular helpers return canonical +//! residues, while the norm helpers return squared centered representatives. All intermediate +//! values fit their declared types. -/// The Falcon modulus q = 12289 = 12·1024 + 1. +/// The Falcon modulus `q = 12289 = 12 · 1024 + 1`. pub const Q: u16 = 12289; pub const Q32: u32 = 12289; -pub const Q64: u64 = 12289; -/// Largest centered-low value: (Q-1)/2. Coefficients above this represent negatives. +/// Largest centered-low value: `(Q - 1) / 2`. Coefficients above this represent negatives. pub const Q_HALF: u16 = 6144; -/// Add two values modulo Q. +/// Adds two values modulo `Q`. +#[cfg(test)] #[inline(always)] pub fn add_mod(a: u16, b: u16) -> u16 { // a, b < Q so a + b <= 24576 < 2^16: the checked u16 add never overflows, and a @@ -27,7 +28,8 @@ pub fn add_mod(a: u16, b: u16) -> u16 { } } -/// Subtract two values modulo Q, via a + Q - b and one conditional subtraction. +/// Subtracts two values modulo `Q` via `a + Q - b` and one conditional subtraction. +#[cfg(test)] #[inline(always)] pub fn sub_mod(a: u16, b: u16) -> u16 { // a < Q so a + Q <= 24577 < 2^16; a + Q - b is in [1, 2Q-1]. @@ -39,7 +41,8 @@ pub fn sub_mod(a: u16, b: u16) -> u16 { } } -/// Multiply two values modulo Q. +/// Multiplies two values modulo `Q`. +#[cfg(test)] #[inline(always)] pub fn mul_mod(a: u16, b: u16) -> u16 { let a: u32 = a.into(); @@ -49,7 +52,7 @@ pub fn mul_mod(a: u16, b: u16) -> u16 { res.try_into().unwrap() } -/// Squared centered representative of a coefficient, as felt252: +/// Squared centered representative of a coefficient as `felt252`: /// x ∈ [0, 6144] → x²; x ∈ [6145, 12288] → (Q - x)². #[inline(always)] pub fn center_sq(coeff: u16) -> felt252 { @@ -133,16 +136,4 @@ mod tests { } } } - - // Probe retained from the port plan: `[u16; N]` const tables are the format the - // generated NTT root tables use. - const PROBE_TABLE: [u16; 3] = [0, 6144, 12288]; - - #[test] - fn test_const_u16_table() { - let t = PROBE_TABLE.span(); - assert_eq!(*t.at(0), 0); - assert_eq!(*t.at(1), 6144); - assert_eq!(*t.at(2), 12288); - } } diff --git a/packages/account/src/lib.cairo b/packages/account/src/lib.cairo index b872440d9..a89c840ac 100644 --- a/packages/account/src/lib.cairo +++ b/packages/account/src/lib.cairo @@ -9,4 +9,7 @@ pub mod utils; pub use account::AccountComponent; pub use eth_account::EthAccountComponent; -pub use falcon_512::{Falcon512ShakeAccount, Falcon512ShakeDirectAccount}; +pub use falcon_512::{ + Falcon512AccountComponent, Falcon512ShakeDirectVerifier, Falcon512ShakeVerifier, + Falcon512SignatureVerifier, +}; diff --git a/packages/account/src/tests.cairo b/packages/account/src/tests.cairo index 21d83d450..f39d455ad 100644 --- a/packages/account/src/tests.cairo +++ b/packages/account/src/tests.cairo @@ -1,7 +1,6 @@ mod extensions; -mod falcon_512_fixture; -mod falcon_512_rotation_fixture; +mod falcon_512_account_mocks; mod test_account; mod test_eth_account; mod test_falcon_512; diff --git a/packages/account/src/tests/falcon_512_account_mocks.cairo b/packages/account/src/tests/falcon_512_account_mocks.cairo new file mode 100644 index 000000000..067c81e56 --- /dev/null +++ b/packages/account/src/tests/falcon_512_account_mocks.cairo @@ -0,0 +1,77 @@ +#[starknet::contract(account)] +pub mod Falcon512ShakeAccountMock { + use openzeppelin_introspection::src5::SRC5Component; + use crate::falcon_512::Falcon512ShakeVerifier; + use crate::falcon_512::account::Falcon512AccountComponent; + + component!(path: Falcon512AccountComponent, storage: account, event: AccountEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + #[abi(embed_v0)] + impl AccountMixinImpl = + Falcon512AccountComponent::Falcon512AccountMixinImpl; + + impl AccountInternalImpl = Falcon512AccountComponent::InternalImpl; + + #[storage] + pub struct Storage { + #[substorage(v0)] + pub account: Falcon512AccountComponent::Storage, + #[substorage(v0)] + pub src5: SRC5Component::Storage, + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + AccountEvent: Falcon512AccountComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event, + } + + #[constructor] + pub fn constructor(ref self: ContractState, public_key: Array) { + self.account.initializer::(public_key); + } +} + +#[starknet::contract(account)] +pub mod Falcon512ShakeDirectAccountMock { + use openzeppelin_introspection::src5::SRC5Component; + use crate::falcon_512::Falcon512ShakeDirectVerifier; + use crate::falcon_512::account::Falcon512AccountComponent; + + component!(path: Falcon512AccountComponent, storage: account, event: AccountEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + #[abi(embed_v0)] + impl AccountMixinImpl = + Falcon512AccountComponent::Falcon512AccountMixinImpl< + ContractState, Falcon512ShakeDirectVerifier, + >; + + impl AccountInternalImpl = Falcon512AccountComponent::InternalImpl; + + #[storage] + pub struct Storage { + #[substorage(v0)] + pub account: Falcon512AccountComponent::Storage, + #[substorage(v0)] + pub src5: SRC5Component::Storage, + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + AccountEvent: Falcon512AccountComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event, + } + + #[constructor] + pub fn constructor(ref self: ContractState, public_key: Array) { + self.account.initializer::(public_key); + } +} diff --git a/packages/account/src/tests/test_falcon_512.cairo b/packages/account/src/tests/test_falcon_512.cairo index d43f2caba..3f8d91ce7 100644 --- a/packages/account/src/tests/test_falcon_512.cairo +++ b/packages/account/src/tests/test_falcon_512.cairo @@ -6,6 +6,11 @@ use openzeppelin_interfaces::accounts::{ ISRC6DispatcherTrait, ISRC6_ID, }; use openzeppelin_interfaces::introspection::{ISRC5Dispatcher, ISRC5DispatcherTrait, ISRC5_ID}; +use openzeppelin_test_common::falcon_512::fixture::{msg, public_key, signature}; +use openzeppelin_test_common::falcon_512::rotation_fixture::{ + accept_ownership_hash, accept_ownership_signature, account_address, current_owner_guid, + new_owner_guid, new_public_key, second_accept_ownership_signature, +}; use openzeppelin_test_common::mocks::simple::{ISimpleMockDispatcher, ISimpleMockDispatcherTrait}; use openzeppelin_testing as utils; use openzeppelin_testing::constants::{ @@ -19,15 +24,8 @@ use snforge_std::{ }; use starknet::ContractAddress; use starknet::account::Call; -use crate::falcon_512::{ - DIRECT_SIGNATURE_FELTS, Falcon512ShakeDirectVerifier, Falcon512ShakeVerifier, PUBLIC_KEY_FELTS, - SIGNATURE_FELTS, -}; -use super::falcon_512_fixture::{msg, public_key, signature}; -use super::falcon_512_rotation_fixture::{ - accept_ownership_hash, accept_ownership_signature, account_address, current_owner_guid, - new_owner_guid, new_public_key, second_accept_ownership_signature, -}; +use crate::falcon_512::verifier_impls::{DIRECT_SIGNATURE_FELTS, PUBLIC_KEY_FELTS, SIGNATURE_FELTS}; +use crate::falcon_512::{Falcon512ShakeDirectVerifier, Falcon512ShakeVerifier}; const Q_POW_9: felt252 = 6392178558614694273495691177456939009; const TWO_POW_160: felt252 = 0x10000000000000000000000000000000000000000; @@ -206,7 +204,7 @@ fn test_hint_verifier_rejects_all_malformed_layout_branches() { } #[test] -fn test_hint_verifier_rejects_every_authenticated_input_tamper() { +fn test_hint_verifier_rejects_tampering_in_each_authenticated_region() { let key = public_key(); let valid_signature = signature(); @@ -285,7 +283,7 @@ fn test_direct_verifier_rejects_all_malformed_layout_branches() { } #[test] -fn test_direct_verifier_rejects_every_authenticated_input_tamper() { +fn test_direct_verifier_rejects_tampering_in_each_authenticated_region() { let key = public_key(); let valid_signature = direct_signature(); @@ -310,7 +308,7 @@ fn test_direct_verifier_rejects_every_authenticated_input_tamper() { } #[test] -fn test_hint_tampering_does_not_change_direct_signature() { +fn test_direct_signature_prefix_remains_valid_when_hint_suffix_is_tampered() { let key = public_key(); let valid_signature = signature(); let tampered = with_replaced(valid_signature.span(), 31, *valid_signature.at(31) + 1); @@ -325,8 +323,8 @@ fn test_hint_tampering_does_not_change_direct_signature() { #[test] fn test_accounts_expose_exact_public_key_and_interfaces() { - let (hint_address, _) = deploy_account("Falcon512ShakeAccount"); - let (direct_address, _) = deploy_account("Falcon512ShakeDirectAccount"); + let (hint_address, _) = deploy_account("Falcon512ShakeAccountMock"); + let (direct_address, _) = deploy_account("Falcon512ShakeDirectAccountMock"); for address in [hint_address, direct_address].span() { let key_dispatcher = IFeltArrayPublicKeyDispatcher { contract_address: *address }; @@ -340,9 +338,9 @@ fn test_accounts_expose_exact_public_key_and_interfaces() { } #[test] -fn test_accounts_is_valid_signature_return_convention() { - let (hint_address, _) = deploy_account("Falcon512ShakeAccount"); - let (direct_address, _) = deploy_account("Falcon512ShakeDirectAccount"); +fn test_accounts_follow_is_valid_signature_return_convention_and_reject_cross_layout() { + let (hint_address, _) = deploy_account("Falcon512ShakeAccountMock"); + let (direct_address, _) = deploy_account("Falcon512ShakeDirectAccountMock"); let hint = ISRC6Dispatcher { contract_address: hint_address }; let direct = ISRC6Dispatcher { contract_address: direct_address }; @@ -358,13 +356,13 @@ fn test_accounts_is_valid_signature_return_convention() { #[test] fn test_hint_account_rotates_key_via_authenticated_execute() { let address = account_address(); - let account = deploy_account_at("Falcon512ShakeAccount", address); + let account = deploy_account_at("Falcon512ShakeAccountMock", address); setup_transaction(address, msg(), signature(), MIN_TRANSACTION_VERSION); // Protocol validation authenticates the outer invoke with the current owner. assert_eq!(account.__validate__(array![]), starknet::VALIDATED); stop_cheat_caller_address(address); - // Cheat only the outer entrypoint call. The nested self-call keeps its real caller. + // Cheat only the outer entry point call. The nested self-call keeps its real caller. cheat_caller_address(address, ZERO, CheatSpan::TargetCalls(1)); let mut spy = spy_events(); @@ -389,7 +387,7 @@ fn test_hint_account_rotates_key_via_authenticated_execute() { #[test] fn test_hint_rotated_key_accepts_new_owner_and_rejects_old_owner() { let address = account_address(); - let account = deploy_account_at("Falcon512ShakeAccount", address); + let account = deploy_account_at("Falcon512ShakeAccountMock", address); let acceptance_signature = accept_ownership_signature(); start_cheat_caller_address(address, address); account.set_public_key(new_public_key(), acceptance_signature.span()); @@ -404,12 +402,12 @@ fn test_hint_rotated_key_accepts_new_owner_and_rejects_old_owner() { #[test] fn test_repeated_key_rotation_keeps_exact_storage_length() { let address = account_address(); - let account = deploy_account_at("Falcon512ShakeAccount", address); + let account = deploy_account_at("Falcon512ShakeAccountMock", address); start_cheat_caller_address(address, address); let first_signature = accept_ownership_signature(); account.set_public_key(new_public_key(), first_signature.span()); - // A second rotation overwrites the 29 stored felts instead of growing the Vec. + // A second rotation keeps the stored key at its fixed 29-felt length. let second_signature = second_accept_ownership_signature(); account.set_public_key(new_public_key(), second_signature.span()); assert_eq!(account.get_public_key().len(), PUBLIC_KEY_FELTS); @@ -418,7 +416,7 @@ fn test_repeated_key_rotation_keeps_exact_storage_length() { #[test] fn test_direct_account_rotates_key_through_camel_case_abi() { let address = account_address(); - let account = deploy_account_at("Falcon512ShakeDirectAccount", address); + let account = deploy_account_at("Falcon512ShakeDirectAccountMock", address); let acceptance_signature = direct_accept_ownership_signature(); start_cheat_caller_address(address, address); @@ -441,7 +439,7 @@ fn test_direct_account_rotates_key_through_camel_case_abi() { #[test] fn test_direct_account_camel_case_signature_alias() { let address = account_address(); - let account = deploy_account_at("Falcon512ShakeDirectAccount", address); + let account = deploy_account_at("Falcon512ShakeDirectAccountMock", address); assert_eq!(account.isValidSignature(msg(), direct_signature()), starknet::VALIDATED); } @@ -450,7 +448,7 @@ fn test_direct_account_camel_case_signature_alias() { fn test_constructor_emits_owner_added_guid() { let address = account_address(); let mut spy = spy_events(); - deploy_account_at("Falcon512ShakeAccount", address); + deploy_account_at("Falcon512ShakeAccountMock", address); spy .assert_only_event( @@ -459,20 +457,20 @@ fn test_constructor_emits_owner_added_guid() { } #[test] -#[should_panic(expected: 'Account: unauthorized')] +#[should_panic(expected: 'Falcon512: unauthorized')] fn test_key_rotation_rejects_non_self_caller() { let address = account_address(); - let account = deploy_account_at("Falcon512ShakeAccount", address); + let account = deploy_account_at("Falcon512ShakeAccountMock", address); start_cheat_caller_address(address, CALLER); account.set_public_key(new_public_key(), array![].span()); } #[test] -#[should_panic(expected: 'Account: invalid public key')] +#[should_panic(expected: 'Falcon512: invalid public key')] fn test_key_rotation_rejects_malformed_public_key_before_signature() { let address = account_address(); - let account = deploy_account_at("Falcon512ShakeDirectAccount", address); + let account = deploy_account_at("Falcon512ShakeDirectAccountMock", address); start_cheat_caller_address(address, address); account @@ -482,10 +480,10 @@ fn test_key_rotation_rejects_malformed_public_key_before_signature() { } #[test] -#[should_panic(expected: 'Account: invalid signature')] +#[should_panic(expected: 'Falcon512: invalid signature')] fn test_key_rotation_rejects_invalid_new_owner_proof() { let address = account_address(); - let account = deploy_account_at("Falcon512ShakeAccount", address); + let account = deploy_account_at("Falcon512ShakeAccountMock", address); start_cheat_caller_address(address, address); let valid_signature = accept_ownership_signature(); let invalid_signature = with_replaced(valid_signature.span(), 0, *valid_signature.at(0) + 1); @@ -494,10 +492,10 @@ fn test_key_rotation_rejects_invalid_new_owner_proof() { } #[test] -#[should_panic(expected: 'Account: invalid signature')] +#[should_panic(expected: 'Falcon512: invalid signature')] fn test_key_rotation_proof_cannot_be_replayed_to_another_account() { let address = CALLER; - let account = deploy_account_at("Falcon512ShakeDirectAccount", address); + let account = deploy_account_at("Falcon512ShakeDirectAccountMock", address); start_cheat_caller_address(address, address); let acceptance_signature = direct_accept_ownership_signature(); @@ -505,10 +503,10 @@ fn test_key_rotation_proof_cannot_be_replayed_to_another_account() { } #[test] -#[should_panic(expected: 'Account: invalid signature')] +#[should_panic(expected: 'Falcon512: invalid signature')] fn test_key_rotation_proof_cannot_be_replayed_after_rotation() { let address = account_address(); - let account = deploy_account_at("Falcon512ShakeDirectAccount", address); + let account = deploy_account_at("Falcon512ShakeDirectAccountMock", address); start_cheat_caller_address(address, address); let acceptance_signature = direct_accept_ownership_signature(); @@ -519,7 +517,7 @@ fn test_key_rotation_proof_cannot_be_replayed_after_rotation() { #[test] fn test_all_validation_entrypoints_accept_hint_account_signature() { - let (address, class_hash) = deploy_account("Falcon512ShakeAccount"); + let (address, class_hash) = deploy_account("Falcon512ShakeAccountMock"); setup_transaction(address, msg(), signature(), MIN_TRANSACTION_VERSION); let src6 = ISRC6Dispatcher { contract_address: address }; @@ -532,7 +530,7 @@ fn test_all_validation_entrypoints_accept_hint_account_signature() { #[test] fn test_all_validation_entrypoints_accept_direct_account_signature() { - let (address, class_hash) = deploy_account("Falcon512ShakeDirectAccount"); + let (address, class_hash) = deploy_account("Falcon512ShakeDirectAccountMock"); setup_transaction(address, msg(), direct_signature(), MIN_TRANSACTION_VERSION); let src6 = ISRC6Dispatcher { contract_address: address }; @@ -544,33 +542,33 @@ fn test_all_validation_entrypoints_accept_direct_account_signature() { } #[test] -#[should_panic(expected: 'Account: invalid signature')] +#[should_panic(expected: 'Falcon512: invalid signature')] fn test_hint_validate_rejects_invalid_signature() { - let (address, _) = deploy_account("Falcon512ShakeAccount"); + let (address, _) = deploy_account("Falcon512ShakeAccountMock"); setup_transaction(address, msg() + 1, signature(), MIN_TRANSACTION_VERSION); ISRC6Dispatcher { contract_address: address }.__validate__(array![]); } #[test] -#[should_panic(expected: 'Account: invalid signature')] +#[should_panic(expected: 'Falcon512: invalid signature')] fn test_direct_validate_rejects_invalid_signature() { - let (address, _) = deploy_account("Falcon512ShakeDirectAccount"); + let (address, _) = deploy_account("Falcon512ShakeDirectAccountMock"); setup_transaction(address, msg() + 1, direct_signature(), MIN_TRANSACTION_VERSION); ISRC6Dispatcher { contract_address: address }.__validate__(array![]); } #[test] -#[should_panic(expected: 'Account: invalid signature')] +#[should_panic(expected: 'Falcon512: invalid signature')] fn test_validate_declare_rejects_invalid_signature() { - let (address, class_hash) = deploy_account("Falcon512ShakeAccount"); + let (address, class_hash) = deploy_account("Falcon512ShakeAccountMock"); setup_transaction(address, msg() + 1, signature(), MIN_TRANSACTION_VERSION); IDeclarerDispatcher { contract_address: address }.__validate_declare__(class_hash); } #[test] -#[should_panic(expected: 'Account: invalid signature')] +#[should_panic(expected: 'Falcon512: invalid signature')] fn test_validate_deploy_rejects_invalid_signature() { - let (address, class_hash) = deploy_account("Falcon512ShakeAccount"); + let (address, class_hash) = deploy_account("Falcon512ShakeAccountMock"); setup_transaction(address, msg() + 1, signature(), MIN_TRANSACTION_VERSION); IFeltArrayDeployableDispatcher { contract_address: address } .__validate_deploy__(class_hash, SALT, public_key()); @@ -579,7 +577,7 @@ fn test_validate_deploy_rejects_invalid_signature() { #[test] #[should_panic] fn test_constructor_rejects_wrong_public_key_length() { - let contract_class = utils::declare_class("Falcon512ShakeAccount"); + let contract_class = utils::declare_class("Falcon512ShakeAccountMock"); let mut calldata = array![]; copy_prefix(public_key().span(), PUBLIC_KEY_FELTS - 1).serialize(ref calldata); utils::deploy(contract_class, calldata); @@ -588,7 +586,7 @@ fn test_constructor_rejects_wrong_public_key_length() { #[test] #[should_panic] fn test_constructor_rejects_noncanonical_public_key() { - let contract_class = utils::declare_class("Falcon512ShakeDirectAccount"); + let contract_class = utils::declare_class("Falcon512ShakeDirectAccountMock"); let mut calldata = array![]; with_replaced(public_key().span(), 0, Q_POW_9).serialize(ref calldata); utils::deploy(contract_class, calldata); @@ -596,7 +594,7 @@ fn test_constructor_rejects_noncanonical_public_key() { #[test] fn test_execute_empty_single_multicall_and_supported_versions() { - let (address, _) = deploy_account("Falcon512ShakeAccount"); + let (address, _) = deploy_account("Falcon512ShakeAccountMock"); setup_transaction(address, msg(), signature(), MIN_TRANSACTION_VERSION); let account = ISRC6Dispatcher { contract_address: address }; account.__execute__(array![]); @@ -622,25 +620,25 @@ fn test_execute_empty_single_multicall_and_supported_versions() { } #[test] -#[should_panic(expected: 'Account: invalid tx version')] +#[should_panic(expected: 'Falcon512: invalid tx version')] fn test_execute_rejects_zero_version() { - let (address, _) = deploy_account("Falcon512ShakeAccount"); + let (address, _) = deploy_account("Falcon512ShakeAccountMock"); setup_transaction(address, msg(), signature(), MIN_TRANSACTION_VERSION - 1); ISRC6Dispatcher { contract_address: address }.__execute__(array![]); } #[test] -#[should_panic(expected: 'Account: invalid tx version')] +#[should_panic(expected: 'Falcon512: invalid tx version')] fn test_execute_rejects_query_offset_without_version() { - let (address, _) = deploy_account("Falcon512ShakeAccount"); + let (address, _) = deploy_account("Falcon512ShakeAccountMock"); setup_transaction(address, msg(), signature(), QUERY_OFFSET); ISRC6Dispatcher { contract_address: address }.__execute__(array![]); } #[test] -#[should_panic(expected: 'Account: invalid caller')] +#[should_panic(expected: 'Falcon512: invalid caller')] fn test_execute_rejects_contract_caller() { - let (address, _) = deploy_account("Falcon512ShakeAccount"); + let (address, _) = deploy_account("Falcon512ShakeAccountMock"); setup_transaction(address, msg(), signature(), MIN_TRANSACTION_VERSION); start_cheat_caller_address(address, CALLER); ISRC6Dispatcher { contract_address: address }.__execute__(array![]); diff --git a/packages/account/src/tests/test_falcon_512_ntt.cairo b/packages/account/src/tests/test_falcon_512_ntt.cairo index 2374ae39b..8d6ac5a0a 100644 --- a/packages/account/src/tests/test_falcon_512_ntt.cairo +++ b/packages/account/src/tests/test_falcon_512_ntt.cairo @@ -71,7 +71,7 @@ fn assert_fast_matches_generic(values: Span) { #[cfg(feature: 'falcon_fast_tests')] #[test] -fn test_fast_ntt_matches_generic_reference_on_adversarial_inputs() { +fn test_fast_ntt_matches_generic_on_boundary_and_pseudorandom_inputs() { assert_fast_matches_generic(zeros().span()); assert_fast_matches_generic(max_values().span()); assert_fast_matches_generic(basis(0).span()); @@ -79,6 +79,20 @@ fn test_fast_ntt_matches_generic_reference_on_adversarial_inputs() { assert_fast_matches_generic(pseudorandom_values(7).span()); } +#[cfg(feature: 'falcon_fast_tests')] +#[test] +fn test_fast_ntt_matches_generic_reference_for_every_basis_vector() { + let mut index = 0; + while index != 512 { + let values = basis(index); + let input = as_felts(values.span()); + let generic = ntt(input.span(), @config()); + let fast = ntt_falcon512_fast_u16_unchecked(values.span()); + assert_eq!(generic.span(), as_felts(fast.span()).span()); + index += 1; + } +} + #[cfg(feature: 'falcon_fast_tests')] #[test] fn test_fast_ntt_inverse_roundtrip() { @@ -119,7 +133,7 @@ fn test_intt_rejects_wrong_length() { #[test] #[should_panic(expected: 'intt: input too large')] -fn test_intt_rejects_excessive_input_bound() { +fn test_intt_rejects_excessive_input_bits() { let values = as_felts(zeros().span()); intt(values.span(), 120, PRODUCT_BOUND_FELT, @config()); } diff --git a/packages/corelib_imports/README.md b/packages/corelib_imports/README.md new file mode 100644 index 000000000..18f5395c8 --- /dev/null +++ b/packages/corelib_imports/README.md @@ -0,0 +1,8 @@ +## Corelib Imports + +Provides the gated Cairo corelib re-exports required by OpenZeppelin packages. The `2023_10` +edition exposes these APIs under Cairo 2.18.0; they are unavailable to a `2024_07` package in that +compiler version. + +This compiler-compatibility package contains only re-exports. Its API is internal to the workspace +and may change with Cairo compiler upgrades. diff --git a/packages/corelib_imports/Scarb.toml b/packages/corelib_imports/Scarb.toml new file mode 100644 index 000000000..66f9be8f2 --- /dev/null +++ b/packages/corelib_imports/Scarb.toml @@ -0,0 +1,24 @@ +[package] +name = "openzeppelin_corelib_imports" +readme = "README.md" +version.workspace = true +edition = "2023_10" +cairo-version.workspace = true +scarb-version.workspace = true +authors.workspace = true +description = "OpenZeppelin compatibility imports for gated Cairo corelib APIs" +documentation.workspace = true +repository.workspace = true +license-file.workspace = true +keywords = [ + "openzeppelin", + "cairo", + "corelib", + "internal" +] + +[tool] +fmt.workspace = true +scarb.workspace = true + +[lib] diff --git a/packages/corelib_imports/src/lib.cairo b/packages/corelib_imports/src/lib.cairo new file mode 100644 index 000000000..da2457510 --- /dev/null +++ b/packages/corelib_imports/src/lib.cairo @@ -0,0 +1,10 @@ +pub mod bounded_int { + #[feature("bounded-int-utils")] + pub use core::internal::bounded_int::{ + BoundedInt, DivRemHelper, UnitInt, bounded_int_div_rem, downcast, upcast, + }; +} + +pub mod integer { + pub use core::integer::{U128sFromFelt252Result, u128s_from_felt252}; +} diff --git a/packages/interfaces/README.md b/packages/interfaces/README.md index 8396f4f21..b57e77364 100644 --- a/packages/interfaces/README.md +++ b/packages/interfaces/README.md @@ -2,7 +2,7 @@ List of interfaces, ABI traits and dispatchers. -### Interfaces +### Interface Traits - [`IAccessControlDefaultAdminRules`](https://docs.openzeppelin.com/contracts-cairo/3.x/api/access#IAccessControlDefaultAdminRules) - [`IAccessControl`](https://docs.openzeppelin.com/contracts-cairo/3.x/api/access#IAccessControl) @@ -68,7 +68,6 @@ List of interfaces, ABI traits and dispatchers. - [`IUpgradeable`](https://docs.openzeppelin.com/contracts-cairo/3.x/api/upgrades#IUpgradeable) - [`IUpgradeAndCall`](https://docs.openzeppelin.com/contracts-cairo/3.x/api/upgrades#IUpgradeAndCall) - ### ABI Traits - `AccessControlDefaultAdminRulesABI` diff --git a/packages/interfaces/src/account/accounts.cairo b/packages/interfaces/src/account/accounts.cairo index 3c3098819..65fe19811 100644 --- a/packages/interfaces/src/account/accounts.cairo +++ b/packages/interfaces/src/account/accounts.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (interfaces/src/account/account.cairo) +// OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 (interfaces/src/account/accounts.cairo) use starknet::account::Call; @@ -53,6 +53,7 @@ pub trait IDeployable { #[starknet::interface] pub trait IFeltArrayDeployable { /// Validates a deploy-account transaction before deployment. + /// This function is used by the protocol to verify `deploy_account` transactions. /// /// Returns the short string 'VALID' if valid, otherwise it reverts. fn __validate_deploy__( diff --git a/packages/presets/README.md b/packages/presets/README.md index 9b8a4a78a..34cd68c95 100644 --- a/packages/presets/README.md +++ b/packages/presets/README.md @@ -8,6 +8,14 @@ generic as possible, there’s no support for custom or complex contracts such a For contract customization and combination of modules you can use [Wizard for Cairo](https://wizard.openzeppelin.com/cairo), our code-generation tool. +The Falcon-512 account presets support SRC9 outside execution, self-authorized class upgrades, and +owner-authorized key rotation. Build their deployable artifacts with +`scarb --release build -p openzeppelin_presets`; their Falcon verification paths require the +release compiler profile. + +> **WARNING:** The Falcon presets use contract-specific public-key and signature encodings for the +> FALCON submission verification relation. They are not FN-DSA (FIPS 206) implementations. + ### Presets - [`AccountUpgradeable`](https://docs.openzeppelin.com/contracts-cairo/3.x/api/account#AccountUpgradeable) @@ -15,4 +23,6 @@ For contract customization and combination of modules you can use - [`ERC721Upgradeable`](https://docs.openzeppelin.com/contracts-cairo/3.x/api/erc721#ERC721Upgradeable) - [`ERC1155Upgradeable`](https://docs.openzeppelin.com/contracts-cairo/3.x/api/erc1155#ERC1155Upgradeable) - [`EthAccountUpgradeable`](https://docs.openzeppelin.com/contracts-cairo/3.x/api/account#EthAccountUpgradeable) +- [`Falcon512ShakeAccountUpgradeable`](https://docs.openzeppelin.com/contracts-cairo/3.x/api/account#Falcon512ShakeAccountUpgradeable) +- [`Falcon512ShakeDirectAccountUpgradeable`](https://docs.openzeppelin.com/contracts-cairo/3.x/api/account#Falcon512ShakeDirectAccountUpgradeable) - [`UniversalDeployer`](https://docs.openzeppelin.com/contracts-cairo/3.x/api/udc#UniversalDeployer) diff --git a/packages/presets/Scarb.toml b/packages/presets/Scarb.toml index a040ea672..bfde777e0 100644 --- a/packages/presets/Scarb.toml +++ b/packages/presets/Scarb.toml @@ -38,6 +38,10 @@ snforge_std.workspace = true openzeppelin_testing = { path = "../testing" } openzeppelin_test_common = { path = "../test_common" } +[features] +# Enables the Falcon preset integration tests; requires the release compiler profile. +falcon_presets_tests = [] + [lib] [[target.starknet-contract]] diff --git a/packages/presets/src/falcon_512_shake_account.cairo b/packages/presets/src/falcon_512_shake_account.cairo new file mode 100644 index 000000000..cdb666ec2 --- /dev/null +++ b/packages/presets/src/falcon_512_shake_account.cairo @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 +// (presets/src/falcon_512_shake_account.cairo) + +/// # Falcon512ShakeAccount Preset +/// +/// OpenZeppelin's upgradeable Falcon-512 account using SHAKE-256 hash-to-point and a +/// verifier-checked polynomial-product hint. Supports outside execution by implementing SRC9. +/// Its public-key and signature formats are contract-specific encodings for the FALCON submission +/// verification relation, rather than FN-DSA (FIPS 206) encodings. +#[starknet::contract(account)] +pub mod Falcon512ShakeAccountUpgradeable { + use openzeppelin_account::extensions::SRC9Component; + use openzeppelin_account::{Falcon512AccountComponent, Falcon512ShakeVerifier}; + use openzeppelin_interfaces::upgrades::IUpgradeable; + use openzeppelin_introspection::src5::SRC5Component; + use openzeppelin_upgrades::UpgradeableComponent; + use starknet::ClassHash; + + component!(path: Falcon512AccountComponent, storage: account, event: AccountEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + component!(path: SRC9Component, storage: src9, event: SRC9Event); + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); + + // Falcon512Account Mixin + #[abi(embed_v0)] + pub(crate) impl Falcon512AccountMixinImpl = + Falcon512AccountComponent::Falcon512AccountMixinImpl; + impl Falcon512AccountInternalImpl = Falcon512AccountComponent::InternalImpl; + + // SRC9 + #[abi(embed_v0)] + impl OutsideExecutionV2Impl = + SRC9Component::OutsideExecutionV2Impl; + impl OutsideExecutionInternalImpl = SRC9Component::InternalImpl; + + // Upgradeable + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; + + #[storage] + pub struct Storage { + #[substorage(v0)] + pub account: Falcon512AccountComponent::Storage, + #[substorage(v0)] + pub src5: SRC5Component::Storage, + #[substorage(v0)] + pub src9: SRC9Component::Storage, + #[substorage(v0)] + pub upgradeable: UpgradeableComponent::Storage, + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + AccountEvent: Falcon512AccountComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event, + #[flat] + SRC9Event: SRC9Component::Event, + #[flat] + UpgradeableEvent: UpgradeableComponent::Event, + } + + #[constructor] + pub fn constructor(ref self: ContractState, public_key: Array) { + self.account.initializer::(public_key); + self.src9.initializer(); + } + + #[abi(embed_v0)] + impl UpgradeableImpl of IUpgradeable { + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { + self.account.assert_only_self(); + self.upgradeable.upgrade(new_class_hash); + } + } +} diff --git a/packages/presets/src/falcon_512_shake_direct_account.cairo b/packages/presets/src/falcon_512_shake_direct_account.cairo new file mode 100644 index 000000000..b8958c813 --- /dev/null +++ b/packages/presets/src/falcon_512_shake_direct_account.cairo @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v4.0.0-alpha.1 +// (presets/src/falcon_512_shake_direct_account.cairo) + +/// # Falcon512ShakeDirectAccount Preset +/// +/// OpenZeppelin's upgradeable Falcon-512 account using SHAKE-256 hash-to-point and on-chain +/// polynomial-product recomputation. Supports outside execution by implementing SRC9. +/// Its public-key and signature formats are contract-specific encodings for the FALCON submission +/// verification relation, rather than FN-DSA (FIPS 206) encodings. +#[starknet::contract(account)] +pub mod Falcon512ShakeDirectAccountUpgradeable { + use openzeppelin_account::extensions::SRC9Component; + use openzeppelin_account::{Falcon512AccountComponent, Falcon512ShakeDirectVerifier}; + use openzeppelin_interfaces::upgrades::IUpgradeable; + use openzeppelin_introspection::src5::SRC5Component; + use openzeppelin_upgrades::UpgradeableComponent; + use starknet::ClassHash; + + component!(path: Falcon512AccountComponent, storage: account, event: AccountEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + component!(path: SRC9Component, storage: src9, event: SRC9Event); + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); + + // Falcon512Account Mixin + #[abi(embed_v0)] + pub(crate) impl Falcon512AccountMixinImpl = + Falcon512AccountComponent::Falcon512AccountMixinImpl< + ContractState, Falcon512ShakeDirectVerifier, + >; + impl Falcon512AccountInternalImpl = Falcon512AccountComponent::InternalImpl; + + // SRC9 + #[abi(embed_v0)] + impl OutsideExecutionV2Impl = + SRC9Component::OutsideExecutionV2Impl; + impl OutsideExecutionInternalImpl = SRC9Component::InternalImpl; + + // Upgradeable + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; + + #[storage] + pub struct Storage { + #[substorage(v0)] + pub account: Falcon512AccountComponent::Storage, + #[substorage(v0)] + pub src5: SRC5Component::Storage, + #[substorage(v0)] + pub src9: SRC9Component::Storage, + #[substorage(v0)] + pub upgradeable: UpgradeableComponent::Storage, + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + AccountEvent: Falcon512AccountComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event, + #[flat] + SRC9Event: SRC9Component::Event, + #[flat] + UpgradeableEvent: UpgradeableComponent::Event, + } + + #[constructor] + pub fn constructor(ref self: ContractState, public_key: Array) { + self.account.initializer::(public_key); + self.src9.initializer(); + } + + #[abi(embed_v0)] + impl UpgradeableImpl of IUpgradeable { + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { + self.account.assert_only_self(); + self.upgradeable.upgrade(new_class_hash); + } + } +} diff --git a/packages/presets/src/interfaces.cairo b/packages/presets/src/interfaces.cairo index f0d3228f8..98f848b2d 100644 --- a/packages/presets/src/interfaces.cairo +++ b/packages/presets/src/interfaces.cairo @@ -3,6 +3,7 @@ pub mod erc1155; pub mod erc20; pub mod erc721; pub mod eth_account; +pub mod falcon_512_account; pub mod meta_tx_v0; pub mod vesting; @@ -21,6 +22,10 @@ pub use eth_account::{ EthAccountUpgradeableABI, EthAccountUpgradeableABIDispatcher, EthAccountUpgradeableABIDispatcherTrait, }; +pub use falcon_512_account::{ + Falcon512AccountUpgradeableABI, Falcon512AccountUpgradeableABIDispatcher, + Falcon512AccountUpgradeableABIDispatcherTrait, +}; pub use meta_tx_v0::{ MetaTransactionV0ABI, MetaTransactionV0ABIDispatcher, MetaTransactionV0ABIDispatcherTrait, }; diff --git a/packages/presets/src/interfaces/falcon_512_account.cairo b/packages/presets/src/interfaces/falcon_512_account.cairo new file mode 100644 index 000000000..d26de0650 --- /dev/null +++ b/packages/presets/src/interfaces/falcon_512_account.cairo @@ -0,0 +1,45 @@ +use openzeppelin_interfaces::src9::OutsideExecution; +use starknet::ClassHash; +use starknet::account::Call; + +#[starknet::interface] +pub trait Falcon512AccountUpgradeableABI { + // ISRC6 + fn __execute__(self: @TState, calls: Array); + fn __validate__(self: @TState, calls: Array) -> felt252; + fn is_valid_signature(self: @TState, hash: felt252, signature: Array) -> felt252; + + // ISRC5 + fn supports_interface(self: @TState, interface_id: felt252) -> bool; + + // ISRC9 + fn execute_from_outside_v2( + ref self: TState, outside_execution: OutsideExecution, signature: Span, + ) -> Array>; + fn is_valid_outside_execution_nonce(self: @TState, nonce: felt252) -> bool; + + // IDeclarer + fn __validate_declare__(self: @TState, class_hash: felt252) -> felt252; + + // IFeltArrayDeployable + fn __validate_deploy__( + self: @TState, + class_hash: felt252, + contract_address_salt: felt252, + public_key: Array, + ) -> felt252; + + // IFeltArrayPublicKey + fn get_public_key(self: @TState) -> Array; + fn set_public_key(ref self: TState, new_public_key: Array, signature: Span); + + // ISRC6CamelOnly + fn isValidSignature(self: @TState, hash: felt252, signature: Array) -> felt252; + + // IFeltArrayPublicKeyCamel + fn getPublicKey(self: @TState) -> Array; + fn setPublicKey(ref self: TState, newPublicKey: Array, signature: Span); + + // IUpgradeable + fn upgrade(ref self: TState, new_class_hash: ClassHash); +} diff --git a/packages/presets/src/lib.cairo b/packages/presets/src/lib.cairo index a06b38d28..0c36b9eb8 100644 --- a/packages/presets/src/lib.cairo +++ b/packages/presets/src/lib.cairo @@ -3,6 +3,16 @@ pub mod erc1155; pub mod erc20; pub mod erc721; pub mod eth_account; +#[cfg(not(test))] +pub mod falcon_512_shake_account; +#[cfg(test)] +#[cfg(feature: 'falcon_presets_tests')] +pub mod falcon_512_shake_account; +#[cfg(not(test))] +pub mod falcon_512_shake_direct_account; +#[cfg(test)] +#[cfg(feature: 'falcon_presets_tests')] +pub mod falcon_512_shake_direct_account; pub mod interfaces; pub mod meta_tx_v0; @@ -17,6 +27,16 @@ pub use erc1155::ERC1155Upgradeable; pub use erc20::ERC20Upgradeable; pub use erc721::ERC721Upgradeable; pub use eth_account::EthAccountUpgradeable; +#[cfg(not(test))] +pub use falcon_512_shake_account::Falcon512ShakeAccountUpgradeable; +#[cfg(test)] +#[cfg(feature: 'falcon_presets_tests')] +pub use falcon_512_shake_account::Falcon512ShakeAccountUpgradeable; +#[cfg(not(test))] +pub use falcon_512_shake_direct_account::Falcon512ShakeDirectAccountUpgradeable; +#[cfg(test)] +#[cfg(feature: 'falcon_presets_tests')] +pub use falcon_512_shake_direct_account::Falcon512ShakeDirectAccountUpgradeable; pub use meta_tx_v0::MetaTransactionV0; pub use universal_deployer::UniversalDeployer; pub use vesting::VestingWallet; diff --git a/packages/presets/src/tests.cairo b/packages/presets/src/tests.cairo index 726e1da2d..72d95c446 100644 --- a/packages/presets/src/tests.cairo +++ b/packages/presets/src/tests.cairo @@ -3,6 +3,8 @@ mod test_erc1155; mod test_erc20; mod test_erc721; mod test_eth_account; +#[cfg(feature: 'falcon_presets_tests')] +mod test_falcon_512_accounts; mod test_meta_tx_v0; mod test_universal_deployer; mod test_vesting; diff --git a/packages/presets/src/tests/test_falcon_512_accounts.cairo b/packages/presets/src/tests/test_falcon_512_accounts.cairo new file mode 100644 index 000000000..2146efa89 --- /dev/null +++ b/packages/presets/src/tests/test_falcon_512_accounts.cairo @@ -0,0 +1,213 @@ +use openzeppelin_account::extensions::SRC9Component::SNIP12MetadataImpl; +use openzeppelin_account::extensions::src9::snip12_utils::OutsideExecutionStructHash; +use openzeppelin_interfaces::accounts::{ + FeltArrayAccountABIDispatcher, FeltArrayAccountABIDispatcherTrait, ISRC6_ID, +}; +use openzeppelin_interfaces::introspection::{ISRC5Dispatcher, ISRC5DispatcherTrait, ISRC5_ID}; +use openzeppelin_interfaces::src9::{ + ISRC9_V2Dispatcher, ISRC9_V2DispatcherTrait, ISRC9_V2_ID, OutsideExecution, +}; +use openzeppelin_interfaces::upgrades::{IUpgradeableDispatcher, IUpgradeableDispatcherTrait}; +use openzeppelin_test_common::falcon_512::fixture::{msg, public_key, signature}; +use openzeppelin_test_common::falcon_512::rotation_fixture::{ + accept_ownership_hash, accept_ownership_signature, account_address, new_public_key, + outside_execution_hash, outside_execution_signature, +}; +use openzeppelin_testing as utils; +use openzeppelin_testing::constants::CLASS_HASH_ZERO; +use openzeppelin_utils::cryptography::snip12::OffchainMessageHash; +use snforge_std::{ + start_cheat_block_timestamp_global, start_cheat_caller_address, start_cheat_chain_id_global, +}; +use starknet::ContractAddress; + +const DIRECT_SIGNATURE_FELTS: u32 = 31; + +fn copy_prefix(mut values: Span, length: u32) -> Array { + let mut output = array![]; + let mut index = 0; + while index != length { + output.append(*values.pop_front().unwrap()); + index += 1; + } + output +} + +fn direct_signature() -> Array { + copy_prefix(signature().span(), DIRECT_SIGNATURE_FELTS) +} + +fn direct_accept_ownership_signature() -> Array { + copy_prefix(accept_ownership_signature().span(), DIRECT_SIGNATURE_FELTS) +} + +fn deploy(class_name: ByteArray) -> FeltArrayAccountABIDispatcher { + let mut calldata = array![]; + public_key().serialize(ref calldata); + let contract_address = utils::declare_and_deploy(class_name, calldata); + FeltArrayAccountABIDispatcher { contract_address } +} + +fn deploy_at( + class_name: ByteArray, contract_address: ContractAddress, +) -> FeltArrayAccountABIDispatcher { + let contract_class = utils::declare_class(class_name); + let mut calldata = array![]; + public_key().serialize(ref calldata); + utils::deploy_at(contract_class, contract_address, calldata); + FeltArrayAccountABIDispatcher { contract_address } +} + +fn deploy_with_new_key_at(class_name: ByteArray) -> FeltArrayAccountABIDispatcher { + let contract_class = utils::declare_class(class_name); + let mut calldata = array![]; + new_public_key().serialize(ref calldata); + let contract_address = account_address(); + utils::deploy_at(contract_class, contract_address, calldata); + FeltArrayAccountABIDispatcher { contract_address } +} + +fn assert_constructor_interfaces_and_signature( + class_name: ByteArray, valid_signature: Array, +) { + let account = deploy(class_name); + assert_eq!(account.get_public_key().span(), public_key().span()); + assert_eq!(account.is_valid_signature(msg(), valid_signature), starknet::VALIDATED); + + let src5 = ISRC5Dispatcher { contract_address: account.contract_address }; + assert!(src5.supports_interface(ISRC5_ID)); + assert!(src5.supports_interface(ISRC6_ID)); + assert!(src5.supports_interface(ISRC9_V2_ID)); +} + +#[test] +fn test_falcon_presets_initialize_interfaces_and_variant_verifiers() { + assert_constructor_interfaces_and_signature("Falcon512ShakeAccountUpgradeable", signature()); + assert_constructor_interfaces_and_signature( + "Falcon512ShakeDirectAccountUpgradeable", direct_signature(), + ); +} + +fn assert_src9_with_real_signature(class_name: ByteArray, valid_signature: Array) { + let account = deploy_with_new_key_at(class_name); + let src9 = ISRC9_V2Dispatcher { contract_address: account.contract_address }; + let nonce = 5; + let calls = array![]; + let outside_execution = OutsideExecution { + caller: 'ANY_CALLER'.try_into().unwrap(), + nonce, + execute_after: 10, + execute_before: 20, + calls: calls.span(), + }; + + start_cheat_chain_id_global('SN_SEPOLIA'); + assert_eq!( + outside_execution.get_message_hash(account.contract_address), outside_execution_hash(), + ); + start_cheat_block_timestamp_global(15); + + assert!(src9.is_valid_outside_execution_nonce(nonce)); + src9.execute_from_outside_v2(outside_execution, valid_signature.span()); + assert!(!src9.is_valid_outside_execution_nonce(nonce)); +} + +#[test] +fn test_falcon_shake_preset_executes_from_outside_with_real_signature() { + assert_src9_with_real_signature( + "Falcon512ShakeAccountUpgradeable", outside_execution_signature(), + ); +} + +#[test] +fn test_falcon_shake_direct_preset_executes_from_outside_with_real_signature() { + assert_src9_with_real_signature( + "Falcon512ShakeDirectAccountUpgradeable", + copy_prefix(outside_execution_signature().span(), DIRECT_SIGNATURE_FELTS), + ); +} + +fn execute_src9_with_invalid_signature(class_name: ByteArray) { + let account = deploy(class_name); + let src9 = ISRC9_V2Dispatcher { contract_address: account.contract_address }; + let outside_execution = OutsideExecution { + caller: 'ANY_CALLER'.try_into().unwrap(), + nonce: 5, + execute_after: 10, + execute_before: 20, + calls: array![].span(), + }; + + start_cheat_block_timestamp_global(15); + src9.execute_from_outside_v2(outside_execution, array![].span()); +} + +#[test] +#[should_panic(expected: 'SRC9: invalid signature')] +fn test_falcon_shake_preset_routes_src9_signature_validation() { + execute_src9_with_invalid_signature("Falcon512ShakeAccountUpgradeable"); +} + +#[test] +#[should_panic(expected: 'SRC9: invalid signature')] +fn test_falcon_shake_direct_preset_routes_src9_signature_validation() { + execute_src9_with_invalid_signature("Falcon512ShakeDirectAccountUpgradeable"); +} + +fn reject_unauthorized_upgrade(class_name: ByteArray) { + let account = deploy(class_name); + IUpgradeableDispatcher { contract_address: account.contract_address }.upgrade(CLASS_HASH_ZERO); +} + +#[test] +#[should_panic(expected: 'Falcon512: unauthorized')] +fn test_falcon_shake_preset_rejects_unauthorized_upgrade() { + reject_unauthorized_upgrade("Falcon512ShakeAccountUpgradeable"); +} + +#[test] +#[should_panic(expected: 'Falcon512: unauthorized')] +fn test_falcon_shake_direct_preset_rejects_unauthorized_upgrade() { + reject_unauthorized_upgrade("Falcon512ShakeDirectAccountUpgradeable"); +} + +fn assert_rotation_survives_cross_variant_upgrade( + source_class_name: ByteArray, + target_class_name: ByteArray, + rotation_signature: Array, + post_upgrade_signature: Array, +) { + let address = account_address(); + let account = deploy_at(source_class_name, address); + start_cheat_caller_address(address, address); + account.set_public_key(new_public_key(), rotation_signature.span()); + + let target_class_hash = utils::declare_class(target_class_name).class_hash; + IUpgradeableDispatcher { contract_address: address }.upgrade(target_class_hash); + + assert_eq!(account.get_public_key().span(), new_public_key().span()); + assert_eq!( + account.is_valid_signature(accept_ownership_hash(), post_upgrade_signature), + starknet::VALIDATED, + ); +} + +#[test] +fn test_falcon_shake_preset_storage_survives_upgrade_to_direct_variant() { + assert_rotation_survives_cross_variant_upgrade( + "Falcon512ShakeAccountUpgradeable", + "Falcon512ShakeDirectAccountUpgradeable", + accept_ownership_signature(), + direct_accept_ownership_signature(), + ); +} + +#[test] +fn test_falcon_shake_direct_preset_storage_survives_upgrade_to_hint_variant() { + assert_rotation_survives_cross_variant_upgrade( + "Falcon512ShakeDirectAccountUpgradeable", + "Falcon512ShakeAccountUpgradeable", + direct_accept_ownership_signature(), + accept_ownership_signature(), + ); +} diff --git a/packages/test_common/src/falcon_512.cairo b/packages/test_common/src/falcon_512.cairo new file mode 100644 index 000000000..36ad16180 --- /dev/null +++ b/packages/test_common/src/falcon_512.cairo @@ -0,0 +1,2 @@ +pub mod fixture; +pub mod rotation_fixture; diff --git a/packages/account/src/tests/falcon_512_fixture.cairo b/packages/test_common/src/falcon_512/fixture.cairo similarity index 93% rename from packages/account/src/tests/falcon_512_fixture.cairo rename to packages/test_common/src/falcon_512/fixture.cairo index 217d1005c..dd79dbbd0 100644 --- a/packages/account/src/tests/falcon_512_fixture.cairo +++ b/packages/test_common/src/falcon_512/fixture.cairo @@ -1,11 +1,11 @@ -//! Locally generated FALCON-512 submission-algorithm fixture — do not edit by hand. +//! Fixed test vector for the Falcon-512 submission algorithm. //! -//! The fixture contains a keypair and signature generated with `tprest/falcon.py` over -//! `BENCH_MSG`, using falcon.py's SHAKE-256 hash-to-point. Its felt encoding is specific -//! to these Cairo accounts and does not claim conformance with FN-DSA/FIPS 206. -//! salt = 0x4ad23197d576f705594ac172f4fca80e3338e991b8b55e269d421aa1522cd0aa2490034127651fc2. +//! The public key and signature were produced with the `tprest/falcon.py` Falcon-512 signer using +//! its SHAKE-256 hash-to-point over `msg()`, then packed with the accounts' Cairo-specific felt +//! encoding. This is not an FN-DSA (FIPS 206) conformance vector. +//! Salt: `0x4ad23197d576f705594ac172f4fca80e3338e991b8b55e269d421aa1522cd0aa2490034127651fc2`. -/// The benchmark message hash ('BENCH_MSG'). +/// Fixed reference message hash used by this fixture. pub fn msg() -> felt252 { 1222479111879746474823 } diff --git a/packages/account/src/tests/falcon_512_rotation_fixture.cairo b/packages/test_common/src/falcon_512/rotation_fixture.cairo similarity index 67% rename from packages/account/src/tests/falcon_512_rotation_fixture.cairo rename to packages/test_common/src/falcon_512/rotation_fixture.cairo index 6ec8d4aae..315380e26 100644 --- a/packages/account/src/tests/falcon_512_rotation_fixture.cairo +++ b/packages/test_common/src/falcon_512/rotation_fixture.cairo @@ -1,9 +1,9 @@ -//! Deterministic Falcon-512 ownership-acceptance test vector — do not edit by hand. +//! Fixed Falcon-512 account integration test vectors. //! -//! The public key comes from the benchmark-only demo key. The signature authenticates -//! `accept_ownership_hash()` and uses a deterministic salt and sampling seed so the -//! fixture remains reproducible. These values are test material and must never be used -//! for production keys. +//! The vectors cover new-owner acceptance, same-key rotation, and SRC9 outside execution for +//! `account_address()`. Their keys and signatures were generated with the +//! `tprest/falcon.py`-based test signer and use the accounts' Cairo-specific felt encoding. This +//! test material must not be used in production. /// Fixed address used to domain-separate the ownership-acceptance fixture. pub fn account_address() -> starknet::ContractAddress { @@ -15,7 +15,7 @@ pub fn accept_ownership_hash() -> felt252 { 0x4e62665f5de5d234888da2b01cedcd1c3ed0b70149a6697d6c881b38021c0a } -/// Poseidon hash of the original fixture's packed public key. +/// Poseidon hash of [`super::fixture::public_key()`]. pub fn current_owner_guid() -> felt252 { 0x1c017f6368c49d94e2fee2cebcac6def11fd9d75dd9042216f630dac0376c4 } @@ -25,7 +25,7 @@ pub fn new_owner_guid() -> felt252 { 0x17705763a1277b0c72cc319dc0015046911575a5f00c2c4b8cde3936383f329 } -/// Canonical packed NTT-domain public key accepted by the new owner (29 felts). +/// Canonical packed NTT-domain public key for the prospective owner (29 felts). pub fn new_public_key() -> Array { array![ 0x9b070736af5cc06617e666684c2c7a010403b7d0d19dcd9cfb81189549805a, @@ -60,7 +60,7 @@ pub fn new_public_key() -> Array { ] } -/// New-owner acceptance signature: s1 (29) || salt (2) || checked product hint (29). +/// New-owner acceptance signature: packed s1 (29) || salt (2) || packed mul_hint (29). pub fn accept_ownership_signature() -> Array { array![ 0x4bd8ec4b708072cf5362a321179003104c4c50f2f986221418a96705d433e21, @@ -124,12 +124,13 @@ pub fn accept_ownership_signature() -> Array { 0xcbd8a83727b237f7924603d6f19, ] } -/// Ownership-acceptance hash after the first rotation, when the demo key is current. + +/// Ownership-acceptance hash after the first rotation, when `new_public_key()` is current. pub fn second_accept_ownership_hash() -> felt252 { 0x2ab9ca182c4cf47c5a03e1d9a614497b148d52814e8837f308fb0aa02355dc8 } -/// Acceptance signature for repeating the rotation while the demo key is current. +/// Acceptance signature for a same-key rotation while `new_public_key()` is current. pub fn second_accept_ownership_signature() -> Array { array![ 0xf6d56a03695a92ffe10775fa7611a000a51ff4f283bb2847933b6b46f5eea, @@ -193,3 +194,76 @@ pub fn second_accept_ownership_signature() -> Array { 0x141b32eb53245d40045054b63367, ] } + +/// SRC9 message hash for an empty outside execution addressed to `account_address()` with +/// `ANY_CALLER`, nonce 5, `execute_after` 10, and `execute_before` 20. Its SNIP-12 domain uses +/// name `Account.execute_from_outside`, version 2, chain ID `SN_SEPOLIA`, and revision 1. +pub fn outside_execution_hash() -> felt252 { + 0x6b92f58cb9070d5700b3dfbc62e82131a570bc1d2ca9133db3842f94eceeac4 +} + +/// SHAKE-256 hint signature over `outside_execution_hash()`, produced with the test private key +/// corresponding to `new_public_key()`. +pub fn outside_execution_signature() -> Array { + array![ + 0x4b4f151f45c3b0494a32916b674cdb2001f749ceb287d544d4eb1b423c612d5, + 0x669c2b57f4da0f10725003a59831704c7c74cb0c97465fd29ca1e7cfb402a, + 0x14bc9f29138bd2b5dd4955b8ba3fbe00004ca48190fc41d52930a646935f67, + 0x4b58b17f52f2ceb59b3c61c78d5100c000d056ba419eac52a06ea247229f1dc, + 0x19e784e2e4caa1e0ba283dca81f90011d4fb189e4ebce11970597abf6028, + 0x1e75077ddd44ba0b716b14ea2ff5604c1f74d4aa5074234290c8a2b36311b, + 0xd6bfb5f18d753edd248480e94ff5304cac84dead887afd07681a0517c11f9, + 0x4b9da03cfe36602cf75d985cc669f8804c1908d74e335bcdd37f45b7c241ee0, + 0x984043daabe592da7fc4e939f0053001bd8fe689974f62c4a9c8bbae5f008, + 0xab76b5c232aafc3429c166b7fefb2000903fdb022dd6bb5c491ae4aa30d03, + 0x4c2de4e714cf0c3bc91d71f101642830000b345550b0a64845ee5ada701fd04, + 0xa04f43ba95737f00106fb91a6015c04c5dfad25791a2c784b31b563c90f57, + 0x9eb444c7c0d1316c89d5acc49c285000f8628dd7b9bda83a35b4a959f3b8f, + 0x4c82d1fd95d2dcb0ec1a01fa7c0403d04ce3016739b647936d5e58df8bd30aa, + 0x4bff67c6cf029ed1d9543399390d06404cc152bd4f37628f3ac591c2c35d0d7, + 0x4bec22a3b29465fe1c3c7a5ebbd1f0900105422562575c128f10f7b1429c137, + 0x31ace09d8191e3824c24c682defd90000ffd1c96849f1479129c98943fdf7, + 0x14d53fc7239a11655b08b8d83cf9b04caae52163fa83d8f37f062d5e71dc5, + 0x4c45e9c4c151b8048e2b3a580af5f7f04c0dd22bd09e81b3f740f0b99acb261, + 0x44ef939b33c377045dc4b8809dade00115511f504c7a669f6cd0a9a1cd321, + 0x4c64505f931305097e6656b43dc910000034d80526b841d7a733b0aa2e87e04, + 0x4c9ade0086aa6a479ef11e7a8ecef680006e8ae19225179ec16434f5d239d8d, + 0x133a95f7e1f1a5e9ba02392266f7d04c8e122d8360fab80ce04b6893e6585, + 0x4bf5c25a4f6512d687f09043d70fe76001fa78db7dd9d5ecd2f84bf1c01d1d4, + 0x4c5123e95a855e6323e19a2d8a710ec04c9fae5c2333d3487add6f11b189e91, + 0x4c879d92c45dd6e62c5698fdade1e7e04b88b8b9612a15111c52e5cb61a3deb, + 0x4b58afe4be4ccce5be0a144ffed1c7800081d486817109a2fa988a0455edf0d, + 0x4b4f0bf92fd7940f8067e3c04b28dc504cc6251102450e734433651191d424a, + 0x1ab4cce57920b58f81e20b60db, 0xb80dead152bad04f674347ab1ab2316e0819c776, + 0xb10b59cd20ba7e6b932a53bb005a7c520a863dec, + 0x4b44c9a863df768c8359ec527d3c7490199a8e7920e4a78e24ddd509f7118a1, + 0x30a24369c757325e6f10fdba527c9450069d063f15798a3ac74907d2811f2a5, + 0x79694d4295f1990078644ca15c0f4303a871d9275157534407ddf2d7062bcf, + 0x3f7c4dd3711929129ac76e18d31e1b02185051a8ab79cbd6187cfd34724aab, + 0x2dd032e5419dcd93295c7c0c3a0107a008f09aafd5afd15ee3cc198c74f0165, + 0x16efbe27c5931f09de4f0afd81c2d1d014acf1e0b31a57c5e52cddc5d205912, + 0x34724daa7b50655841e43076be0653601ec0530a3ffba787e9627f7026b1c95, + 0x360a1d235d9da8dee21aae138bc935101ebe51741d2fb32b50046c3871aef9f, + 0x14a4b268d64bee657bfdaa57d16feb01a3af3aa0d5f729d4ab7d4ce2107c74, + 0x37c1924a346a648a07fd892877479ef04700ff9c31878fef065e5d1f9b19fb1, + 0x1cacc5fa22f1d916415a7a71c3132cd03be9dd39aee46d29d20d5c595767b5f, + 0x15fe178b816546d91e6f8ab2aa28cde04716fcff74bd578462ac0f2d1abd7c9, + 0x428055334432c3b4a0c68a8957f5459029a54fe83bc944df1ca0d078e50ae54, + 0x1d69ed931e5a94e384e07c2576fbeab03a39e3d2eb37ecd71f988bbebaf35f9, + 0x62edf675f00ec8a0cb8ee2b2120ebe0350bca5f7f14ecd7e12036d04d15052, + 0x15cf6f0b586cc06ac67945ef11576c0052ebaf911c2d525f0b17b5772d4ef8, + 0x20bb1217bc9d9bc929d8c3d650d98fd032b78dfd0f39a74fef9c564df3b6d6e, + 0x4306db5a13a7ec15fdb4ce63999fdd037590c842025460f903b70e208a92f5, + 0x4013552d954d5e2bbef1ed4d838d91804543e8c639e7b3f01fd7dbdf04345d2, + 0x4c6d90027346dd12b1e9bbf24ca59b3047b5778058b35edbab930d485fc662a, + 0x1eb6cabc8e08b00e90500dc103eb67301e72ad3aa33801e4231966a4ceb0484, + 0x443d6fa13abe7de7208a5ccde5b1e80042b5b027441297ede9a9aab33012d96, + 0x4198eea2930ce04241e14f446ee882a01563d42e6dae95f64bbc7b612011117, + 0xa136d1fdb5a1bbf4515709da15e0b200680ae20b27443a7bf0a485372f0fec, + 0x24150041bfbc26a21f6665ee16a042702091d6cc00cd2e924d2f23c1d6256e7, + 0x1885d40d392b1d6e838473a57ff705403488156122fb5faa95d10640e660f7b, + 0x45de57705f8580e6f94493d9a39de340442c117a13a255dc60e4f5b0018aacb, + 0x124081749e93c944284324c3e23b2b002a3186d4070e5863dc20aa85287e799, + 0x14355bbe3b117dc3152be52f77de, + ] +} diff --git a/packages/test_common/src/lib.cairo b/packages/test_common/src/lib.cairo index 56d8dac92..6adf07df4 100644 --- a/packages/test_common/src/lib.cairo +++ b/packages/test_common/src/lib.cairo @@ -5,6 +5,7 @@ pub mod erc20; pub mod erc6909; pub mod erc721; pub mod eth_account; +pub mod falcon_512; pub mod math; pub mod mocks; pub mod ownable; diff --git a/scripts/falcon_512/generate_ntt.py b/scripts/falcon_512/generate_ntt.py new file mode 100644 index 000000000..470f6f042 --- /dev/null +++ b/scripts/falcon_512/generate_ntt.py @@ -0,0 +1,583 @@ +#!/usr/bin/env python3 +"""Verify and generate the Falcon-512 NTT sources used by openzeppelin_account. + +The forward roots are derived from q = 12289 and sqrt(-1) = 1479. At each recursive +level, the canonical square root of every previous evaluation point is the smaller +representative of the pair ``r`` and ``q-r``. This fully determines the root order used +by the recursive Falcon transform. + +The executable checks cover the root construction, the iterative Cairo engine, inverse +and pointwise-product paths, and the fully unrolled production transform. The generated +operation graph is interval-checked to ensure that felt arithmetic does not wrap and that +every shifted output fits the u128 reduction path. + +Run without arguments to execute the checks. Pass ``--write`` to regenerate and format: + + packages/account/src/falcon_512/ntt/{bitrev,roots_felt,roots_scaled,falcon512_fast}.cairo +""" + +import argparse +import random +import subprocess +import tomllib +from dataclasses import dataclass +from pathlib import Path + + +Q = 12289 +SQR1 = 1479 +I2 = 6145 +STARK_PRIME = 2**251 + 17 * 2**192 + 1 +SIZES = [2, 4, 8, 16, 32, 64, 128, 256, 512] +QBITS = Q.bit_length() +FWD_GROWTH_BITS = (Q + 1).bit_length() +INV_GROWTH_BITS = (2 * Q).bit_length() +PRODUCT_BITS = 2 * QBITS +SAFE_BITS = 126 + +REPO_ROOT = Path(__file__).resolve().parents[2] +WORKSPACE_VERSION = tomllib.loads((REPO_ROOT / "Scarb.toml").read_text())["workspace"][ + "package" +]["version"] +NTT_DIR = REPO_ROOT / "packages/account/src/falcon_512/ntt" +BITREV_OUT = NTT_DIR / "bitrev.cairo" +ROOTS_FELT_OUT = NTT_DIR / "roots_felt.cairo" +ROOTS_SCALED_OUT = NTT_DIR / "roots_scaled.cairo" +FAST_OUT = NTT_DIR / "falcon512_fast.cairo" + + +def derive_forward_roots() -> dict[int, list[int]]: + """Derive the exact recursive merge-root tables for sizes 2 through 512.""" + canonical_sqrt = {x * x % Q: x for x in range((Q + 1) // 2)} + tables = {2: [SQR1]} + points = [SQR1, Q - SQR1] + for size in SIZES[1:]: + roots = [canonical_sqrt[point] for point in points] + tables[size] = roots + points = [value for root in roots for value in (root, Q - root)] + return tables + + +def derive_scaled_inverse_roots(tables: dict[int, list[int]]) -> dict[int, list[int]]: + return { + size: [I2 * pow(root, -1, Q) % Q for root in tables[size]] for size in SIZES + } + + +def evaluation_points(tables: dict[int, list[int]], size: int) -> list[int]: + return [value for root in tables[size] for value in (root, Q - root)] + + +def bitrev_perm(size: int) -> list[int]: + bits = size.bit_length() - 1 + return [int(f"{index:0{bits}b}"[::-1], 2) for index in range(size)] + + +def ntt_recursive(values: list[int], tables: dict[int, list[int]]) -> list[int]: + size = len(values) + if size == 2: + product = SQR1 * values[1] + return [(values[0] + product) % Q, (values[0] - product) % Q] + even = ntt_recursive(values[0::2], tables) + odd = ntt_recursive(values[1::2], tables) + output = [] + for left, right, root in zip(even, odd, tables[size]): + output.extend(((left + root * right) % Q, (left - root * right) % Q)) + return output + + +def intt_recursive(values: list[int], tables: dict[int, list[int]]) -> list[int]: + size = len(values) + if size == 2: + return [ + I2 * (values[0] + values[1]) % Q, + I2 * (values[0] - values[1]) * pow(SQR1, -1, Q) % Q, + ] + even = [] + odd = [] + for index, root in enumerate(tables[size]): + left, right = values[2 * index], values[2 * index + 1] + even.append(I2 * (left + right) % Q) + odd.append(I2 * (left - right) * pow(root, -1, Q) % Q) + even = intt_recursive(even, tables) + odd = intt_recursive(odd, tables) + return [coefficient for pair in zip(even, odd) for coefficient in pair] + + +class Stats: + def __init__(self) -> None: + self.max_value = 0 + + def see(self, values: list[int]) -> None: + self.max_value = max(self.max_value, max(values)) + + +def ntt_iterative(values: list[int], tables: dict[int, list[int]], stats: Stats) -> list[int]: + """Exact integer model of engine.cairo's forward transform and reduction schedule.""" + size = len(values) + current = [values[index] for index in bitrev_perm(size)] + bits = QBITS + bound = Q + half = 1 + level = 0 + while half != size: + if bits + FWD_GROWTH_BITS > SAFE_BITS: + current = [value % Q for value in current] + bits = QBITS + bound = Q + roots = tables[SIZES[level]] + offset = bound * Q + output = [] + for block in range(0, size, 2 * half): + left = current[block : block + half] + right = current[block + half : block + 2 * half] + for index, root in enumerate(roots): + product = root * right[index] + output.extend((left[index] + product, left[index] + offset - product)) + assert min(output) >= 0 + stats.see(output) + current = output + bits += FWD_GROWTH_BITS + bound *= Q + 1 + half *= 2 + level += 1 + return [value % Q for value in current] + + +def intt_iterative( + values: list[int], + tables: dict[int, list[int]], + stats: Stats, + input_bits: int = QBITS, + input_bound: int = Q, +) -> list[int]: + """Exact integer model of engine.cairo's inverse transform and reduction schedule.""" + size = len(values) + scaled = derive_scaled_inverse_roots(tables) + current = list(values) + bits = input_bits + bound = input_bound + half = size // 2 + level = size.bit_length() - 2 + while True: + if bits + INV_GROWTH_BITS > SAFE_BITS: + current = [value % Q for value in current] + bits = QBITS + bound = Q + roots = scaled[SIZES[level]] + offset = bound * Q + output = [] + for block in range(0, size, 2 * half): + odd = [] + for index, root in enumerate(roots): + left = current[block + 2 * index] + right = current[block + 2 * index + 1] + output.append(I2 * (left + right)) + odd.append(root * left + offset - root * right) + output.extend(odd) + assert min(output) >= 0 + stats.see(output) + current = output + bits += INV_GROWTH_BITS + bound *= 2 * Q + if half == 1: + break + half //= 2 + level -= 1 + return [current[index] % Q for index in bitrev_perm(size)] + + +class ConcreteOps: + @staticmethod + def mul_const(value: int, constant: int) -> int: + return value * constant + + @staticmethod + def add(left: int, right: int) -> int: + return left + right + + @staticmethod + def sub(left: int, right: int) -> int: + return left - right + + +@dataclass(frozen=True) +class CairoValue: + name: str + low: int + high: int + + +class CairoOps: + """Record straight-line Cairo operations together with exact inclusive intervals.""" + + def __init__(self) -> None: + self.lines: list[str] = [] + self.next_id = 0 + self.low = 0 + self.high = Q - 1 + + def record(self, expression: str, low: int, high: int) -> CairoValue: + name = f"v{self.next_id}" + self.next_id += 1 + self.lines.append(f" let {name} = {expression};") + self.low = min(self.low, low) + self.high = max(self.high, high) + return CairoValue(name, low, high) + + def mul_const(self, value: CairoValue, constant: int) -> CairoValue: + return self.record( + f"{value.name} * {constant}", value.low * constant, value.high * constant + ) + + def add(self, left: CairoValue, right: CairoValue) -> CairoValue: + return self.record( + f"{left.name} + {right.name}", left.low + right.low, left.high + right.high + ) + + def sub(self, left: CairoValue, right: CairoValue) -> CairoValue: + return self.record( + f"{left.name} - {right.name}", left.low - right.high, left.high - right.low + ) + + +def ntt_unrolled(values, tables: dict[int, list[int]], ops): + """Build the recursive NTT operation graph without intermediate modular reductions.""" + size = len(values) + if size == 2: + product = ops.mul_const(values[1], SQR1) + return [ops.add(values[0], product), ops.sub(values[0], product)] + even = ntt_unrolled(values[0::2], tables, ops) + odd = ntt_unrolled(values[1::2], tables, ops) + output = [] + for left, right, root in zip(even, odd, tables[size]): + product = ops.mul_const(right, root) + output.extend((ops.add(left, product), ops.sub(left, product))) + return output + + +def shift_for_bound(low: int) -> int: + return 0 if low >= 0 else ((-low + Q - 1) // Q) * Q + + +def sample_inputs(size: int, rng: random.Random) -> list[list[int]]: + return [ + [0] * size, + [Q - 1] * size, + [1] + [0] * (size - 1), + list(range(size)), + *[[rng.randrange(Q) for _ in range(size)] for _ in range(3)], + ] + + +def verify(tables: dict[int, list[int]]) -> tuple[CairoOps, list[CairoValue], int]: + assert SQR1 * SQR1 % Q == Q - 1 + assert I2 * 2 % Q == 1 + + for size in SIZES: + points = evaluation_points(tables, size) + assert len(points) == size + assert len(set(points)) == size + assert all(pow(point, size, Q) == Q - 1 for point in points) + + rng = random.Random(Q) + stats = Stats() + for size in SIZES[1:]: + for values in sample_inputs(size, rng): + expected = ntt_recursive(values, tables) + assert ntt_iterative(values, tables, stats) == expected + assert intt_recursive(expected, tables) == [value % Q for value in values] + assert intt_iterative(expected, tables, stats) == [value % Q for value in values] + + for _ in range(3): + left = [rng.randrange(Q) for _ in range(512)] + right = [rng.randrange(Q) for _ in range(512)] + left_ntt = ntt_recursive(left, tables) + right_ntt = ntt_recursive(right, tables) + products = [a * b for a, b in zip(left_ntt, right_ntt)] + expected = intt_recursive([value % Q for value in products], tables) + assert ( + intt_iterative( + products, + tables, + stats, + input_bits=PRODUCT_BITS, + input_bound=Q * Q, + ) + == expected + ) + + points = evaluation_points(tables, 512) + for basis_index in range(512): + basis = [0] * 512 + basis[basis_index] = 1 + expected = [pow(point, basis_index, Q) for point in points] + raw = ntt_unrolled(basis, tables, ConcreteOps()) + assert [value % Q for value in raw] == expected + + trace = CairoOps() + inputs = [CairoValue(f"f{index}", 0, Q - 1) for index in range(512)] + outputs = ntt_unrolled(inputs, tables, trace) + shift = shift_for_bound(trace.low) + assert shift % Q == 0 + assert min(output.low for output in outputs) + shift >= 0 + assert max(output.high for output in outputs) + shift < 2**128 + assert max(abs(trace.low), trace.high) < STARK_PRIME + assert stats.max_value < 2**SAFE_BITS + + print("verified deterministic roots and distinct negacyclic evaluation points") + print("verified iterative NTT, INTT, and unreduced product paths for sizes 4..512") + print("verified all 512 basis vectors through the generated fast operation graph") + print( + f"verified {trace.next_id} generated operations; shifted outputs fit " + f"{(max(output.high for output in outputs) + shift).bit_length()} bits" + ) + return trace, outputs, shift + + +def generated_header(path: Path, description: list[str]) -> str: + relative = path.relative_to(REPO_ROOT).as_posix() + lines = [ + "// SPDX-License-Identifier: MIT", + f"// OpenZeppelin Contracts for Cairo v{WORKSPACE_VERSION} " + f"({relative.removeprefix('packages/')})", + "", + "// Generated by scripts/falcon_512/generate_ntt.py; do not edit by hand.", + *[f"// {line}" for line in description], + "// Regenerate with: python3 scripts/falcon_512/generate_ntt.py --write", + "", + ] + return "\n".join(lines) + "\n" + + +def format_table(name: str, values: list[int], value_type: str) -> str: + body = ", ".join(str(value) for value in values) + return f"const {name}: [{value_type}; {len(values)}] = [{body}];\n" + + +def emit_dispatch(function_name: str, doc: str, names: dict[int, str]) -> str: + branches = [] + for size in SIZES: + branches.append(f" if degree == {size} {{\n {names[size]}.span()\n }}") + return ( + f"\n{doc}pub fn {function_name}(degree: u32) -> Span {{\n" + + " else ".join(branches) + + ' else {\n panic!("no root table for degree")\n }\n}\n' + ) + + +def render_roots_felt(tables: dict[int, list[int]]) -> str: + output = [ + generated_header( + ROOTS_FELT_OUT, + [ + "Forward merge roots derived recursively from q = 12289 and sqrt(-1) = 1479,", + "choosing the smaller representative of each modular square-root pair.", + ], + ), + "//! Forward NTT root tables as felts (generated).\n\n", + ] + names = {} + for size in SIZES: + name = f"phi{2 * size}_roots_zq_felt" + names[size] = name + output.append(format_table(name, tables[size], "felt252")) + output.append( + emit_dispatch( + "get_even_roots_felt", + "/// Forward merge roots for a power-of-two transform degree from 2 through 512.\n", + names, + ) + ) + return "".join(output) + + +def render_roots_scaled(tables: dict[int, list[int]]) -> str: + scaled = derive_scaled_inverse_roots(tables) + output = [ + generated_header( + ROOTS_SCALED_OUT, + [ + "Inverse roots derived from the deterministic forward roots and prescaled by", + "I2 = 2^-1 mod q for the inverse transform's split butterflies.", + ], + ), + "//! I2-scaled inverse NTT root tables (generated).\n\n", + ] + names = {} + for size in SIZES: + name = f"phi{2 * size}_roots_zq_inv_scaled" + names[size] = name + output.append(format_table(name, scaled[size], "felt252")) + output.append( + emit_dispatch( + "get_scaled_inv_roots", + "/// I2-prescaled inverse roots for a power-of-two transform degree from 2 through " + "512.\n", + names, + ) + ) + return "".join(output) + + +def render_bitrev() -> str: + return "".join( + [ + generated_header( + BITREV_OUT, + [ + "Self-inverse bit-reversal permutation for n = 512, used to map between", + "natural coefficient order and the recursive transform's leaf order.", + ], + ), + "//! Bit-reversal permutation table (generated).\n\n", + format_table("BITREV_512", bitrev_perm(512), "u16").replace( + "const", "pub(crate) const", 1 + ), + "\n/// The 512-entry bit-reversal permutation as a span.\n", + "pub fn bitrev_512() -> Span {\n BITREV_512.span()\n}\n", + ] + ) + + +def wrapped(items: list[str], indent: str = " ", width: int = 100) -> str: + lines = [] + current = indent + for item in items: + token = f"{item}," + if len(current) + len(token) + 1 > width and current.strip(): + lines.append(current.rstrip()) + current = indent + token + " " + else: + current += token + " " + if current.strip(): + lines.append(current.rstrip()) + return "\n".join(lines) + + +def render_fast( + trace: CairoOps, outputs: list[CairoValue], shift: int +) -> str: + input_params = [f"f{index}: felt252" for index in range(512)] + input_names = [f"f{index}" for index in range(512)] + reduced_names = [f"r{index}" for index in range(512)] + return_type = "(" + ", ".join("Falcon512Zq" for _ in outputs) + ")" + output = [ + generated_header( + FAST_OUT, + [ + "Fully unrolled Falcon-512 forward NTT for n = 512 and q = 12289.", + "The generator checks exact integer bounds and reduces each output once.", + ], + ), + "//! Generated Falcon-512 forward NTT.\n//!\n", + "//! Inputs are 512 coefficients in `[0, 12289)`. The unchecked entry points rely\n", + "//! on callers to enforce that precondition. Outputs are reduced to the same range.\n\n", + "use openzeppelin_corelib_imports::bounded_int::{\n", + " BoundedInt, DivRemHelper, UnitInt, bounded_int_div_rem, upcast,\n", + "};\n", + "use openzeppelin_corelib_imports::integer::{\n", + " U128sFromFelt252Result, u128s_from_felt252,\n", + "};\n\n", + "type Falcon512Zq = BoundedInt<0, 12288>;\n", + "type Falcon512Q = UnitInt<12289>;\n", + "type U128AsBounded = BoundedInt<0, 340282366920938463463374607431768211455>;\n\n", + "const FALCON512_Q_NZ: NonZero = 12289;\n", + f"const SHIFT: felt252 = {shift};\n\n", + "impl Falcon512FastDivRemImpl of DivRemHelper {\n", + " type DivT = BoundedInt<0, 27689996494502275487295516920153650>;\n", + " type RemT = Falcon512Zq;\n", + "}\n\n", + "#[inline(always)]\n", + "fn felt252_as_u128(value: felt252) -> u128 {\n", + " // Exact generated bounds put canonical shifted outputs below 2^128.\n", + " match u128s_from_felt252(value) {\n", + " U128sFromFelt252Result::Narrow(low) => low,\n", + " U128sFromFelt252Result::Wide((_, low)) => low,\n", + " }\n", + "}\n\n", + "#[inline(always)]\n", + "fn ntt_falcon512_fast_inner(\n", + wrapped(input_params), + f"\n) -> {return_type} {{\n", + "\n".join(trace.lines), + "\n", + ] + for value, name in zip(outputs, reduced_names): + output.extend( + [ + f" let {name}_bounded: U128AsBounded = ", + f"upcast(felt252_as_u128({value.name} + SHIFT));\n", + f" let (_, {name}) = bounded_int_div_rem({name}_bounded, FALCON512_Q_NZ);\n", + ] + ) + output.extend([" (\n", wrapped(reduced_names, " "), "\n )\n}\n\n"]) + output.extend( + [ + "/// Test-only felt wrapper for the generated Falcon-512 forward NTT.\n", + "#[cfg(test)]\n", + "pub fn ntt_falcon512_fast_unchecked(mut f: Span) -> Array {\n", + " assert(f.len() == 512, 'fast NTT: bad length');\n", + " let boxed = f.multi_pop_front::<512>().unwrap();\n", + " let [\n", + wrapped(input_names, " "), + "\n ] = boxed.unbox();\n", + " let (\n", + wrapped(reduced_names, " "), + "\n ) = ntt_falcon512_fast_inner(\n", + wrapped(input_names, " "), + "\n );\n", + " array![\n", + wrapped([f"upcast({name})" for name in reduced_names], " "), + "\n ]\n", + "}\n\n", + "/// Computes the Falcon-512 forward NTT from canonical `u16` coefficients.\n", + "///\n", + "/// Inputs must be in `[0, 12289)`; violating that precondition may return an\n", + "/// invalid transform. Outputs are reduced to the same range.\n", + "pub fn ntt_falcon512_fast_u16_unchecked(mut f: Span) -> Array {\n", + " assert(f.len() == 512, 'fast NTT: bad length');\n", + " let boxed = f.multi_pop_front::<512>().unwrap();\n", + " let [\n", + wrapped(input_names, " "), + "\n ] = boxed.unbox();\n", + " let (\n", + wrapped(reduced_names, " "), + "\n ) = ntt_falcon512_fast_inner(\n", + wrapped([f"{name}.into()" for name in input_names], " "), + "\n );\n", + " array![\n", + wrapped([f"upcast({name})" for name in reduced_names], " "), + "\n ]\n", + "}\n", + ] + ) + return "".join(output) + + +def write_generated(tables: dict[int, list[int]], trace, outputs, shift: int) -> None: + rendered = { + BITREV_OUT: render_bitrev(), + ROOTS_FELT_OUT: render_roots_felt(tables), + ROOTS_SCALED_OUT: render_roots_scaled(tables), + FAST_OUT: render_fast(trace, outputs, shift), + } + for path, source in rendered.items(): + path.write_text(source) + subprocess.run(["scarb", "fmt", str(path)], cwd=REPO_ROOT, check=True) + print(f"wrote {path.relative_to(REPO_ROOT)}") + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--write", action="store_true", help="regenerate the Cairo sources") + args = parser.parse_args() + + tables = derive_forward_roots() + trace, outputs, shift = verify(tables) + if args.write: + write_generated(tables, trace, outputs, shift) + + +if __name__ == "__main__": + main() diff --git a/scripts/get_hashes_page.py b/scripts/get_hashes_page.py index 593e79704..214e580c5 100644 --- a/scripts/get_hashes_page.py +++ b/scripts/get_hashes_page.py @@ -7,7 +7,9 @@ "ERC1155Upgradeable", "AccountUpgradeable", "EthAccountUpgradeable", - "UniversalDeployer" + "Falcon512ShakeAccountUpgradeable", + "Falcon512ShakeDirectAccountUpgradeable", + "UniversalDeployer", ] def main(): From ceea8d67aaef6db975142b9b4aefe38f2d24246e Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Mon, 10 Aug 2026 16:40:22 +0200 Subject: [PATCH 4/6] feat: apply review updates --- .../ROOT/pages/utils/_class_hashes.adoc | 4 +- .../src/falcon_512/ntt/falcon512.cairo | 1 + .../src/falcon_512/verifier_impls.cairo | 49 ++++++++++++------- .../src/tests/test_falcon_512_ntt.cairo | 28 +++++++++-- .../test_common/src/falcon_512/fixture.cairo | 2 +- 5 files changed, 58 insertions(+), 26 deletions(-) diff --git a/docs/modules/ROOT/pages/utils/_class_hashes.adoc b/docs/modules/ROOT/pages/utils/_class_hashes.adoc index 2d8fa2315..50b89b059 100644 --- a/docs/modules/ROOT/pages/utils/_class_hashes.adoc +++ b/docs/modules/ROOT/pages/utils/_class_hashes.adoc @@ -7,8 +7,8 @@ :ERC1155Upgradeable-class-hash: 0x075829cba5eea49a998988967e6a97de68824830118f7f008f3ac07d407cb99a :AccountUpgradeable-class-hash: 0x03013bf2edcf93ac575367eee41cd07bb50e809c78925817b61878db29826144 :EthAccountUpgradeable-class-hash: 0x000be1cf8839442d664572712fd36554c4d259f86549ad02e22353d10729b900 -:Falcon512ShakeAccountUpgradeable-class-hash: 0x00a6a8a5766d093d4f21c6f913417b03f85427438833149e3ca34f60fff8de4c -:Falcon512ShakeDirectAccountUpgradeable-class-hash: 0x056159818224483320514f4d5fbfb1e2e374cf50a4a97175b6ad18504837c3a9 +:Falcon512ShakeAccountUpgradeable-class-hash: 0x0737b72596a5aa02fb18c77615eca82cbf47988a22cf42e6b44280c8ef0b89df +:Falcon512ShakeDirectAccountUpgradeable-class-hash: 0x0734b642ac6247e1a747b46e86c2f443bd9b00879683b2243b932a0dbf3f3a99 :UniversalDeployer-class-hash: 0x05412c7a3e145fdc79862e65df612324d83b0169c071dd564021155bd8cfd29e :MetaTransactionV0-class-hash: 0x059b5a7a062db4a9fa1ec4b96ea74cac3d55477971d68ac9fba877c087e59808 :VestingWallet-class-hash: 0x05613fb6c8f729fdcbef50d25c9dd72260762543d48254c59e6851dd3d46595a diff --git a/packages/account/src/falcon_512/ntt/falcon512.cairo b/packages/account/src/falcon_512/ntt/falcon512.cairo index 87d59b6e7..328ab8865 100644 --- a/packages/account/src/falcon_512/ntt/falcon512.cairo +++ b/packages/account/src/falcon_512/ntt/falcon512.cairo @@ -33,6 +33,7 @@ pub fn config() -> NttConfig { /// bit-reversal permutation programmatically. `levels` must equal `log2(n)`. Tests and auxiliary /// tooling use this function; production callers use [`config`], which reads the pinned table. pub fn config_for_degree(n: u32, levels: u32) -> NttConfig { + assert!(n >= 4 && n <= 512 && (n & (n - 1)) == 0, "config_for_degree: unsupported degree {n}"); let mut perm: Array = array![]; let mut i: u32 = 0; while i != n { diff --git a/packages/account/src/falcon_512/verifier_impls.cairo b/packages/account/src/falcon_512/verifier_impls.cairo index bf3a47fe3..493ace9b9 100644 --- a/packages/account/src/falcon_512/verifier_impls.cairo +++ b/packages/account/src/falcon_512/verifier_impls.cairo @@ -5,14 +5,34 @@ use super::hashing::hash_to_point::hash_to_point_shake_512; use super::verifier::Falcon512SignatureVerifier; use super::{falcon, packing}; +/// Number of felts containing the Falcon salt. +const SALT_FELTS: u32 = 2; + /// Number of felts in a packed Falcon-512 public key. -pub(crate) const PUBLIC_KEY_FELTS: u32 = 29; +pub(crate) const PUBLIC_KEY_FELTS: u32 = packing::PACKED_SLOTS; + +/// Number of felts in a direct Falcon-512 signature. +pub(crate) const DIRECT_SIGNATURE_FELTS: u32 = PUBLIC_KEY_FELTS + SALT_FELTS; /// Number of felts in a Falcon-512 signature carrying a product hint. -pub(crate) const SIGNATURE_FELTS: u32 = 60; +pub(crate) const SIGNATURE_FELTS: u32 = DIRECT_SIGNATURE_FELTS + PUBLIC_KEY_FELTS; -/// Number of felts in a direct Falcon-512 signature. -pub(crate) const DIRECT_SIGNATURE_FELTS: u32 = 31; +/// Decodes the public key, signature polynomial, and salt shared by both layouts. +fn decode_common( + public_key: Span, signature: Span, +) -> Option<(Array, Array, felt252, felt252)> { + let h_ntt = match packing::unpack_512_u16(public_key) { + Some(value) => value, + None => { return None; }, + }; + let s1 = match packing::unpack_512_u16(signature.slice(0, PUBLIC_KEY_FELTS)) { + Some(value) => value, + None => { return None; }, + }; + let salt_a = *signature.at(PUBLIC_KEY_FELTS); + let salt_b = *signature.at(PUBLIC_KEY_FELTS + 1); + Some((h_ntt, s1, salt_a, salt_b)) +} /// Verifier for the 60-felt SHAKE-256 signature carrying a polynomial-product hint. pub impl Falcon512ShakeVerifier of Falcon512SignatureVerifier { @@ -20,17 +40,14 @@ pub impl Falcon512ShakeVerifier of Falcon512SignatureVerifier { if public_key.len() != PUBLIC_KEY_FELTS || signature.len() != SIGNATURE_FELTS { return false; } - let h_ntt = match packing::unpack_512_u16(public_key) { + let (h_ntt, s1, salt_a, salt_b) = match decode_common(public_key, signature) { Some(value) => value, None => { return false; }, }; - let s1 = match packing::unpack_512_u16(signature.slice(0, 29)) { - Some(value) => value, - None => { return false; }, - }; - let salt_a = *signature.at(29); - let salt_b = *signature.at(30); - let mul_hint = match packing::unpack_512_u16(signature.slice(31, 29)) { + let mul_hint = + match packing::unpack_512_u16( + signature.slice(DIRECT_SIGNATURE_FELTS, PUBLIC_KEY_FELTS), + ) { Some(value) => value, None => { return false; }, }; @@ -60,16 +77,10 @@ pub impl Falcon512ShakeDirectVerifier of Falcon512SignatureVerifier { if public_key.len() != PUBLIC_KEY_FELTS || signature.len() != DIRECT_SIGNATURE_FELTS { return false; } - let h_ntt = match packing::unpack_512_u16(public_key) { - Some(value) => value, - None => { return false; }, - }; - let s1 = match packing::unpack_512_u16(signature.slice(0, 29)) { + let (h_ntt, s1, salt_a, salt_b) = match decode_common(public_key, signature) { Some(value) => value, None => { return false; }, }; - let salt_a = *signature.at(29); - let salt_b = *signature.at(30); let message_point = match hash_to_point_shake_512(message_hash, salt_a, salt_b) { Some(value) => value, None => { return false; }, diff --git a/packages/account/src/tests/test_falcon_512_ntt.cairo b/packages/account/src/tests/test_falcon_512_ntt.cairo index 8d6ac5a0a..1e9554978 100644 --- a/packages/account/src/tests/test_falcon_512_ntt.cairo +++ b/packages/account/src/tests/test_falcon_512_ntt.cairo @@ -149,19 +149,39 @@ fn test_degree_four_roundtrip_exercises_reduction_tail() { #[test] fn test_intt_reduces_before_last_level() { - let values = array![0, 0, 0, 0]; - let recovered = intt(values.span(), 111, 1, @config_for_degree(4, 2)); + let values = array![1, 2, 3, 4]; + let cfg = config_for_degree(4, 2); + let transformed = ntt(values.span(), @cfg); + let recovered = intt(transformed.span(), 111, 12289, @cfg); assert_eq!(recovered.span(), values.span()); } #[test] -#[should_panic] +#[should_panic(expected: "config_for_degree: unsupported degree 2")] +fn test_config_rejects_degree_below_range() { + config_for_degree(2, 1); +} + +#[test] +#[should_panic(expected: "config_for_degree: unsupported degree 6")] +fn test_config_rejects_non_power_of_two_degree() { + config_for_degree(6, 3); +} + +#[test] +#[should_panic(expected: "config_for_degree: unsupported degree 1024")] +fn test_config_rejects_degree_above_range() { + config_for_degree(1024, 10); +} + +#[test] +#[should_panic(expected: "no root table for degree")] fn test_forward_roots_reject_unsupported_degree() { get_even_roots_felt(3); } #[test] -#[should_panic] +#[should_panic(expected: "no root table for degree")] fn test_inverse_roots_reject_unsupported_degree() { get_scaled_inv_roots(3); } diff --git a/packages/test_common/src/falcon_512/fixture.cairo b/packages/test_common/src/falcon_512/fixture.cairo index dd79dbbd0..1f478f1a8 100644 --- a/packages/test_common/src/falcon_512/fixture.cairo +++ b/packages/test_common/src/falcon_512/fixture.cairo @@ -5,7 +5,7 @@ //! encoding. This is not an FN-DSA (FIPS 206) conformance vector. //! Salt: `0x4ad23197d576f705594ac172f4fca80e3338e991b8b55e269d421aa1522cd0aa2490034127651fc2`. -/// Fixed reference message hash used by this fixture. +/// Fixed reference message felt (`'BENCH_MSG'`) used by this fixture. pub fn msg() -> felt252 { 1222479111879746474823 } From b266b9e1ad274d8e7871a9ab1713995cb3856ab4 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Tue, 18 Aug 2026 14:33:53 +0200 Subject: [PATCH 5/6] feat: apply review updates --- packages/account/src/falcon_512/account.cairo | 4 +++ .../account/src/falcon_512/ntt/engine.cairo | 1 + .../src/falcon_512/ntt/falcon512.cairo | 6 ++-- packages/account/src/falcon_512/packing.cairo | 3 +- .../account/src/tests/test_falcon_512.cairo | 35 ++++++++++++------- 5 files changed, 32 insertions(+), 17 deletions(-) diff --git a/packages/account/src/falcon_512/account.cairo b/packages/account/src/falcon_512/account.cairo index 9a8746052..767bb8cc4 100644 --- a/packages/account/src/falcon_512/account.cairo +++ b/packages/account/src/falcon_512/account.cairo @@ -78,6 +78,8 @@ pub mod Falcon512AccountComponent { /// - If the transaction is a simulation (version >= `QUERY_OFFSET`), it must be /// greater than or equal to `QUERY_OFFSET` + `MIN_TRANSACTION_VERSION`. fn __execute__(self: @ComponentState, calls: Array) { + // Avoid calls from other contracts + // https://github.com/OpenZeppelin/cairo-contracts/issues/344 let sender = starknet::get_caller_address(); assert(sender.is_zero(), Errors::INVALID_CALLER); assert(is_tx_version_valid(), Errors::INVALID_TX_VERSION); @@ -378,6 +380,8 @@ pub mod Falcon512AccountComponent { } /// Stores `new_public_key` and emits an `OwnerAdded` event. + /// Once initialized, the stored key length is fixed; subsequent calls must provide a key + /// with the same number of felts. /// Callers are responsible for enforcing the applicable encoding and authorization checks. fn _set_public_key( ref self: ComponentState, new_public_key: Array, diff --git a/packages/account/src/falcon_512/ntt/engine.cairo b/packages/account/src/falcon_512/ntt/engine.cairo index 8a12b0426..068dbf00b 100644 --- a/packages/account/src/falcon_512/ntt/engine.cairo +++ b/packages/account/src/falcon_512/ntt/engine.cairo @@ -332,6 +332,7 @@ pub fn intt( if bits + growth_bits > SAFE_BITS { cur = reduce_pass(cur.span(), q_nz); bound = q_felt; + // No later growth decision reads `bits`; the final output is reduced unconditionally. } let t0 = *(*split_roots.at(0)).at(0); let off = bound * q_felt; diff --git a/packages/account/src/falcon_512/ntt/falcon512.cairo b/packages/account/src/falcon_512/ntt/falcon512.cairo index 328ab8865..108761956 100644 --- a/packages/account/src/falcon_512/ntt/falcon512.cairo +++ b/packages/account/src/falcon_512/ntt/falcon512.cairo @@ -10,8 +10,8 @@ use super::engine::NttConfig; use super::roots_felt::get_even_roots_felt; use super::roots_scaled::get_scaled_inv_roots; -/// The Falcon modulus q = 12289 = 12·1024 + 1. -pub const Q: u16 = 12289; +/// The modulus as a non-zero `u128` divisor. +const Q_NZ: NonZero = 12289; /// q as a felt. pub const Q_FELT: felt252 = 12289; /// 2^-1 mod q. @@ -68,7 +68,7 @@ fn config_with_perm(n: u32, levels: u32, perm: Span) -> NttConfig { NttConfig { n, levels, - q_nz: 12289, + q_nz: Q_NZ, q_felt: Q_FELT, i2_felt: I2_FELT, qbits: REDUCED_BITS, diff --git a/packages/account/src/falcon_512/packing.cairo b/packages/account/src/falcon_512/packing.cairo index 2f83218bd..6f8411df8 100644 --- a/packages/account/src/falcon_512/packing.cairo +++ b/packages/account/src/falcon_512/packing.cairo @@ -18,6 +18,7 @@ use openzeppelin_corelib_imports::bounded_int::{ use super::zq::Q; /// Coefficients per full `felt252` slot (two `u128` halves of 9). +#[cfg(test)] pub const VALS_PER_FELT: u32 = 18; /// `felt252` slots for 512 coefficients: 28 full slots + 1 slot with 8. pub const PACKED_SLOTS: u32 = 29; @@ -210,7 +211,7 @@ fn pack_half(coeffs: Span) -> u128 { i -= 1; let c = *coeffs.at(i); assert(c < Q, 'pack: coeff >= Q'); - acc = acc * 12289 + c.into(); + acc = acc * Q.into() + c.into(); } acc } diff --git a/packages/account/src/tests/test_falcon_512.cairo b/packages/account/src/tests/test_falcon_512.cairo index 3f8d91ce7..fa7ae2ac5 100644 --- a/packages/account/src/tests/test_falcon_512.cairo +++ b/packages/account/src/tests/test_falcon_512.cairo @@ -18,9 +18,9 @@ use openzeppelin_testing::constants::{ }; use openzeppelin_testing::{EventSpyExt, ExpectedEvent, spy_events}; use snforge_std::{ - CheatSpan, cheat_caller_address, start_cheat_caller_address, start_cheat_signature_global, - start_cheat_transaction_hash_global, start_cheat_transaction_version_global, - stop_cheat_caller_address, + CheatSpan, ContractClassTrait, cheat_caller_address, start_cheat_caller_address, + start_cheat_signature_global, start_cheat_transaction_hash_global, + start_cheat_transaction_version_global, stop_cheat_caller_address, }; use starknet::ContractAddress; use starknet::account::Call; @@ -95,6 +95,19 @@ fn deploy_account_at( FeltArrayAccountABIDispatcher { contract_address } } +fn assert_deploy_rejects_public_key(contract_name: ByteArray, public_key: Array) { + let contract_class = utils::declare_class(contract_name); + let mut calldata = array![]; + public_key.serialize(ref calldata); + + match contract_class.deploy(@calldata) { + Result::Ok(_) => panic!("Expected invalid public key"), + Result::Err(panic_data) => { + assert_eq!(panic_data, array!['Falcon512: invalid public key']); + }, + } +} + fn execute_key_rotation( account: FeltArrayAccountABIDispatcher, new_key: Array, @@ -575,21 +588,17 @@ fn test_validate_deploy_rejects_invalid_signature() { } #[test] -#[should_panic] fn test_constructor_rejects_wrong_public_key_length() { - let contract_class = utils::declare_class("Falcon512ShakeAccountMock"); - let mut calldata = array![]; - copy_prefix(public_key().span(), PUBLIC_KEY_FELTS - 1).serialize(ref calldata); - utils::deploy(contract_class, calldata); + assert_deploy_rejects_public_key( + "Falcon512ShakeAccountMock", copy_prefix(public_key().span(), PUBLIC_KEY_FELTS - 1), + ); } #[test] -#[should_panic] fn test_constructor_rejects_noncanonical_public_key() { - let contract_class = utils::declare_class("Falcon512ShakeDirectAccountMock"); - let mut calldata = array![]; - with_replaced(public_key().span(), 0, Q_POW_9).serialize(ref calldata); - utils::deploy(contract_class, calldata); + assert_deploy_rejects_public_key( + "Falcon512ShakeDirectAccountMock", with_replaced(public_key().span(), 0, Q_POW_9), + ); } #[test] From 53aa1eefea36d086dedf0486304c816d814de19f Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Tue, 18 Aug 2026 15:20:06 +0200 Subject: [PATCH 6/6] feat: remove docs references --- docs/modules/ROOT/pages/api/account.adoc | 478 ------------------ docs/modules/ROOT/pages/presets.adoc | 12 - .../ROOT/pages/utils/_class_hashes.adoc | 21 +- scripts/get_hashes_page.py | 4 +- 4 files changed, 11 insertions(+), 504 deletions(-) diff --git a/docs/modules/ROOT/pages/api/account.adoc b/docs/modules/ROOT/pages/api/account.adoc index e34f55729..941515eda 100644 --- a/docs/modules/ROOT/pages/api/account.adoc +++ b/docs/modules/ROOT/pages/api/account.adoc @@ -66,87 +66,6 @@ Validates whether a signature is valid or not for the given message hash. Returns the short string `'VALID'` if valid, otherwise it reverts. -[.contract] -[[IFeltArrayDeployable]] -=== `++IFeltArrayDeployable++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v4.0.0-alpha.1/packages/interfaces/src/account/accounts.cairo[{github-icon},role=heading-link] - -```cairo -use openzeppelin_interfaces::accounts::IFeltArrayDeployable; -``` - -Deployment validation interface for accounts whose public key is encoded as an array of felts. - -[.contract-index] -.Functions --- -* xref:#IFeltArrayDeployable-\\__validate_deploy__[`++__validate_deploy__(class_hash, contract_address_salt, public_key)++`] --- - -[#IFeltArrayDeployable-Functions] -==== Functions - -[.contract-item] -[[IFeltArrayDeployable-__validate_deploy__]] -==== `[.contract-item-name]#++__validate_deploy__++#++(class_hash: felt252, contract_address_salt: felt252, public_key: Array) → felt252++` [.item-kind]#external# - -Validates a deploy-account transaction. - -This function is used by the protocol to verify `deploy_account` transactions. - -Returns the short string `'VALID'` if valid, otherwise it reverts. - -[.contract] -[[IFeltArrayPublicKey]] -=== `++IFeltArrayPublicKey++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v4.0.0-alpha.1/packages/interfaces/src/account/accounts.cairo[{github-icon},role=heading-link] - -```cairo -use openzeppelin_interfaces::accounts::IFeltArrayPublicKey; -``` - -Public-key management interface for accounts whose key is encoded as an array of felts. - -[.contract-index] -.Functions --- -* xref:#IFeltArrayPublicKey-get_public_key[`++get_public_key()++`] -* xref:#IFeltArrayPublicKey-set_public_key[`++set_public_key(new_public_key, signature)++`] --- - -[#IFeltArrayPublicKey-Functions] -==== Functions - -[.contract-item] -[[IFeltArrayPublicKey-get_public_key]] -==== `[.contract-item-name]#++get_public_key++#++() → Array++` [.item-kind]#external# - -Returns the account's current public key. - -[.contract-item] -[[IFeltArrayPublicKey-set_public_key]] -==== `[.contract-item-name]#++set_public_key++#++(new_public_key: Array, signature: Span)++` [.item-kind]#external# - -Sets the public key of the account to `new_public_key`. - -[.contract] -[[IFeltArrayPublicKeyCamel]] -=== `++IFeltArrayPublicKeyCamel++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v4.0.0-alpha.1/packages/interfaces/src/account/accounts.cairo[{github-icon},role=heading-link] - -```cairo -use openzeppelin_interfaces::accounts::IFeltArrayPublicKeyCamel; -``` - -Camel-case aliases `getPublicKey` and `setPublicKey` for xref:IFeltArrayPublicKey[`IFeltArrayPublicKey`]. - -[.contract] -[[FeltArrayAccountABI]] -=== `++FeltArrayAccountABI++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v4.0.0-alpha.1/packages/interfaces/src/account/accounts.cairo[{github-icon},role=heading-link] - -```cairo -use openzeppelin_interfaces::accounts::FeltArrayAccountABI; -``` - -Aggregate ABI trait combining `ISRC6`, `ISRC5`, `IDeclarer`, xref:IFeltArrayDeployable[`IFeltArrayDeployable`], xref:IFeltArrayPublicKey[`IFeltArrayPublicKey`], and their camel-case aliases. - [.contract] [[ISRC9_V2]] === `++ISRC9_V2++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v4.0.0-alpha.1/packages/interfaces/src/account/src9.cairo[{github-icon},role=heading-link] @@ -702,267 +621,6 @@ Emitted when a `public_key` is added. Emitted when a `public_key` is removed. -[.contract] -[[Falcon512AccountComponent]] -=== `++Falcon512AccountComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v4.0.0-alpha.1/packages/account/src/falcon_512/account.cairo[{github-icon},role=heading-link] - -:FalconOwnerAdded: xref:Falcon512AccountComponent-OwnerAdded[OwnerAdded] -:FalconOwnerRemoved: xref:Falcon512AccountComponent-OwnerRemoved[OwnerRemoved] - -```cairo -use openzeppelin_account::{ - Falcon512AccountComponent, Falcon512ShakeDirectVerifier, Falcon512ShakeVerifier, - Falcon512SignatureVerifier, -}; -``` - -Account component for canonical packed Falcon-512 public keys. The component is generic over an -implementation of `Falcon512SignatureVerifier`; use `Falcon512ShakeVerifier` for the -verifier-checked product-hint signature or `Falcon512ShakeDirectVerifier` to recompute the -polynomial product on-chain. - -[WARNING] -==== -The supplied verifiers target the verification relation and SHAKE-256 hash-to-point from the -FALCON submission selected by NIST. Their public-key and signature encodings are contract-specific, -and they are not FN-DSA (FIPS 206) implementations. -==== - -NOTE: {src5-component-required-note} - -[.contract-index#Falcon512AccountComponent-Embeddable-Mixin-Impl] -.{mixin-impls} --- -.Falcon512AccountMixinImpl - -* xref:#Falcon512AccountComponent-Embeddable-Impls-SRC6Impl[`++SRC6Impl++`] -* xref:#Falcon512AccountComponent-Embeddable-Impls-DeclarerImpl[`++DeclarerImpl++`] -* xref:#Falcon512AccountComponent-Embeddable-Impls-DeployableImpl[`++DeployableImpl++`] -* xref:#Falcon512AccountComponent-Embeddable-Impls-PublicKeyImpl[`++PublicKeyImpl++`] -* xref:#Falcon512AccountComponent-Embeddable-Impls-SRC6CamelOnlyImpl[`++SRC6CamelOnlyImpl++`] -* xref:#Falcon512AccountComponent-Embeddable-Impls-PublicKeyCamelImpl[`++PublicKeyCamelImpl++`] -* xref:api/introspection.adoc#SRC5Component-Embeddable-Impls[`++SRC5Impl++`] --- - -[.contract-index#Falcon512AccountComponent-Embeddable-Impls] -.Embeddable Implementations --- -[.sub-index#Falcon512AccountComponent-Embeddable-Impls-SRC6Impl] -.SRC6Impl - -* xref:#Falcon512AccountComponent-\\__execute__[`++__execute__(self, calls)++`] -* xref:#Falcon512AccountComponent-\\__validate__[`++__validate__(self, calls)++`] -* xref:#Falcon512AccountComponent-is_valid_signature[`++is_valid_signature(self, hash, signature)++`] - -[.sub-index#Falcon512AccountComponent-Embeddable-Impls-DeclarerImpl] -.DeclarerImpl - -* xref:#Falcon512AccountComponent-\\__validate_declare__[`++__validate_declare__(self, class_hash)++`] - -[.sub-index#Falcon512AccountComponent-Embeddable-Impls-DeployableImpl] -.DeployableImpl - -* xref:#Falcon512AccountComponent-\\__validate_deploy__[`++__validate_deploy__(self, class_hash, contract_address_salt, public_key)++`] - -[.sub-index#Falcon512AccountComponent-Embeddable-Impls-PublicKeyImpl] -.PublicKeyImpl - -* xref:#Falcon512AccountComponent-get_public_key[`++get_public_key(self)++`] -* xref:#Falcon512AccountComponent-set_public_key[`++set_public_key(self, new_public_key, signature)++`] - -[.sub-index#Falcon512AccountComponent-Embeddable-Impls-SRC6CamelOnlyImpl] -.SRC6CamelOnlyImpl - -* xref:#Falcon512AccountComponent-isValidSignature[`++isValidSignature(self, hash, signature)++`] - -[.sub-index#Falcon512AccountComponent-Embeddable-Impls-PublicKeyCamelImpl] -.PublicKeyCamelImpl - -* xref:#Falcon512AccountComponent-getPublicKey[`++getPublicKey(self)++`] -* xref:#Falcon512AccountComponent-setPublicKey[`++setPublicKey(self, newPublicKey, signature)++`] --- - -[.contract-index] -.Internal Implementations --- -.InternalImpl - -* xref:#Falcon512AccountComponent-initializer[`++initializer(self, public_key)++`] -* xref:#Falcon512AccountComponent-assert_only_self[`++assert_only_self(self)++`] -* xref:#Falcon512AccountComponent-assert_valid_new_owner[`++assert_valid_new_owner(self, current_owner, new_owner, signature)++`] -* xref:#Falcon512AccountComponent-validate_transaction[`++validate_transaction(self)++`] -* xref:#Falcon512AccountComponent-_set_public_key[`++_set_public_key(self, new_public_key)++`] -* xref:#Falcon512AccountComponent-read_public_key[`++read_public_key(self)++`] --- - -[.contract-index] -.Events --- -* xref:#Falcon512AccountComponent-OwnerAdded[`++OwnerAdded(new_owner_guid)++`] -* xref:#Falcon512AccountComponent-OwnerRemoved[`++OwnerRemoved(removed_owner_guid)++`] --- - -==== Verifier strategies - -`Falcon512SignatureVerifier` defines the compile-time verification strategy used by the component. -Its `verify` function authenticates a message hash and its `is_valid_public_key` function validates -the packed public-key encoding. - -`Falcon512ShakeVerifier` accepts a 60-felt signature containing 29 felts for `s1`, 2 salt felts, and -a 29-felt polynomial-product hint. `Falcon512ShakeDirectVerifier` accepts 29 felts for `s1` and 2 -salt felts, and recomputes the polynomial product on-chain. - -==== Felt encoding - -Each 29-felt polynomial represents 512 coefficients in `Z_q`, where `q = 12289`. Each of the first -28 felts stores 18 coefficients in two 128-bit halves. The low half contains the first nine -coefficients and the high half contains the next nine, with each half encoded as -`c0 + q(c1 + q(c2 + ... + q*c8))`. The final felt stores eight coefficients in its low half and -requires a zero high half. Public keys use the verifier's NTT-domain coefficient order. - -Each signature salt felt must be smaller than `2^160`. The two felts encode the 40-byte Falcon salt -as two consecutive 20-byte little-endian limbs. Hash-to-point absorbs those limbs followed by the -message hash encoded as 32 little-endian bytes. - -IMPORTANT: Build the deployable Falcon presets from this repository with -`scarb --release build -p openzeppelin_presets`. The workspace dev profile produces Falcon Sierra -artifacts that cannot be lowered to CASM. Only the workspace's `target/release` Falcon preset -artifacts are suitable for declaration. Consumer projects that embed the component must likewise -use a declaration profile with inlining enabled. - -[#Falcon512AccountComponent-Embeddable-Functions] -==== Embeddable functions - -[.contract-item] -[[Falcon512AccountComponent-__execute__]] -==== `[.contract-item-name]#++__execute__++#++(self: @ContractState, calls: Array)++` [.item-kind]#external# - -Executes the list of calls after protocol validation succeeds. - -[.contract-item] -[[Falcon512AccountComponent-__validate__]] -==== `[.contract-item-name]#++__validate__++#++(self: @ContractState, calls: Array) → felt252++` [.item-kind]#external# - -Validates an invoke transaction with the selected verifier strategy. - -[.contract-item] -[[Falcon512AccountComponent-is_valid_signature]] -==== `[.contract-item-name]#++is_valid_signature++#++(self: @ContractState, hash: felt252, signature: Array) → felt252++` [.item-kind]#external# - -Returns the short string `'VALID'` when `signature` authenticates `hash` under the current public -key, and returns `0` otherwise. - -[.contract-item] -[[Falcon512AccountComponent-__validate_declare__]] -==== `[.contract-item-name]#++__validate_declare__++#++(self: @ContractState, class_hash: felt252) → felt252++` [.item-kind]#external# - -Validates a declare transaction with the selected verifier strategy. - -[.contract-item] -[[Falcon512AccountComponent-__validate_deploy__]] -==== `[.contract-item-name]#++__validate_deploy__++#++(self: @ContractState, class_hash: felt252, contract_address_salt: felt252, public_key: Array) → felt252++` [.item-kind]#external# - -Validates a deploy-account transaction with the selected verifier strategy. - -[.contract-item] -[[Falcon512AccountComponent-get_public_key]] -==== `[.contract-item-name]#++get_public_key++#++(self: @ContractState) → Array++` [.item-kind]#external# - -Returns the current packed public key. - -[.contract-item] -[[Falcon512AccountComponent-set_public_key]] -==== `[.contract-item-name]#++set_public_key++#++(ref self: ContractState, new_public_key: Array, signature: Span)++` [.item-kind]#external# - -Sets the current key to `new_public_key`. - -Requirements: - -- The caller must be the account contract itself. -- `new_public_key` must use the canonical 29-felt encoding. -- `signature` must authenticate the ownership-acceptance message under `new_public_key`. - -Emits both a {FalconOwnerRemoved} and a {FalconOwnerAdded} event. - -[.contract-item] -[[Falcon512AccountComponent-isValidSignature]] -==== `[.contract-item-name]#++isValidSignature++#++(self: @ContractState, hash: felt252, signature: Array) → felt252++` [.item-kind]#external# - -See xref:Falcon512AccountComponent-is_valid_signature[is_valid_signature]. - -[.contract-item] -[[Falcon512AccountComponent-getPublicKey]] -==== `[.contract-item-name]#++getPublicKey++#++(self: @ContractState) → Array++` [.item-kind]#external# - -See xref:Falcon512AccountComponent-get_public_key[get_public_key]. - -[.contract-item] -[[Falcon512AccountComponent-setPublicKey]] -==== `[.contract-item-name]#++setPublicKey++#++(ref self: ContractState, newPublicKey: Array, signature: Span)++` [.item-kind]#external# - -See xref:Falcon512AccountComponent-set_public_key[set_public_key]. - -[#Falcon512AccountComponent-Internal-Functions] -==== Internal functions - -[.contract-item] -[[Falcon512AccountComponent-initializer]] -==== `[.contract-item-name]#++initializer++#++(ref self: ComponentState, public_key: Array)++` [.item-kind]#internal# - -Validates and stores a canonical packed public key and registers the `ISRC6` interface ID. - -Emits a {FalconOwnerAdded} event. - -[.contract-item] -[[Falcon512AccountComponent-assert_only_self]] -==== `[.contract-item-name]#++assert_only_self++#++(self: @ComponentState)++` [.item-kind]#internal# - -Validates that the caller is the account contract itself. - -[.contract-item] -[[Falcon512AccountComponent-assert_valid_new_owner]] -==== `[.contract-item-name]#++assert_valid_new_owner++#++(self: @ComponentState, current_owner: Span, new_owner: Span, signature: Span)++` [.item-kind]#internal# - -Validates that `new_owner` signed the domain-separated ownership-acceptance message. - -CAUTION: The caller must pass the account's current public key as `current_owner`. - -[.contract-item] -[[Falcon512AccountComponent-validate_transaction]] -==== `[.contract-item-name]#++validate_transaction++#++(self: @ComponentState) → felt252++` [.item-kind]#internal# - -Validates the current transaction signature and returns the short string `'VALID'`. - -[.contract-item] -[[Falcon512AccountComponent-_set_public_key]] -==== `[.contract-item-name]#++_set_public_key++#++(ref self: ComponentState, new_public_key: Array)++` [.item-kind]#internal# - -Stores `new_public_key` and emits a {FalconOwnerAdded} event. Callers are responsible for validating -authorization and key encoding before using this function. - -[.contract-item] -[[Falcon512AccountComponent-read_public_key]] -==== `[.contract-item-name]#++read_public_key++#++(self: @ComponentState) → Array++` [.item-kind]#internal# - -Returns the stored key in its 29-felt packed representation. - -[#Falcon512AccountComponent-Events] -==== Events - -The event GUID is the Poseidon hash of the packed public key. - -[.contract-item] -[[Falcon512AccountComponent-OwnerAdded]] -==== `[.contract-item-name]#++OwnerAdded++#++(new_owner_guid: felt252)++` [.item-kind]#event# - -Emitted when a public key is added. - -[.contract-item] -[[Falcon512AccountComponent-OwnerRemoved]] -==== `[.contract-item-name]#++OwnerRemoved++#++(removed_owner_guid: felt252)++` [.item-kind]#event# - -Emitted when a public key is removed. - == Extensions [.contract] @@ -1170,139 +828,3 @@ Requirements: - The caller is the account contract itself. - `new_class_hash` cannot be zero. - -[WARNING] -==== -The Falcon presets use contract-specific public-key and signature encodings for the FALCON -submission verification relation. They are not FN-DSA (FIPS 206) implementations. -==== - -[.contract] -[[Falcon512ShakeAccountUpgradeable]] -=== `++Falcon512ShakeAccountUpgradeable++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v4.0.0-alpha.1/packages/presets/src/falcon_512_shake_account.cairo[{github-icon},role=heading-link] - -```cairo -use openzeppelin_presets::Falcon512ShakeAccountUpgradeable; -``` - -Upgradeable Falcon-512 account using SHAKE-256 hash-to-point and a 60-felt signature with a -verifier-checked polynomial-product hint. It supports owner-authorized key rotation and outside -execution through xref:#SRC9Component[SRC9]. - -include::../utils/_class_hashes.adoc[] - -[.contract-index] -.{presets-page} --- -{Falcon512ShakeAccountUpgradeable-class-hash} --- - -[.contract-index] -.Constructor --- -* xref:#Falcon512ShakeAccountUpgradeable-constructor[`++constructor(self, public_key)++`] --- - -[.contract-index] -.Embedded Implementations --- -.Falcon512AccountComponent -* xref:#Falcon512AccountComponent-Embeddable-Mixin-Impl[`++Falcon512AccountMixinImpl++`] - -.SRC9Component -* xref:#SRC9Component-Embeddable-Impls-OutsideExecutionV2Impl[`++OutsideExecutionV2Impl++`] --- - -[.contract-index] -.External Functions --- -* xref:#Falcon512ShakeAccountUpgradeable-upgrade[`++upgrade(self, new_class_hash)++`] --- - -[#Falcon512ShakeAccountUpgradeable-constructor-section] -==== Constructor - -[.contract-item] -[[Falcon512ShakeAccountUpgradeable-constructor]] -==== `[.contract-item-name]#++constructor++#++(ref self: ContractState, public_key: Array)++` [.item-kind]#constructor# - -Sets a canonical 29-felt NTT-domain public key and registers the interfaces the contract supports. - -[#Falcon512ShakeAccountUpgradeable-external-functions] -==== External functions - -[.contract-item] -[[Falcon512ShakeAccountUpgradeable-upgrade]] -==== `[.contract-item-name]#++upgrade++#++(ref self: ContractState, new_class_hash: ClassHash)++` [.item-kind]#external# - -Upgrades the account implementation to `new_class_hash`. - -Requirements: - -- The caller must be the account contract itself. -- `new_class_hash` cannot be zero. - -[.contract] -[[Falcon512ShakeDirectAccountUpgradeable]] -=== `++Falcon512ShakeDirectAccountUpgradeable++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v4.0.0-alpha.1/packages/presets/src/falcon_512_shake_direct_account.cairo[{github-icon},role=heading-link] - -```cairo -use openzeppelin_presets::Falcon512ShakeDirectAccountUpgradeable; -``` - -Upgradeable Falcon-512 account using SHAKE-256 hash-to-point and a 31-felt signature. It recomputes -the polynomial product on-chain and supports owner-authorized key rotation and outside execution -through xref:#SRC9Component[SRC9]. - -include::../utils/_class_hashes.adoc[] - -[.contract-index] -.{presets-page} --- -{Falcon512ShakeDirectAccountUpgradeable-class-hash} --- - -[.contract-index] -.Constructor --- -* xref:#Falcon512ShakeDirectAccountUpgradeable-constructor[`++constructor(self, public_key)++`] --- - -[.contract-index] -.Embedded Implementations --- -.Falcon512AccountComponent -* xref:#Falcon512AccountComponent-Embeddable-Mixin-Impl[`++Falcon512AccountMixinImpl++`] - -.SRC9Component -* xref:#SRC9Component-Embeddable-Impls-OutsideExecutionV2Impl[`++OutsideExecutionV2Impl++`] --- - -[.contract-index] -.External Functions --- -* xref:#Falcon512ShakeDirectAccountUpgradeable-upgrade[`++upgrade(self, new_class_hash)++`] --- - -[#Falcon512ShakeDirectAccountUpgradeable-constructor-section] -==== Constructor - -[.contract-item] -[[Falcon512ShakeDirectAccountUpgradeable-constructor]] -==== `[.contract-item-name]#++constructor++#++(ref self: ContractState, public_key: Array)++` [.item-kind]#constructor# - -Sets a canonical 29-felt NTT-domain public key and registers the interfaces the contract supports. - -[#Falcon512ShakeDirectAccountUpgradeable-external-functions] -==== External functions - -[.contract-item] -[[Falcon512ShakeDirectAccountUpgradeable-upgrade]] -==== `[.contract-item-name]#++upgrade++#++(ref self: ContractState, new_class_hash: ClassHash)++` [.item-kind]#external# - -Upgrades the account implementation to `new_class_hash`. - -Requirements: - -- The caller must be the account contract itself. -- `new_class_hash` cannot be zero. diff --git a/docs/modules/ROOT/pages/presets.adoc b/docs/modules/ROOT/pages/presets.adoc index 7a0511b8e..6437590f5 100644 --- a/docs/modules/ROOT/pages/presets.adoc +++ b/docs/modules/ROOT/pages/presets.adoc @@ -3,8 +3,6 @@ :erc721-upgradeable: xref:/api/erc721.adoc#ERC721Upgradeable[ERC721Upgradeable] :erc1155-upgradeable: xref:/api/erc1155.adoc#ERC1155Upgradeable[ERC1155Upgradeable] :eth-account-upgradeable: xref:/api/account.adoc#EthAccountUpgradeable[EthAccountUpgradeable] -:falcon512-shake-account-upgradeable: xref:/api/account.adoc#Falcon512ShakeAccountUpgradeable[Falcon512ShakeAccountUpgradeable] -:falcon512-shake-direct-account-upgradeable: xref:/api/account.adoc#Falcon512ShakeDirectAccountUpgradeable[Falcon512ShakeDirectAccountUpgradeable] :udc: xref:/api/udc.adoc#UniversalDeployer[UniversalDeployer] :vesting-wallet: xref:/api/finance.adoc#VestingWallet[VestingWallet] :sierra-class-hashes: https://docs.starknet.io/architecture-and-concepts/smart-contracts/class-hash/[Sierra class hashes] @@ -50,12 +48,6 @@ CAUTION: Before version 4.0.0-alpha.1, class hashes were computed using the `sca | `{eth-account-upgradeable}` | `{EthAccountUpgradeable-class-hash}` -| `{falcon512-shake-account-upgradeable}` -| `{Falcon512ShakeAccountUpgradeable-class-hash}` - -| `{falcon512-shake-direct-account-upgradeable}` -| `{Falcon512ShakeDirectAccountUpgradeable-class-hash}` - | `{udc}` | `{UniversalDeployer-class-hash}` @@ -65,10 +57,6 @@ CAUTION: Before version 4.0.0-alpha.1, class hashes were computed using the `sca TIP: {starkli} class-hash command can be used to compute the class hash from a Sierra artifact. -IMPORTANT: Build Falcon account preset artifacts with -`scarb --release build -p openzeppelin_presets`. Their verification paths require compiler -inlining, so use the artifacts under `target/release` for declaration. - == Usage :setup_project: xref:index.adoc#set_up_your_project[setting up a project] diff --git a/docs/modules/ROOT/pages/utils/_class_hashes.adoc b/docs/modules/ROOT/pages/utils/_class_hashes.adoc index 50b89b059..2396a7ea8 100644 --- a/docs/modules/ROOT/pages/utils/_class_hashes.adoc +++ b/docs/modules/ROOT/pages/utils/_class_hashes.adoc @@ -1,17 +1,16 @@ // Version -:class-hash-cairo-version: https://crates.io/crates/cairo-lang-compiler/2.18.0[cairo 2.18.0] +:class-hash-cairo-version: https://crates.io/crates/cairo-lang-compiler/2.17.0[cairo 2.17.0] // Class Hashes -:ERC20Upgradeable-class-hash: 0x06d081c70145f14c3e0d05eacbfaa01620a27fde89430e6c227a8f4a573e3727 -:ERC721Upgradeable-class-hash: 0x05f3d201fc5c9de757ca6c42ec753d514ab1ea011b60bd139b6167cd069109d2 -:ERC1155Upgradeable-class-hash: 0x075829cba5eea49a998988967e6a97de68824830118f7f008f3ac07d407cb99a -:AccountUpgradeable-class-hash: 0x03013bf2edcf93ac575367eee41cd07bb50e809c78925817b61878db29826144 -:EthAccountUpgradeable-class-hash: 0x000be1cf8839442d664572712fd36554c4d259f86549ad02e22353d10729b900 -:Falcon512ShakeAccountUpgradeable-class-hash: 0x0737b72596a5aa02fb18c77615eca82cbf47988a22cf42e6b44280c8ef0b89df -:Falcon512ShakeDirectAccountUpgradeable-class-hash: 0x0734b642ac6247e1a747b46e86c2f443bd9b00879683b2243b932a0dbf3f3a99 -:UniversalDeployer-class-hash: 0x05412c7a3e145fdc79862e65df612324d83b0169c071dd564021155bd8cfd29e -:MetaTransactionV0-class-hash: 0x059b5a7a062db4a9fa1ec4b96ea74cac3d55477971d68ac9fba877c087e59808 -:VestingWallet-class-hash: 0x05613fb6c8f729fdcbef50d25c9dd72260762543d48254c59e6851dd3d46595a +:ERC20Upgradeable-class-hash: 0x02306d411d9061591d2a661f177ea92e53918a474803c33a901bace86dfaea6c +:ERC721Upgradeable-class-hash: 0x0735400adfb617a215a0eadd009e84ecebf10b258b7ce7583d4131f7fa793bf1 +:ERC1155Upgradeable-class-hash: 0x02f81498862c308981a544e7da3e65c715c375c97a881da2d157b2f3a3dcdd93 +:AccountUpgradeable-class-hash: 0x0342f3c683f708fd920e625e425b77726aa4c834da47ec92b4da5c207f025207 +:EthAccountUpgradeable-class-hash: 0x0216f6773aef13b25e5f8ecc989c4a4668d45ba118b41bbdca10d08fc6fda878 +:UniversalDeployer-class-hash: 0x00ce766f9026176e6796a13720059301c4caa1b24cc55d964afb62bed07b0158 +:MetaTransactionV0-class-hash: 0x051d6576154ab74933e521cd0d8e3367d14ff1713fd2b2ce982af1f555564134 +:VestingWallet-class-hash: 0x051be0e6c0a6904b496c77f991f423e766ff465752cb18e93b6eb5ca525cf219 // Presets page :presets-page: xref:presets.adoc[Sierra class hash] + diff --git a/scripts/get_hashes_page.py b/scripts/get_hashes_page.py index 214e580c5..593e79704 100644 --- a/scripts/get_hashes_page.py +++ b/scripts/get_hashes_page.py @@ -7,9 +7,7 @@ "ERC1155Upgradeable", "AccountUpgradeable", "EthAccountUpgradeable", - "Falcon512ShakeAccountUpgradeable", - "Falcon512ShakeDirectAccountUpgradeable", - "UniversalDeployer", + "UniversalDeployer" ] def main():