From 40866e775aa707b8572571917b4eb35e2b40481b Mon Sep 17 00:00:00 2001 From: Hamish Ivey-Law <426294+unzvfu@users.noreply.github.com> Date: Fri, 10 Feb 2023 23:07:57 +1100 Subject: [PATCH 1/4] Refactor arithmetic operation traits (#876) * Use U256s in `generate(...)` interfaces; fix reduction bug modular. * Refactor `Operation` trait. * Rename file. * Rename `add_cc` things to `addcy`. * Clippy. * Simplify generation of less-than and greater-than. * Add some comparison tests. * Use `PrimeField64` instead of `RichField` where possible. * Connect `SUBMOD` operation to witness generator. * Add clippy exception. * Add missing verification of range counter column. * Fix generation of RANGE_COUNTER column. * Address William's PR comments. --- evm/src/arithmetic/{addcc.rs => addcy.rs} | 93 +++++------- evm/src/arithmetic/arithmetic_stark.rs | 117 +++++++++------ evm/src/arithmetic/columns.rs | 2 + evm/src/arithmetic/mod.rs | 131 +++++++++++++---- evm/src/arithmetic/modular.rs | 141 ++++++++++-------- evm/src/arithmetic/mul.rs | 14 +- evm/src/arithmetic/operations.rs | 166 ---------------------- evm/src/arithmetic/utils.rs | 42 ++++-- evm/src/cpu/columns/ops.rs | 3 +- evm/src/cpu/stack.rs | 1 + evm/src/witness/transition.rs | 4 + 11 files changed, 346 insertions(+), 368 deletions(-) rename evm/src/arithmetic/{addcc.rs => addcy.rs} (82%) delete mode 100644 evm/src/arithmetic/operations.rs diff --git a/evm/src/arithmetic/addcc.rs b/evm/src/arithmetic/addcy.rs similarity index 82% rename from evm/src/arithmetic/addcc.rs rename to evm/src/arithmetic/addcy.rs index ed173ec7..32fa4a9e 100644 --- a/evm/src/arithmetic/addcc.rs +++ b/evm/src/arithmetic/addcy.rs @@ -14,51 +14,19 @@ //! GT: X > Z, inputs X, Z, output CY, auxiliary output Y //! LT: Z < X, inputs Z, X, output CY, auxiliary output Y -use itertools::{izip, Itertools}; +use ethereum_types::U256; +use itertools::Itertools; use plonky2::field::extension::Extendable; use plonky2::field::packed::PackedField; -use plonky2::field::types::Field; +use plonky2::field::types::{Field, PrimeField64}; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::plonk::circuit_builder::CircuitBuilder; use crate::arithmetic::columns::*; -use crate::arithmetic::utils::read_value_u64_limbs; +use crate::arithmetic::utils::u256_to_array; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -fn u256_add_cc(input0: [u64; N_LIMBS], input1: [u64; N_LIMBS]) -> ([u64; N_LIMBS], u64) { - // Input and output have 16-bit limbs - let mut output = [0u64; N_LIMBS]; - - const MASK: u64 = (1u64 << LIMB_BITS) - 1u64; - let mut cy = 0u64; - for (i, a, b) in izip!(0.., input0, input1) { - let s = a + b + cy; - cy = s >> LIMB_BITS; - assert!(cy <= 1u64, "input limbs were larger than 16 bits"); - output[i] = s & MASK; - } - (output, cy) -} - -fn u256_sub_br(input0: [u64; N_LIMBS], input1: [u64; N_LIMBS]) -> ([u64; N_LIMBS], u64) { - const LIMB_BOUNDARY: u64 = 1 << LIMB_BITS; - const MASK: u64 = LIMB_BOUNDARY - 1u64; - - let mut output = [0u64; N_LIMBS]; - let mut br = 0u64; - for (i, a, b) in izip!(0.., input0, input1) { - let d = LIMB_BOUNDARY + a - b - br; - // if a < b, then d < 2^16 so br = 1 - // if a >= b, then d >= 2^16 so br = 0 - br = 1u64 - (d >> LIMB_BITS); - assert!(br <= 1u64, "input limbs were larger than 16 bits"); - output[i] = d & MASK; - } - - (output, br) -} - /// Generate row for ADD, SUB, GT and LT operations. /// /// A row consists of four values, GENERAL_REGISTER_[012] and @@ -69,27 +37,35 @@ fn u256_sub_br(input0: [u64; N_LIMBS], input1: [u64; N_LIMBS]) -> ([u64; N_LIMBS /// SUB: REGISTER_2 - REGISTER_0, output in REGISTER_1, ignore REGISTER_BIT /// GT: REGISTER_0 > REGISTER_2, output in REGISTER_BIT, auxiliary output in REGISTER_1 /// LT: REGISTER_2 < REGISTER_0, output in REGISTER_BIT, auxiliary output in REGISTER_1 -pub(crate) fn generate(lv: &mut [F], filter: usize) { +pub(crate) fn generate( + lv: &mut [F], + filter: usize, + left_in: U256, + right_in: U256, +) { + // Swap left_in and right_in for LT + let (left_in, right_in) = if filter == IS_LT { + (right_in, left_in) + } else { + (left_in, right_in) + }; + match filter { IS_ADD => { - let x = read_value_u64_limbs(lv, GENERAL_REGISTER_0); - let y = read_value_u64_limbs(lv, GENERAL_REGISTER_1); - // x + y == z + cy*2^256 - let (z, cy) = u256_add_cc(x, y); - - lv[GENERAL_REGISTER_2].copy_from_slice(&z.map(F::from_canonical_u64)); - lv[GENERAL_REGISTER_BIT] = F::from_canonical_u64(cy); + let (result, cy) = left_in.overflowing_add(right_in); + u256_to_array(&mut lv[GENERAL_REGISTER_0], left_in); // x + u256_to_array(&mut lv[GENERAL_REGISTER_1], right_in); // y + u256_to_array(&mut lv[GENERAL_REGISTER_2], result); // z + lv[GENERAL_REGISTER_BIT] = F::from_bool(cy); } IS_SUB | IS_GT | IS_LT => { - let x = read_value_u64_limbs(lv, GENERAL_REGISTER_0); - let z = read_value_u64_limbs(lv, GENERAL_REGISTER_2); - // y == z - x + cy*2^256 - let (y, cy) = u256_sub_br(z, x); - - lv[GENERAL_REGISTER_1].copy_from_slice(&y.map(F::from_canonical_u64)); - lv[GENERAL_REGISTER_BIT] = F::from_canonical_u64(cy); + let (diff, cy) = right_in.overflowing_sub(left_in); + u256_to_array(&mut lv[GENERAL_REGISTER_0], left_in); // x + u256_to_array(&mut lv[GENERAL_REGISTER_2], right_in); // z + u256_to_array(&mut lv[GENERAL_REGISTER_1], diff); // y + lv[GENERAL_REGISTER_BIT] = F::from_bool(cy); } _ => panic!("unexpected operation filter"), }; @@ -144,7 +120,7 @@ const GOLDILOCKS_INVERSE_65536: u64 = 18446462594437939201; /// is true if `(x_n + y_n)*2^(16*n) == cy_{n-1}*2^(16*n) + /// z_n*2^(16*n) + cy_n*2^(16*n)` (again, this is `t` on line 127ff) /// with the last `cy_n` checked against the `given_cy` given as input. -pub(crate) fn eval_packed_generic_add_cc( +pub(crate) fn eval_packed_generic_addcy( yield_constr: &mut ConstraintConsumer

, filter: P, x: &[P], @@ -202,11 +178,11 @@ pub fn eval_packed_generic( eval_packed_generic_check_is_one_bit(yield_constr, op_filter, cy); // x + y = z + cy*2^256 - eval_packed_generic_add_cc(yield_constr, op_filter, x, y, z, cy, false); + eval_packed_generic_addcy(yield_constr, op_filter, x, y, z, cy, false); } #[allow(clippy::needless_collect)] -pub(crate) fn eval_ext_circuit_add_cc, const D: usize>( +pub(crate) fn eval_ext_circuit_addcy, const D: usize>( builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, yield_constr: &mut RecursiveConstraintConsumer, filter: ExtensionTarget, @@ -272,7 +248,7 @@ pub fn eval_ext_circuit, const D: usize>( let op_filter = builder.add_many_extension([is_add, is_sub, is_lt, is_gt]); eval_ext_circuit_check_is_one_bit(builder, yield_constr, op_filter, cy); - eval_ext_circuit_add_cc(builder, yield_constr, op_filter, x, y, z, cy, false); + eval_ext_circuit_addcy(builder, yield_constr, op_filter, x, y, z, cy, false); } #[cfg(test)] @@ -328,7 +304,7 @@ mod tests { .map(|_| F::from_canonical_u16(rng.gen::())); // set operation filter and ensure all constraints are - // satisfied. we have to explicitly set the other + // satisfied. We have to explicitly set the other // operation filters to zero since all are treated by // the call. lv[IS_ADD] = F::ZERO; @@ -337,7 +313,10 @@ mod tests { lv[IS_GT] = F::ZERO; lv[op_filter] = F::ONE; - generate(&mut lv, op_filter); + let left_in = U256::from(rng.gen::<[u8; 32]>()); + let right_in = U256::from(rng.gen::<[u8; 32]>()); + + generate(&mut lv, op_filter, left_in, right_in); let mut constrant_consumer = ConstraintConsumer::new( vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)], diff --git a/evm/src/arithmetic/arithmetic_stark.rs b/evm/src/arithmetic/arithmetic_stark.rs index 9af1a9c3..0a89f3c6 100644 --- a/evm/src/arithmetic/arithmetic_stark.rs +++ b/evm/src/arithmetic/arithmetic_stark.rs @@ -4,11 +4,12 @@ use itertools::Itertools; use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::field::packed::PackedField; use plonky2::field::polynomial::PolynomialValues; +use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; +use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::util::transpose; -use crate::arithmetic::operations::Operation; -use crate::arithmetic::{addcc, columns, modular, mul}; +use crate::arithmetic::{addcy, columns, modular, mul, Operation}; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::lookup::{eval_lookups, eval_lookups_circuit, permuted_cols}; use crate::permutation::PermutationPair; @@ -33,6 +34,9 @@ impl ArithmeticStark { for i in 0..RANGE_MAX { cols[columns::RANGE_COUNTER][i] = F::from_canonical_usize(i); } + for i in RANGE_MAX..n_rows { + cols[columns::RANGE_COUNTER][i] = F::from_canonical_usize(RANGE_MAX - 1); + } // For each column c in cols, generate the range-check // permutations and put them in the corresponding range-check @@ -44,7 +48,8 @@ impl ArithmeticStark { } } - pub fn generate(&self, operations: Vec<&dyn Operation>) -> Vec> { + #[allow(unused)] + pub(crate) fn generate(&self, operations: Vec) -> Vec> { // The number of rows reserved is the smallest value that's // guaranteed to avoid a reallocation: The only ops that use // two rows are the modular operations and DIV, so the only @@ -96,14 +101,25 @@ impl, const D: usize> Stark for ArithmeticSta let lv = vars.local_values; let nv = vars.next_values; + // Check the range column: First value must be 0, last row + // must be 2^16-1, and intermediate rows must increment by 0 + // or 1. + let rc1 = lv[columns::RANGE_COUNTER]; + let rc2 = nv[columns::RANGE_COUNTER]; + yield_constr.constraint_first_row(rc1); + let incr = rc2 - rc1; + yield_constr.constraint_transition(incr * incr - incr); + let range_max = P::Scalar::from_canonical_u64((RANGE_MAX - 1) as u64); + yield_constr.constraint_last_row(rc1 - range_max); + mul::eval_packed_generic(lv, yield_constr); - addcc::eval_packed_generic(lv, yield_constr); + addcy::eval_packed_generic(lv, yield_constr); modular::eval_packed_generic(lv, nv, yield_constr); } fn eval_ext_circuit( &self, - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, + builder: &mut CircuitBuilder, vars: StarkEvaluationTargets, yield_constr: &mut RecursiveConstraintConsumer, ) { @@ -114,8 +130,20 @@ impl, const D: usize> Stark for ArithmeticSta let lv = vars.local_values; let nv = vars.next_values; + + let rc1 = lv[columns::RANGE_COUNTER]; + let rc2 = nv[columns::RANGE_COUNTER]; + yield_constr.constraint_first_row(builder, rc1); + let incr = builder.sub_extension(rc2, rc1); + let t = builder.mul_sub_extension(incr, incr, incr); + yield_constr.constraint_transition(builder, t); + let range_max = + builder.constant_extension(F::Extension::from_canonical_usize(RANGE_MAX - 1)); + let t = builder.sub_extension(rc1, range_max); + yield_constr.constraint_last_row(builder, t); + mul::eval_ext_circuit(builder, lv, yield_constr); - addcc::eval_ext_circuit(builder, lv, yield_constr); + addcy::eval_ext_circuit(builder, lv, yield_constr); modular::eval_ext_circuit(builder, lv, nv, yield_constr); } @@ -148,7 +176,7 @@ mod tests { use rand_chacha::ChaCha8Rng; use super::{columns, ArithmeticStark}; - use crate::arithmetic::operations::*; + use crate::arithmetic::*; use crate::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; #[test] @@ -189,34 +217,37 @@ mod tests { }; // 123 + 456 == 579 - let add = SimpleBinaryOp::new(columns::IS_ADD, U256::from(123), U256::from(456)); + let add = Operation::binary(BinaryOperator::Add, U256::from(123), U256::from(456)); // (123 * 456) % 1007 == 703 - let mulmod = ModularBinaryOp::new( - columns::IS_MULMOD, + let mulmod = Operation::ternary( + TernaryOperator::MulMod, U256::from(123), U256::from(456), U256::from(1007), ); - // (123 - 456) % 1007 == 674 - let submod = ModularBinaryOp::new( - columns::IS_SUBMOD, - U256::from(123), - U256::from(456), + // (1234 + 567) % 1007 == 794 + let addmod = Operation::ternary( + TernaryOperator::AddMod, + U256::from(1234), + U256::from(567), U256::from(1007), ); // 123 * 456 == 56088 - let mul = SimpleBinaryOp::new(columns::IS_MUL, U256::from(123), U256::from(456)); - // 128 % 13 == 11 - let modop = ModOp { - input: U256::from(128), - modulus: U256::from(13), - }; + let mul = Operation::binary(BinaryOperator::Mul, U256::from(123), U256::from(456)); // 128 / 13 == 9 - let div = DivOp { - numerator: U256::from(128), - denominator: U256::from(13), - }; - let ops: Vec<&dyn Operation> = vec![&add, &mulmod, &submod, &mul, &div, &modop]; + let div = Operation::binary(BinaryOperator::Div, U256::from(128), U256::from(13)); + + // 128 < 13 == 0 + let lt1 = Operation::binary(BinaryOperator::Lt, U256::from(128), U256::from(13)); + // 13 < 128 == 1 + let lt2 = Operation::binary(BinaryOperator::Lt, U256::from(13), U256::from(128)); + // 128 < 128 == 0 + let lt3 = Operation::binary(BinaryOperator::Lt, U256::from(128), U256::from(128)); + + // 128 % 13 == 11 + let modop = Operation::binary(BinaryOperator::Mod, U256::from(128), U256::from(13)); + + let ops: Vec = vec![add, mulmod, addmod, mul, modop, lt1, lt2, lt3, div]; let pols = stark.generate(ops); @@ -228,15 +259,21 @@ mod tests { && pols.iter().all(|v| v.len() == super::RANGE_MAX) ); + // Wrap the single value GENERAL_REGISTER_BIT in a Range. + let cmp_range = columns::GENERAL_REGISTER_BIT..columns::GENERAL_REGISTER_BIT + 1; + // Each operation has a single word answer that we can check let expected_output = [ // Row (some ops take two rows), col, expected - (0, columns::GENERAL_REGISTER_2, 579), // ADD_OUTPUT - (1, columns::MODULAR_OUTPUT, 703), - (3, columns::MODULAR_OUTPUT, 674), - (5, columns::MUL_OUTPUT, 56088), - (6, columns::MODULAR_OUTPUT, 11), - (8, columns::DIV_OUTPUT, 9), + (0, &columns::GENERAL_REGISTER_2, 579), // ADD_OUTPUT + (1, &columns::MODULAR_OUTPUT, 703), + (3, &columns::MODULAR_OUTPUT, 794), + (5, &columns::MUL_OUTPUT, 56088), + (6, &columns::MODULAR_OUTPUT, 11), + (8, &cmp_range, 0), + (9, &cmp_range, 1), + (10, &cmp_range, 0), + (11, &columns::DIV_OUTPUT, 9), ]; for (row, col, expected) in expected_output { @@ -269,18 +306,14 @@ mod tests { let ops = (0..super::RANGE_MAX) .map(|_| { - SimpleBinaryOp::new( - columns::IS_MUL, + Operation::binary( + BinaryOperator::Mul, U256::from(rng.gen::<[u8; 32]>()), U256::from(rng.gen::<[u8; 32]>()), ) }) .collect::>(); - // TODO: This is clearly not the right way to build this - // vector; I can't work out how to do it using the map above - // though, with or without Boxes. - let ops = ops.iter().map(|o| o as &dyn Operation).collect(); let pols = stark.generate(ops); // Trace should always have NUM_ARITH_COLUMNS columns and @@ -293,8 +326,8 @@ mod tests { let ops = (0..super::RANGE_MAX) .map(|_| { - ModularBinaryOp::new( - columns::IS_MULMOD, + Operation::ternary( + TernaryOperator::MulMod, U256::from(rng.gen::<[u8; 32]>()), U256::from(rng.gen::<[u8; 32]>()), U256::from(rng.gen::<[u8; 32]>()), @@ -302,10 +335,6 @@ mod tests { }) .collect::>(); - // TODO: This is clearly not the right way to build this - // vector; I can't work out how to do it using the map above - // though, with or without Boxes. - let ops = ops.iter().map(|o| o as &dyn Operation).collect(); let pols = stark.generate(ops); // Trace should always have NUM_ARITH_COLUMNS columns and diff --git a/evm/src/arithmetic/columns.rs b/evm/src/arithmetic/columns.rs index e05a4070..952a8ed5 100644 --- a/evm/src/arithmetic/columns.rs +++ b/evm/src/arithmetic/columns.rs @@ -101,7 +101,9 @@ pub(crate) const MODULAR_AUX_INPUT_HI: Range = AUX_REGISTER_2; // Must be set to MOD_IS_ZERO for DIV operation i.e. MOD_IS_ZERO * lv[IS_DIV] pub(crate) const MODULAR_DIV_DENOM_IS_ZERO: usize = AUX_REGISTER_2.end; +#[allow(unused)] // TODO: Will be used when hooking into the CPU pub(crate) const DIV_NUMERATOR: Range = MODULAR_INPUT_0; +#[allow(unused)] // TODO: Will be used when hooking into the CPU pub(crate) const DIV_DENOMINATOR: Range = MODULAR_MODULUS; #[allow(unused)] // TODO: Will be used when hooking into the CPU pub(crate) const DIV_OUTPUT: Range = diff --git a/evm/src/arithmetic/mod.rs b/evm/src/arithmetic/mod.rs index 60c8a2f8..6ba8ed12 100644 --- a/evm/src/arithmetic/mod.rs +++ b/evm/src/arithmetic/mod.rs @@ -1,10 +1,9 @@ -use std::str::FromStr; - use ethereum_types::U256; +use plonky2::field::types::PrimeField64; use crate::util::{addmod, mulmod, submod}; -mod addcc; +mod addcy; mod modular; mod mul; mod utils; @@ -12,8 +11,6 @@ mod utils; pub mod arithmetic_stark; pub(crate) mod columns; -pub mod operations; - #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub(crate) enum BinaryOperator { Add, @@ -48,31 +45,36 @@ impl BinaryOperator { input0 % input1 } } - BinaryOperator::Lt => { - if input0 < input1 { - U256::one() - } else { - U256::zero() - } - } - BinaryOperator::Gt => { - if input0 > input1 { - U256::one() - } else { - U256::zero() - } - } - BinaryOperator::AddFp254 => addmod(input0, input1, bn_base_order()), - BinaryOperator::MulFp254 => mulmod(input0, input1, bn_base_order()), - BinaryOperator::SubFp254 => submod(input0, input1, bn_base_order()), + BinaryOperator::Lt => U256::from((input0 < input1) as u8), + BinaryOperator::Gt => U256::from((input0 > input1) as u8), + BinaryOperator::AddFp254 => addmod(input0, input1, BN_BASE_ORDER), + BinaryOperator::MulFp254 => mulmod(input0, input1, BN_BASE_ORDER), + BinaryOperator::SubFp254 => submod(input0, input1, BN_BASE_ORDER), + } + } + + pub(crate) fn row_filter(&self) -> usize { + match self { + BinaryOperator::Add => columns::IS_ADD, + BinaryOperator::Mul => columns::IS_MUL, + BinaryOperator::Sub => columns::IS_SUB, + BinaryOperator::Div => columns::IS_DIV, + BinaryOperator::Mod => columns::IS_MOD, + BinaryOperator::Lt => columns::IS_LT, + BinaryOperator::Gt => columns::IS_GT, + BinaryOperator::AddFp254 => columns::IS_ADDMOD, + BinaryOperator::MulFp254 => columns::IS_MULMOD, + BinaryOperator::SubFp254 => columns::IS_SUBMOD, } } } +#[allow(clippy::enum_variant_names)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub(crate) enum TernaryOperator { AddMod, MulMod, + SubMod, } impl TernaryOperator { @@ -80,6 +82,15 @@ impl TernaryOperator { match self { TernaryOperator::AddMod => addmod(input0, input1, input2), TernaryOperator::MulMod => mulmod(input0, input1, input2), + TernaryOperator::SubMod => submod(input0, input1, input2), + } + } + + pub(crate) fn row_filter(&self) -> usize { + match self { + TernaryOperator::AddMod => columns::IS_ADDMOD, + TernaryOperator::MulMod => columns::IS_MULMOD, + TernaryOperator::SubMod => columns::IS_SUBMOD, } } } @@ -135,8 +146,80 @@ impl Operation { Operation::TernaryOperation { result, .. } => *result, } } + + /// Convert operation into one or two rows of the trace. + /// + /// Morally these types should be [F; NUM_ARITH_COLUMNS], but we + /// use vectors because that's what utils::transpose (who consumes + /// the result of this function as part of the range check code) + /// expects. + fn to_rows(&self) -> (Vec, Option>) { + match *self { + Operation::BinaryOperation { + operator, + input0, + input1, + result, + } => binary_op_to_rows(operator, input0, input1, result), + Operation::TernaryOperation { + operator, + input0, + input1, + input2, + result, + } => ternary_op_to_rows(operator.row_filter(), input0, input1, input2, result), + } + } } -fn bn_base_order() -> U256 { - U256::from_str("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47").unwrap() +fn ternary_op_to_rows( + row_filter: usize, + input0: U256, + input1: U256, + input2: U256, + _result: U256, +) -> (Vec, Option>) { + let mut row1 = vec![F::ZERO; columns::NUM_ARITH_COLUMNS]; + let mut row2 = vec![F::ZERO; columns::NUM_ARITH_COLUMNS]; + + row1[row_filter] = F::ONE; + + modular::generate(&mut row1, &mut row2, row_filter, input0, input1, input2); + + (row1, Some(row2)) } + +fn binary_op_to_rows( + op: BinaryOperator, + input0: U256, + input1: U256, + result: U256, +) -> (Vec, Option>) { + let mut row = vec![F::ZERO; columns::NUM_ARITH_COLUMNS]; + row[op.row_filter()] = F::ONE; + + match op { + BinaryOperator::Add | BinaryOperator::Sub | BinaryOperator::Lt | BinaryOperator::Gt => { + addcy::generate(&mut row, op.row_filter(), input0, input1); + (row, None) + } + BinaryOperator::Mul => { + mul::generate(&mut row, input0, input1); + (row, None) + } + BinaryOperator::Div | BinaryOperator::Mod => { + ternary_op_to_rows::(op.row_filter(), input0, U256::zero(), input1, result) + } + BinaryOperator::AddFp254 | BinaryOperator::MulFp254 | BinaryOperator::SubFp254 => { + ternary_op_to_rows::(op.row_filter(), input0, input1, BN_BASE_ORDER, result) + } + } +} + +/// Order of the BN254 base field. +const BN_BASE_ORDER: U256 = U256([ + 4332616871279656263, + 10917124144477883021, + 13281191951274694749, + 3486998266802970665, +]); diff --git a/evm/src/arithmetic/modular.rs b/evm/src/arithmetic/modular.rs index 799a4ad4..99eccacb 100644 --- a/evm/src/arithmetic/modular.rs +++ b/evm/src/arithmetic/modular.rs @@ -108,17 +108,18 @@ //! only require 96 columns, or 80 if the output doesn't need to be //! reduced. +use ethereum_types::U256; use num::bigint::Sign; use num::{BigInt, One, Zero}; use plonky2::field::extension::Extendable; use plonky2::field::packed::PackedField; -use plonky2::field::types::Field; +use plonky2::field::types::{Field, PrimeField64}; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::plonk::circuit_builder::CircuitBuilder; use super::columns; -use crate::arithmetic::addcc::{eval_ext_circuit_add_cc, eval_packed_generic_add_cc}; +use crate::arithmetic::addcy::{eval_ext_circuit_addcy, eval_packed_generic_addcy}; use crate::arithmetic::columns::*; use crate::arithmetic::utils::*; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; @@ -189,7 +190,7 @@ fn bigint_to_columns(num: &BigInt) -> [i64; N] { /// /// NB: `operation` can set the higher order elements in its result to /// zero if they are not used. -fn generate_modular_op( +fn generate_modular_op( lv: &mut [F], nv: &mut [F], filter: usize, @@ -213,6 +214,13 @@ fn generate_modular_op( let mut constr_poly = [0i64; 2 * N_LIMBS]; constr_poly[..2 * N_LIMBS - 1].copy_from_slice(&operation(input0_limbs, input1_limbs)); + // two_exp_256 == 2^256 + let two_exp_256 = { + let mut t = BigInt::zero(); + t.set_bit(256, true); + t + }; + let mut mod_is_zero = F::ZERO; if modulus.is_zero() { if filter == columns::IS_DIV { @@ -242,8 +250,8 @@ fn generate_modular_op( let quot = (&input - &output) / &modulus; // exact division; can be -ve let quot_limbs = bigint_to_columns::<{ 2 * N_LIMBS }>("); - // output < modulus here, so the proof requires (modulus - output). - let out_aux_red = bigint_to_columns::(&(modulus - output)); + // output < modulus here; the proof requires (output - modulus) % 2^256: + let out_aux_red = bigint_to_columns::(&(two_exp_256 - modulus + output)); // constr_poly is the array of coefficients of the polynomial // @@ -283,8 +291,20 @@ fn generate_modular_op( /// Generate the output and auxiliary values for modular operations. /// /// `filter` must be one of `columns::IS_{ADDMOD,MULMOD,MOD}`. -pub(crate) fn generate(lv: &mut [F], nv: &mut [F], filter: usize) { +pub(crate) fn generate( + lv: &mut [F], + nv: &mut [F], + filter: usize, + input0: U256, + input1: U256, + modulus: U256, +) { debug_assert!(lv.len() == NUM_ARITH_COLUMNS && nv.len() == NUM_ARITH_COLUMNS); + + u256_to_array(&mut lv[MODULAR_INPUT_0], input0); + u256_to_array(&mut lv[MODULAR_INPUT_1], input1); + u256_to_array(&mut lv[MODULAR_MODULUS], modulus); + match filter { columns::IS_ADDMOD => generate_modular_op(lv, nv, filter, pol_add), columns::IS_SUBMOD => generate_modular_op(lv, nv, filter, pol_sub), @@ -332,30 +352,30 @@ fn modular_constr_poly( yield_constr.constraint_transition(filter * (mod_is_zero * lv[IS_DIV] - div_denom_is_zero)); // Needed to compensate for adding mod_is_zero to modulus above, - // since the call eval_packed_generic_add_cc() below subtracts modulus + // since the call eval_packed_generic_addcy() below subtracts modulus // to verify in the case of a DIV. output[0] += div_denom_is_zero; // Verify that the output is reduced, i.e. output < modulus. let out_aux_red = &nv[MODULAR_OUT_AUX_RED]; - // This sets is_greater_than to 0 unless we get mod_is_zero when - // doing a DIV; in that case, we need is_greater_than=1, since - // eval_packed_generic_add_cc checks + // This sets is_less_than to 1 unless we get mod_is_zero when + // doing a DIV; in that case, we need is_less_than=0, since + // eval_packed_generic_addcy checks // - // output + out_aux_red == modulus + is_greater_than*2^256 + // modulus + out_aux_red == output + is_less_than*2^256 // - // and we were given output = out_aux_red - let is_greater_than = mod_is_zero * lv[IS_DIV]; + // and we are given output = out_aux_red when modulus is zero. + let is_less_than = P::ONES - mod_is_zero * lv[IS_DIV]; // NB: output and modulus in lv while out_aux_red and - // is_greater_than (via mod_is_zero) depend on nv, hence the + // is_less_than (via mod_is_zero) depend on nv, hence the // 'is_two_row_op' argument is set to 'true'. - eval_packed_generic_add_cc( + eval_packed_generic_addcy( yield_constr, filter, - &output, - out_aux_red, &modulus, - is_greater_than, + out_aux_red, + &output, + is_less_than, true, ); // restore output[0] @@ -483,16 +503,18 @@ fn modular_constr_poly_ext_circuit, const D: usize> output[0] = builder.add_extension(output[0], div_denom_is_zero); let out_aux_red = &nv[MODULAR_OUT_AUX_RED]; - let is_greater_than = builder.mul_extension(mod_is_zero, lv[IS_DIV]); + let one = builder.one_extension(); + let is_less_than = + builder.arithmetic_extension(F::NEG_ONE, F::ONE, mod_is_zero, lv[IS_DIV], one); - eval_ext_circuit_add_cc( + eval_ext_circuit_addcy( builder, yield_constr, filter, - &output, - out_aux_red, &modulus, - is_greater_than, + out_aux_red, + &output, + is_less_than, true, ); output[0] = builder.sub_extension(output[0], div_denom_is_zero); @@ -574,7 +596,6 @@ pub(crate) fn eval_ext_circuit, const D: usize>( #[cfg(test)] mod tests { - use itertools::izip; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::field::types::{Field, Sample}; use rand::{Rng, SeedableRng}; @@ -620,38 +641,40 @@ mod tests { type F = GoldilocksField; let mut rng = ChaCha8Rng::seed_from_u64(0x6feb51b7ec230f25); - let mut lv = [F::default(); NUM_ARITH_COLUMNS].map(|_| F::sample(&mut rng)); - let mut nv = [F::default(); NUM_ARITH_COLUMNS].map(|_| F::sample(&mut rng)); for op_filter in [IS_ADDMOD, IS_DIV, IS_SUBMOD, IS_MOD, IS_MULMOD] { - // Reset operation columns, then select one - lv[IS_ADDMOD] = F::ZERO; - lv[IS_SUBMOD] = F::ZERO; - lv[IS_MULMOD] = F::ZERO; - lv[IS_MOD] = F::ZERO; - lv[IS_DIV] = F::ZERO; - lv[op_filter] = F::ONE; - for i in 0..N_RND_TESTS { // set inputs to random values - for (ai, bi, mi) in izip!(MODULAR_INPUT_0, MODULAR_INPUT_1, MODULAR_MODULUS) { - lv[ai] = F::from_canonical_u16(rng.gen()); - lv[bi] = F::from_canonical_u16(rng.gen()); - lv[mi] = F::from_canonical_u16(rng.gen()); - } + let mut lv = [F::default(); NUM_ARITH_COLUMNS] + .map(|_| F::from_canonical_u16(rng.gen::())); + let mut nv = [F::default(); NUM_ARITH_COLUMNS] + .map(|_| F::from_canonical_u16(rng.gen::())); + // Reset operation columns, then select one + lv[IS_ADDMOD] = F::ZERO; + lv[IS_SUBMOD] = F::ZERO; + lv[IS_MULMOD] = F::ZERO; + lv[IS_MOD] = F::ZERO; + lv[IS_DIV] = F::ZERO; + lv[op_filter] = F::ONE; + + let input0 = U256::from(rng.gen::<[u8; 32]>()); + let input1 = U256::from(rng.gen::<[u8; 32]>()); + + let mut modulus_limbs = [0u8; 32]; // For the second half of the tests, set the top // 16-start digits of the modulus to zero so it is // much smaller than the inputs. if i > N_RND_TESTS / 2 { // 1 <= start < N_LIMBS - let start = (rng.gen::() % (N_LIMBS - 1)) + 1; - for mi in MODULAR_MODULUS.skip(start) { - lv[mi] = F::ZERO; + let start = (rng.gen::() % (modulus_limbs.len() - 1)) + 1; + for mi in modulus_limbs.iter_mut().skip(start) { + *mi = 0u8; } } + let modulus = U256::from(modulus_limbs); - generate(&mut lv, &mut nv, op_filter); + generate(&mut lv, &mut nv, op_filter, input0, input1, modulus); let mut constraint_consumer = ConstraintConsumer::new( vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)], @@ -672,29 +695,29 @@ mod tests { type F = GoldilocksField; let mut rng = ChaCha8Rng::seed_from_u64(0x6feb51b7ec230f25); - let mut lv = [F::default(); NUM_ARITH_COLUMNS].map(|_| F::sample(&mut rng)); - let mut nv = [F::default(); NUM_ARITH_COLUMNS].map(|_| F::sample(&mut rng)); for op_filter in [IS_ADDMOD, IS_SUBMOD, IS_DIV, IS_MOD, IS_MULMOD] { - // Reset operation columns, then select one - lv[IS_ADDMOD] = F::ZERO; - lv[IS_SUBMOD] = F::ZERO; - lv[IS_MULMOD] = F::ZERO; - lv[IS_MOD] = F::ZERO; - lv[IS_DIV] = F::ZERO; - lv[op_filter] = F::ONE; - for _i in 0..N_RND_TESTS { // set inputs to random values and the modulus to zero; // the output is defined to be zero when modulus is zero. + let mut lv = [F::default(); NUM_ARITH_COLUMNS] + .map(|_| F::from_canonical_u16(rng.gen::())); + let mut nv = [F::default(); NUM_ARITH_COLUMNS] + .map(|_| F::from_canonical_u16(rng.gen::())); - for (ai, bi, mi) in izip!(MODULAR_INPUT_0, MODULAR_INPUT_1, MODULAR_MODULUS) { - lv[ai] = F::from_canonical_u16(rng.gen()); - lv[bi] = F::from_canonical_u16(rng.gen()); - lv[mi] = F::ZERO; - } + // Reset operation columns, then select one + lv[IS_ADDMOD] = F::ZERO; + lv[IS_SUBMOD] = F::ZERO; + lv[IS_MULMOD] = F::ZERO; + lv[IS_MOD] = F::ZERO; + lv[IS_DIV] = F::ZERO; + lv[op_filter] = F::ONE; - generate(&mut lv, &mut nv, op_filter); + let input0 = U256::from(rng.gen::<[u8; 32]>()); + let input1 = U256::from(rng.gen::<[u8; 32]>()); + let modulus = U256::zero(); + + generate(&mut lv, &mut nv, op_filter, input0, input1, modulus); // check that the correct output was generated if op_filter == IS_DIV { diff --git a/evm/src/arithmetic/mul.rs b/evm/src/arithmetic/mul.rs index f12b407a..03acfa97 100644 --- a/evm/src/arithmetic/mul.rs +++ b/evm/src/arithmetic/mul.rs @@ -55,9 +55,10 @@ //! file `modular.rs`), we don't need to check that output is reduced, //! since any value of output is less than β^16 and is hence reduced. +use ethereum_types::U256; use plonky2::field::extension::Extendable; use plonky2::field::packed::PackedField; -use plonky2::field::types::Field; +use plonky2::field::types::{Field, PrimeField64}; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::plonk::circuit_builder::CircuitBuilder; @@ -66,7 +67,12 @@ use crate::arithmetic::columns::*; use crate::arithmetic::utils::*; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -pub fn generate(lv: &mut [F]) { +pub fn generate(lv: &mut [F], left_in: U256, right_in: U256) { + // TODO: It would probably be clearer/cleaner to read the U256 + // into an [i64;N] and then copy that to the lv table. + u256_to_array(&mut lv[MUL_INPUT_0], left_in); + u256_to_array(&mut lv[MUL_INPUT_1], right_in); + let input0 = read_value_i64_limbs(lv, MUL_INPUT_0); let input1 = read_value_i64_limbs(lv, MUL_INPUT_1); @@ -252,7 +258,9 @@ mod tests { lv[bi] = F::from_canonical_u16(rng.gen()); } - generate(&mut lv); + let left_in = U256::from(rng.gen::<[u8; 32]>()); + let right_in = U256::from(rng.gen::<[u8; 32]>()); + generate(&mut lv, left_in, right_in); let mut constraint_consumer = ConstraintConsumer::new( vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)], diff --git a/evm/src/arithmetic/operations.rs b/evm/src/arithmetic/operations.rs deleted file mode 100644 index f8aed566..00000000 --- a/evm/src/arithmetic/operations.rs +++ /dev/null @@ -1,166 +0,0 @@ -use ethereum_types::U256; -use plonky2::hash::hash_types::RichField; -use static_assertions::const_assert; - -use crate::arithmetic::columns::*; -use crate::arithmetic::{addcc, modular, mul}; - -#[inline] -fn u64_to_array(out: &mut [F], x: u64) { - const_assert!(LIMB_BITS == 16); - debug_assert!(out.len() == 4); - - out[0] = F::from_canonical_u16(x as u16); - out[1] = F::from_canonical_u16((x >> 16) as u16); - out[2] = F::from_canonical_u16((x >> 32) as u16); - out[3] = F::from_canonical_u16((x >> 48) as u16); -} - -fn u256_to_array(out: &mut [F], x: U256) { - const_assert!(N_LIMBS == 16); - debug_assert!(out.len() == N_LIMBS); - - u64_to_array(&mut out[0..4], x.0[0]); - u64_to_array(&mut out[4..8], x.0[1]); - u64_to_array(&mut out[8..12], x.0[2]); - u64_to_array(&mut out[12..16], x.0[3]); -} - -pub trait Operation { - /// Convert operation into one or two rows of the trace. - /// - /// Morally these types should be [F; NUM_ARITH_COLUMNS], but we - /// use vectors because that's what utils::transpose expects. - fn to_rows(&self) -> (Vec, Option>); -} - -pub struct SimpleBinaryOp { - /// The operation is identified using the associated filter from - /// `columns::IS_ADD` etc., stored in `op_filter`. - op_filter: usize, - input0: U256, - input1: U256, -} - -impl SimpleBinaryOp { - pub fn new(op_filter: usize, input0: U256, input1: U256) -> Self { - assert!( - op_filter == IS_ADD - || op_filter == IS_SUB - || op_filter == IS_MUL - || op_filter == IS_LT - || op_filter == IS_GT - ); - Self { - op_filter, - input0, - input1, - } - } -} - -impl Operation for SimpleBinaryOp { - fn to_rows(&self) -> (Vec, Option>) { - let mut row = vec![F::ZERO; NUM_ARITH_COLUMNS]; - row[self.op_filter] = F::ONE; - - if self.op_filter == IS_SUB || self.op_filter == IS_GT { - u256_to_array(&mut row[GENERAL_REGISTER_2], self.input0); - u256_to_array(&mut row[GENERAL_REGISTER_0], self.input1); - } else if self.op_filter == IS_LT { - u256_to_array(&mut row[GENERAL_REGISTER_0], self.input0); - u256_to_array(&mut row[GENERAL_REGISTER_2], self.input1); - } else { - assert!( - self.op_filter == IS_ADD || self.op_filter == IS_MUL, - "unrecognised operation" - ); - u256_to_array(&mut row[GENERAL_REGISTER_0], self.input0); - u256_to_array(&mut row[GENERAL_REGISTER_1], self.input1); - } - - if self.op_filter == IS_MUL { - mul::generate(&mut row); - } else { - addcc::generate(&mut row, self.op_filter); - } - (row, None) - } -} - -pub struct ModularBinaryOp { - op_filter: usize, - input0: U256, - input1: U256, - modulus: U256, -} - -impl ModularBinaryOp { - pub fn new(op_filter: usize, input0: U256, input1: U256, modulus: U256) -> Self { - assert!(op_filter == IS_ADDMOD || op_filter == IS_SUBMOD || op_filter == IS_MULMOD); - Self { - op_filter, - input0, - input1, - modulus, - } - } -} - -fn modular_to_rows_helper( - op_filter: usize, - input0: U256, - input1: U256, - modulus: U256, -) -> (Vec, Option>) { - let mut row1 = vec![F::ZERO; NUM_ARITH_COLUMNS]; - let mut row2 = vec![F::ZERO; NUM_ARITH_COLUMNS]; - - row1[op_filter] = F::ONE; - - u256_to_array(&mut row1[MODULAR_INPUT_0], input0); - u256_to_array(&mut row1[MODULAR_INPUT_1], input1); - u256_to_array(&mut row1[MODULAR_MODULUS], modulus); - - modular::generate(&mut row1, &mut row2, op_filter); - - (row1, Some(row2)) -} - -impl Operation for ModularBinaryOp { - fn to_rows(&self) -> (Vec, Option>) { - modular_to_rows_helper(self.op_filter, self.input0, self.input1, self.modulus) - } -} - -pub struct ModOp { - pub input: U256, - pub modulus: U256, -} - -impl Operation for ModOp { - fn to_rows(&self) -> (Vec, Option>) { - modular_to_rows_helper(IS_MOD, self.input, U256::zero(), self.modulus) - } -} - -pub struct DivOp { - pub numerator: U256, - pub denominator: U256, -} - -impl Operation for DivOp { - fn to_rows(&self) -> (Vec, Option>) { - let mut row1 = vec![F::ZERO; NUM_ARITH_COLUMNS]; - let mut row2 = vec![F::ZERO; NUM_ARITH_COLUMNS]; - - row1[IS_DIV] = F::ONE; - - u256_to_array(&mut row1[DIV_NUMERATOR], self.numerator); - u256_to_array(&mut row1[DIV_DENOMINATOR], self.denominator); - - modular::generate(&mut row1, &mut row2, IS_DIV); - - (row1, Some(row2)) - } -} diff --git a/evm/src/arithmetic/utils.rs b/evm/src/arithmetic/utils.rs index 7eb33099..8b4f546a 100644 --- a/evm/src/arithmetic/utils.rs +++ b/evm/src/arithmetic/utils.rs @@ -1,11 +1,14 @@ use std::ops::{Add, AddAssign, Mul, Neg, Range, Shr, Sub, SubAssign}; +use ethereum_types::U256; use plonky2::field::extension::Extendable; +use plonky2::field::types::{Field, PrimeField64}; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::plonk::circuit_builder::CircuitBuilder; +use static_assertions::const_assert; -use crate::arithmetic::columns::N_LIMBS; +use crate::arithmetic::columns::{LIMB_BITS, N_LIMBS}; /// Return an array of `N` zeros of type T. pub(crate) fn pol_zero() -> [T; N] @@ -315,24 +318,35 @@ pub(crate) fn read_value(lv: &[T], value_idxs: Range( - lv: &[F], - value_idxs: Range, -) -> [u64; N] { - let limbs: [_; N] = lv[value_idxs].try_into().unwrap(); - limbs.map(|c| F::to_canonical_u64(&c)) -} - /// Read the range `value_idxs` of values from `lv` into an array of /// length `N`, interpreting the values as `i64`s. Panics if the /// length of the range is not `N`. -pub(crate) fn read_value_i64_limbs( +pub(crate) fn read_value_i64_limbs( lv: &[F], value_idxs: Range, ) -> [i64; N] { let limbs: [_; N] = lv[value_idxs].try_into().unwrap(); - limbs.map(|c| F::to_canonical_u64(&c) as i64) + limbs.map(|c| c.to_canonical_u64() as i64) +} + +#[inline] +fn u64_to_array(out: &mut [F], x: u64) { + const_assert!(LIMB_BITS == 16); + debug_assert!(out.len() == 4); + + out[0] = F::from_canonical_u16(x as u16); + out[1] = F::from_canonical_u16((x >> 16) as u16); + out[2] = F::from_canonical_u16((x >> 32) as u16); + out[3] = F::from_canonical_u16((x >> 48) as u16); +} + +// TODO: Refactor/replace u256_limbs in evm/src/util.rs +pub(crate) fn u256_to_array(out: &mut [F], x: U256) { + const_assert!(N_LIMBS == 16); + debug_assert!(out.len() == N_LIMBS); + + u64_to_array(&mut out[0..4], x.0[0]); + u64_to_array(&mut out[4..8], x.0[1]); + u64_to_array(&mut out[8..12], x.0[2]); + u64_to_array(&mut out[12..16], x.0[3]); } diff --git a/evm/src/cpu/columns/ops.rs b/evm/src/cpu/columns/ops.rs index 63f6795d..1000d2fa 100644 --- a/evm/src/cpu/columns/ops.rs +++ b/evm/src/cpu/columns/ops.rs @@ -13,12 +13,13 @@ pub struct OpsColumnsView { pub sub: T, pub div: T, pub mod_: T, - // TODO: combine ADDMOD, MULMOD into one flag + // TODO: combine ADDMOD, MULMOD and SUBMOD into one flag pub addmod: T, pub mulmod: T, pub addfp254: T, pub mulfp254: T, pub subfp254: T, + pub submod: T, pub lt: T, pub gt: T, pub eq: T, // Note: This column must be 0 when is_cpu_cycle = 0. diff --git a/evm/src/cpu/stack.rs b/evm/src/cpu/stack.rs index 7f44c7b3..ee96a682 100644 --- a/evm/src/cpu/stack.rs +++ b/evm/src/cpu/stack.rs @@ -50,6 +50,7 @@ const STACK_BEHAVIORS: OpsColumnsView> = OpsColumnsView { addfp254: BASIC_BINARY_OP, mulfp254: BASIC_BINARY_OP, subfp254: BASIC_BINARY_OP, + submod: BASIC_TERNARY_OP, lt: BASIC_BINARY_OP, gt: BASIC_BINARY_OP, eq: BASIC_BINARY_OP, diff --git a/evm/src/witness/transition.rs b/evm/src/witness/transition.rs index b8e46d78..a60141c5 100644 --- a/evm/src/witness/transition.rs +++ b/evm/src/witness/transition.rs @@ -51,6 +51,9 @@ fn decode(registers: RegistersState, opcode: u8) -> Result Ok(Operation::BinaryArithmetic( arithmetic::BinaryOperator::SubFp254, )), + (0x0f, true) => Ok(Operation::TernaryArithmetic( + arithmetic::TernaryOperator::SubMod, + )), (0x10, _) => Ok(Operation::BinaryArithmetic(arithmetic::BinaryOperator::Lt)), (0x11, _) => Ok(Operation::BinaryArithmetic(arithmetic::BinaryOperator::Gt)), (0x12, _) => Ok(Operation::Syscall(opcode)), @@ -167,6 +170,7 @@ fn fill_op_flag(op: Operation, row: &mut CpuColumnsView) { Operation::BinaryArithmetic(arithmetic::BinaryOperator::SubFp254) => &mut flags.subfp254, Operation::TernaryArithmetic(arithmetic::TernaryOperator::AddMod) => &mut flags.addmod, Operation::TernaryArithmetic(arithmetic::TernaryOperator::MulMod) => &mut flags.mulmod, + Operation::TernaryArithmetic(arithmetic::TernaryOperator::SubMod) => &mut flags.submod, Operation::KeccakGeneral => &mut flags.keccak_general, Operation::ProverInput => &mut flags.prover_input, Operation::Pop => &mut flags.pop, From 614c4ae69f7831b7ef7b6940607a767be105f977 Mon Sep 17 00:00:00 2001 From: Emanuele Cesena Date: Fri, 10 Feb 2023 08:20:22 -0600 Subject: [PATCH 2/4] Make le_sum public MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Methods like split_le are public. le_sum is the inverse map so useful to have it pub as well (at the very least, we’d like to access it). --- plonky2/src/gadgets/split_base.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plonky2/src/gadgets/split_base.rs b/plonky2/src/gadgets/split_base.rs index 5fbe8053..dd0edf5d 100644 --- a/plonky2/src/gadgets/split_base.rs +++ b/plonky2/src/gadgets/split_base.rs @@ -33,7 +33,7 @@ impl, const D: usize> CircuitBuilder { /// Takes an iterator of bits `(b_i)` and returns `sum b_i * 2^i`, i.e., /// the number with little-endian bit representation given by `bits`. - pub(crate) fn le_sum(&mut self, bits: impl Iterator>) -> Target { + pub fn le_sum(&mut self, bits: impl Iterator>) -> Target { let bits = bits.map(|b| *b.borrow()).collect_vec(); let num_bits = bits.len(); assert!( From ff80f28b938bd5afb55b8cd869afc0134108571f Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Fri, 10 Feb 2023 21:47:51 -0800 Subject: [PATCH 3/4] Revert "Set CI to use an older version of nightly" This reverts commit da23fb116b934925f8a5cf37c2f1f092452fdc4d. --- .../continuous-integration-workflow.yml | 4 +- evm/src/cross_table_lookup.rs | 17 ++------ evm/src/fixed_recursive_verifier.rs | 2 +- evm/src/permutation.rs | 8 ++-- evm/src/prover.rs | 10 ++--- evm/src/recursive_verifier.rs | 4 +- evm/src/vanishing_poly.rs | 11 ++--- evm/src/verifier.rs | 4 +- plonky2/benches/field_arithmetic.rs | 15 +++---- plonky2/examples/bench_recursion.rs | 2 +- plonky2/src/fri/challenges.rs | 2 +- plonky2/src/fri/proof.rs | 10 ++--- plonky2/src/fri/recursive_verifier.rs | 20 ++++----- plonky2/src/hash/merkle_tree.rs | 1 - plonky2/src/iop/ext_target.rs | 4 +- plonky2/src/plonk/get_challenges.rs | 2 +- plonky2/src/plonk/proof.rs | 4 +- plonky2/src/plonk/prover.rs | 2 +- plonky2/src/plonk/vanishing_poly.rs | 43 ++++--------------- plonky2/src/plonk/verifier.rs | 2 +- .../conditional_recursive_verifier.rs | 4 +- plonky2/src/recursion/cyclic_recursion.rs | 10 ++--- plonky2/src/recursion/dummy_circuit.rs | 2 +- plonky2/src/recursion/recursive_verifier.rs | 20 +++------ starky/src/permutation.rs | 8 ++-- starky/src/prover.rs | 6 +-- starky/src/recursive_verifier.rs | 2 +- starky/src/vanishing_poly.rs | 9 ++-- starky/src/verifier.rs | 2 +- 29 files changed, 82 insertions(+), 148 deletions(-) diff --git a/.github/workflows/continuous-integration-workflow.yml b/.github/workflows/continuous-integration-workflow.yml index 0e3e5977..03924ba6 100644 --- a/.github/workflows/continuous-integration-workflow.yml +++ b/.github/workflows/continuous-integration-workflow.yml @@ -24,7 +24,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: nightly-2022-11-23 + toolchain: nightly override: true - name: rust-cache @@ -61,7 +61,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: nightly-2022-11-23 + toolchain: nightly override: true components: rustfmt, clippy diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index 1b184a50..15e186aa 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -217,7 +217,7 @@ impl CtlData { } } -pub(crate) fn cross_table_lookup_data, const D: usize>( +pub(crate) fn cross_table_lookup_data( trace_poly_values: &[Vec>; NUM_TABLES], cross_table_lookups: &[CrossTableLookup], ctl_challenges: &GrandProductChallengeSet, @@ -371,7 +371,7 @@ impl<'a, F: RichField + Extendable, const D: usize> } } -pub(crate) fn eval_cross_table_lookup_checks( +pub(crate) fn eval_cross_table_lookup_checks( vars: StarkEvaluationVars, ctl_vars: &[CtlCheckVars], consumer: &mut ConstraintConsumer

, @@ -379,7 +379,6 @@ pub(crate) fn eval_cross_table_lookup_checks, FE: FieldExtension, P: PackedField, - C: GenericConfig, S: Stark, { for lookup_vars in ctl_vars { @@ -540,11 +539,7 @@ pub(crate) fn eval_cross_table_lookup_checks_circuit< } } -pub(crate) fn verify_cross_table_lookups< - F: RichField + Extendable, - C: GenericConfig, - const D: usize, ->( +pub(crate) fn verify_cross_table_lookups, const D: usize>( cross_table_lookups: &[CrossTableLookup], ctl_zs_lasts: [Vec; NUM_TABLES], config: &StarkConfig, @@ -573,11 +568,7 @@ pub(crate) fn verify_cross_table_lookups< Ok(()) } -pub(crate) fn verify_cross_table_lookups_circuit< - F: RichField + Extendable, - C: GenericConfig, - const D: usize, ->( +pub(crate) fn verify_cross_table_lookups_circuit, const D: usize>( builder: &mut CircuitBuilder, cross_table_lookups: Vec>, ctl_zs_lasts: [Vec; NUM_TABLES], diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 344b6d1c..e662874f 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -228,7 +228,7 @@ where } // Verify the CTL checks. - verify_cross_table_lookups_circuit::( + verify_cross_table_lookups_circuit::( &mut builder, all_cross_table_lookups(), pis.map(|p| p.ctl_zs_last), diff --git a/evm/src/permutation.rs b/evm/src/permutation.rs index 64223ad7..a92774cb 100644 --- a/evm/src/permutation.rs +++ b/evm/src/permutation.rs @@ -13,7 +13,7 @@ use plonky2::iop::challenger::{Challenger, RecursiveChallenger}; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::iop::target::Target; use plonky2::plonk::circuit_builder::CircuitBuilder; -use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher}; +use plonky2::plonk::config::{AlgebraicHasher, Hasher}; use plonky2::plonk::plonk_common::{reduce_with_powers, reduce_with_powers_ext_circuit}; use plonky2::util::reducing::{ReducingFactor, ReducingFactorTarget}; use plonky2_maybe_rayon::*; @@ -89,7 +89,7 @@ pub(crate) struct GrandProductChallengeSet { } /// Compute all Z polynomials (for permutation arguments). -pub(crate) fn compute_permutation_z_polys( +pub(crate) fn compute_permutation_z_polys( stark: &S, config: &StarkConfig, trace_poly_values: &[PolynomialValues], @@ -97,7 +97,6 @@ pub(crate) fn compute_permutation_z_polys( ) -> Vec> where F: RichField + Extendable, - C: GenericConfig, S: Stark, { let permutation_pairs = stark.permutation_pairs(); @@ -286,7 +285,7 @@ where pub(crate) permutation_challenge_sets: Vec>, } -pub(crate) fn eval_permutation_checks( +pub(crate) fn eval_permutation_checks( stark: &S, config: &StarkConfig, vars: StarkEvaluationVars, @@ -296,7 +295,6 @@ pub(crate) fn eval_permutation_checks, FE: FieldExtension, P: PackedField, - C: GenericConfig, S: Stark, { let PermutationCheckVars { diff --git a/evm/src/prover.rs b/evm/src/prover.rs index b4ea0d90..c801950a 100644 --- a/evm/src/prover.rs +++ b/evm/src/prover.rs @@ -124,7 +124,7 @@ where let ctl_data_per_table = timed!( timing, "compute CTL data", - cross_table_lookup_data::( + cross_table_lookup_data::( &trace_poly_values, &all_stark.cross_table_lookups, &ctl_challenges, @@ -286,7 +286,7 @@ where timed!( timing, "compute permutation Z(x) polys", - compute_permutation_z_polys::(stark, config, trace_poly_values, challenges) + compute_permutation_z_polys::(stark, config, trace_poly_values, challenges) ) }); let num_permutation_zs = permutation_zs.as_ref().map(|v| v.len()).unwrap_or(0); @@ -533,7 +533,7 @@ where filter_column: &zs_columns.filter_column, }) .collect::>(); - eval_vanishing_poly::( + eval_vanishing_poly::( stark, config, vars, @@ -550,7 +550,7 @@ where let num_challenges = alphas.len(); - (0..P::WIDTH).into_iter().map(move |i| { + (0..P::WIDTH).map(move |i| { (0..num_challenges) .map(|j| constraints_evals[j].as_slice()[i]) .collect() @@ -651,7 +651,7 @@ fn check_constraints<'a, F, C, S, const D: usize>( filter_column: &zs_columns.filter_column, }) .collect::>(); - eval_vanishing_poly::( + eval_vanishing_poly::( stark, config, vars, diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index 1fba88e3..d3018ba4 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -132,7 +132,7 @@ impl, C: GenericConfig, const D: usize> } // Verify the CTL checks. - verify_cross_table_lookups::( + verify_cross_table_lookups::( &cross_table_lookups, pis.map(|p| p.ctl_zs_last), inner_config, @@ -393,7 +393,7 @@ fn verify_stark_proof_with_challenges_circuit< with_context!( builder, "evaluate vanishing polynomial", - eval_vanishing_poly_circuit::( + eval_vanishing_poly_circuit::( builder, stark, inner_config, diff --git a/evm/src/vanishing_poly.rs b/evm/src/vanishing_poly.rs index e776fa5c..3a2da78c 100644 --- a/evm/src/vanishing_poly.rs +++ b/evm/src/vanishing_poly.rs @@ -2,7 +2,6 @@ use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::field::packed::PackedField; use plonky2::hash::hash_types::RichField; use plonky2::plonk::circuit_builder::CircuitBuilder; -use plonky2::plonk::config::GenericConfig; use crate::config::StarkConfig; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; @@ -17,7 +16,7 @@ use crate::permutation::{ use crate::stark::Stark; use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; -pub(crate) fn eval_vanishing_poly( +pub(crate) fn eval_vanishing_poly( stark: &S, config: &StarkConfig, vars: StarkEvaluationVars, @@ -28,12 +27,11 @@ pub(crate) fn eval_vanishing_poly, FE: FieldExtension, P: PackedField, - C: GenericConfig, S: Stark, { stark.eval_packed_generic(vars, consumer); if let Some(permutation_vars) = permutation_vars { - eval_permutation_checks::( + eval_permutation_checks::( stark, config, vars, @@ -41,10 +39,10 @@ pub(crate) fn eval_vanishing_poly(vars, ctl_vars, consumer); + eval_cross_table_lookup_checks::(vars, ctl_vars, consumer); } -pub(crate) fn eval_vanishing_poly_circuit( +pub(crate) fn eval_vanishing_poly_circuit( builder: &mut CircuitBuilder, stark: &S, config: &StarkConfig, @@ -54,7 +52,6 @@ pub(crate) fn eval_vanishing_poly_circuit( consumer: &mut RecursiveConstraintConsumer, ) where F: RichField + Extendable, - C: GenericConfig, S: Stark, [(); S::COLUMNS]:, { diff --git a/evm/src/verifier.rs b/evm/src/verifier.rs index c6d0373e..b4b2f6bd 100644 --- a/evm/src/verifier.rs +++ b/evm/src/verifier.rs @@ -97,7 +97,7 @@ where config, )?; - verify_cross_table_lookups::( + verify_cross_table_lookups::( cross_table_lookups, all_proof.stark_proofs.map(|p| p.proof.openings.ctl_zs_last), config, @@ -155,7 +155,7 @@ where next_zs: permutation_ctl_zs_next[..num_permutation_zs].to_vec(), permutation_challenge_sets: challenges.permutation_challenge_sets.clone().unwrap(), }); - eval_vanishing_poly::( + eval_vanishing_poly::( stark, config, vars, diff --git a/plonky2/benches/field_arithmetic.rs b/plonky2/benches/field_arithmetic.rs index a3b66fa1..9db45a50 100644 --- a/plonky2/benches/field_arithmetic.rs +++ b/plonky2/benches/field_arithmetic.rs @@ -119,7 +119,7 @@ pub(crate) fn bench_field(c: &mut Criterion) { &format!("batch_multiplicative_inverse-tiny<{}>", type_name::()), |b| { b.iter_batched( - || (0..2).into_iter().map(|_| F::rand()).collect::>(), + || (0..2).map(|_| F::rand()).collect::>(), |x| F::batch_multiplicative_inverse(&x), BatchSize::SmallInput, ) @@ -130,7 +130,7 @@ pub(crate) fn bench_field(c: &mut Criterion) { &format!("batch_multiplicative_inverse-small<{}>", type_name::()), |b| { b.iter_batched( - || (0..4).into_iter().map(|_| F::rand()).collect::>(), + || (0..4).map(|_| F::rand()).collect::>(), |x| F::batch_multiplicative_inverse(&x), BatchSize::SmallInput, ) @@ -141,7 +141,7 @@ pub(crate) fn bench_field(c: &mut Criterion) { &format!("batch_multiplicative_inverse-medium<{}>", type_name::()), |b| { b.iter_batched( - || (0..16).into_iter().map(|_| F::rand()).collect::>(), + || (0..16).map(|_| F::rand()).collect::>(), |x| F::batch_multiplicative_inverse(&x), BatchSize::SmallInput, ) @@ -152,7 +152,7 @@ pub(crate) fn bench_field(c: &mut Criterion) { &format!("batch_multiplicative_inverse-large<{}>", type_name::()), |b| { b.iter_batched( - || (0..256).into_iter().map(|_| F::rand()).collect::>(), + || (0..256).map(|_| F::rand()).collect::>(), |x| F::batch_multiplicative_inverse(&x), BatchSize::LargeInput, ) @@ -163,12 +163,7 @@ pub(crate) fn bench_field(c: &mut Criterion) { &format!("batch_multiplicative_inverse-huge<{}>", type_name::()), |b| { b.iter_batched( - || { - (0..65536) - .into_iter() - .map(|_| F::rand()) - .collect::>() - }, + || (0..65536).map(|_| F::rand()).collect::>(), |x| F::batch_multiplicative_inverse(&x), BatchSize::LargeInput, ) diff --git a/plonky2/examples/bench_recursion.rs b/plonky2/examples/bench_recursion.rs index 5a1196e1..9b06b435 100644 --- a/plonky2/examples/bench_recursion.rs +++ b/plonky2/examples/bench_recursion.rs @@ -103,7 +103,7 @@ where { let (inner_proof, inner_vd, inner_cd) = inner; let mut builder = CircuitBuilder::::new(config.clone()); - let pt = builder.add_virtual_proof_with_pis::(inner_cd); + let pt = builder.add_virtual_proof_with_pis(inner_cd); let inner_data = builder.add_virtual_verifier_data(inner_cd.config.fri_config.cap_height); diff --git a/plonky2/src/fri/challenges.rs b/plonky2/src/fri/challenges.rs index 7a184504..41b00a7e 100644 --- a/plonky2/src/fri/challenges.rs +++ b/plonky2/src/fri/challenges.rs @@ -73,7 +73,7 @@ impl, H: AlgebraicHasher, const D: usize> } } - pub fn fri_challenges>( + pub fn fri_challenges( &mut self, builder: &mut CircuitBuilder, commit_phase_merkle_caps: &[MerkleCapTarget], diff --git a/plonky2/src/fri/proof.rs b/plonky2/src/fri/proof.rs index f841b274..0e0d7df3 100644 --- a/plonky2/src/fri/proof.rs +++ b/plonky2/src/fri/proof.rs @@ -15,7 +15,7 @@ use crate::hash::merkle_tree::MerkleCap; use crate::hash::path_compression::{compress_merkle_proofs, decompress_merkle_proofs}; use crate::iop::ext_target::ExtensionTarget; use crate::iop::target::Target; -use crate::plonk::config::{GenericConfig, Hasher}; +use crate::plonk::config::Hasher; use crate::plonk::plonk_common::salt_size; use crate::plonk::proof::{FriInferredElements, ProofChallenges}; @@ -135,11 +135,7 @@ pub struct CompressedFriProof, H: Hasher, const impl, H: Hasher, const D: usize> FriProof { /// Compress all the Merkle paths in the FRI proof and remove duplicate indices. - pub fn compress>( - self, - indices: &[usize], - params: &FriParams, - ) -> CompressedFriProof { + pub fn compress(self, indices: &[usize], params: &FriParams) -> CompressedFriProof { let FriProof { commit_phase_merkle_caps, query_round_proofs, @@ -241,7 +237,7 @@ impl, H: Hasher, const D: usize> FriProof, H: Hasher, const D: usize> CompressedFriProof { /// Decompress all the Merkle paths in the FRI proof and reinsert duplicate indices. - pub(crate) fn decompress>( + pub(crate) fn decompress( self, challenges: &ProofChallenges, fri_inferred_elements: FriInferredElements, diff --git a/plonky2/src/fri/recursive_verifier.rs b/plonky2/src/fri/recursive_verifier.rs index ac74f50f..8e9329d5 100644 --- a/plonky2/src/fri/recursive_verifier.rs +++ b/plonky2/src/fri/recursive_verifier.rs @@ -25,7 +25,7 @@ use crate::with_context; impl, const D: usize> CircuitBuilder { /// Computes P'(x^arity) from {P(x*g^i)}_(i=0..arity), where g is a `arity`-th root of unity /// and P' is the FRI reduced polynomial. - fn compute_evaluation>( + fn compute_evaluation( &mut self, x: Target, x_index_within_coset_bits: &[BoolTarget], @@ -58,7 +58,7 @@ impl, const D: usize> CircuitBuilder { /// Make sure we have enough wires and routed wires to do the FRI checks efficiently. This check /// isn't required -- without it we'd get errors elsewhere in the stack -- but just gives more /// helpful errors. - fn check_recursion_config>(&self, max_fri_arity_bits: usize) { + fn check_recursion_config(&self, max_fri_arity_bits: usize) { let random_access = RandomAccessGate::::new_from_config( &self.config, max_fri_arity_bits.max(self.config.fri_config.cap_height), @@ -91,11 +91,7 @@ impl, const D: usize> CircuitBuilder { ); } - fn fri_verify_proof_of_work>( - &mut self, - fri_pow_response: Target, - config: &FriConfig, - ) { + fn fri_verify_proof_of_work(&mut self, fri_pow_response: Target, config: &FriConfig) { self.assert_leading_zeros( fri_pow_response, config.proof_of_work_bits + (64 - F::order().bits()) as u32, @@ -114,7 +110,7 @@ impl, const D: usize> CircuitBuilder { C::Hasher: AlgebraicHasher, { if let Some(max_arity_bits) = params.max_arity_bits() { - self.check_recursion_config::(max_arity_bits); + self.check_recursion_config(max_arity_bits); } debug_assert_eq!( @@ -129,7 +125,7 @@ impl, const D: usize> CircuitBuilder { with_context!( self, "check PoW", - self.fri_verify_proof_of_work::(challenges.fri_pow_response, ¶ms.config) + self.fri_verify_proof_of_work(challenges.fri_pow_response, ¶ms.config) ); // Check that parameters are coherent. @@ -206,7 +202,7 @@ impl, const D: usize> CircuitBuilder { } } - fn fri_combine_initial>( + fn fri_combine_initial( &mut self, instance: &FriInstanceInfoTarget, proof: &FriInitialTreeProofTarget, @@ -298,7 +294,7 @@ impl, const D: usize> CircuitBuilder { let mut old_eval = with_context!( self, "combine initial oracles", - self.fri_combine_initial::( + self.fri_combine_initial( instance, &round_proof.initial_trees_proof, challenges.fri_alpha, @@ -324,7 +320,7 @@ impl, const D: usize> CircuitBuilder { old_eval = with_context!( self, "infer evaluation using interpolation", - self.compute_evaluation::( + self.compute_evaluation( subgroup_x, x_index_within_coset_bits, arity_bits, diff --git a/plonky2/src/hash/merkle_tree.rs b/plonky2/src/hash/merkle_tree.rs index e0c8e79d..e884554f 100644 --- a/plonky2/src/hash/merkle_tree.rs +++ b/plonky2/src/hash/merkle_tree.rs @@ -184,7 +184,6 @@ impl> MerkleTree { // Mask out high bits to get the index within the sub-tree. let mut pair_index = leaf_index & ((1 << num_layers) - 1); let siblings = (0..num_layers) - .into_iter() .map(|i| { let parity = pair_index & 1; pair_index >>= 1; diff --git a/plonky2/src/iop/ext_target.rs b/plonky2/src/iop/ext_target.rs index c9929b21..08bdcba0 100644 --- a/plonky2/src/iop/ext_target.rs +++ b/plonky2/src/iop/ext_target.rs @@ -139,9 +139,7 @@ pub fn flatten_target(l: &[ExtensionTarget]) -> Vec { } /// Batch every D-sized chunks into extension targets. -pub fn unflatten_target, const D: usize>( - l: &[Target], -) -> Vec> { +pub fn unflatten_target(l: &[Target]) -> Vec> { debug_assert_eq!(l.len() % D, 0); l.chunks_exact(D) .map(|c| c.to_vec().try_into().unwrap()) diff --git a/plonky2/src/plonk/get_challenges.rs b/plonky2/src/plonk/get_challenges.rs index 240d9047..1f6bd7ae 100644 --- a/plonky2/src/plonk/get_challenges.rs +++ b/plonky2/src/plonk/get_challenges.rs @@ -277,7 +277,7 @@ impl, const D: usize> CircuitBuilder { plonk_gammas, plonk_alphas, plonk_zeta, - fri_challenges: challenger.fri_challenges::( + fri_challenges: challenger.fri_challenges( self, commit_phase_merkle_caps, final_poly, diff --git a/plonky2/src/plonk/proof.rs b/plonky2/src/plonk/proof.rs index ea616e8f..67884e01 100644 --- a/plonky2/src/plonk/proof.rs +++ b/plonky2/src/plonk/proof.rs @@ -65,7 +65,7 @@ impl, C: GenericConfig, const D: usize> P plonk_zs_partial_products_cap, quotient_polys_cap, openings, - opening_proof: opening_proof.compress::(indices, params), + opening_proof: opening_proof.compress(indices, params), } } } @@ -163,7 +163,7 @@ impl, C: GenericConfig, const D: usize> plonk_zs_partial_products_cap, quotient_polys_cap, openings, - opening_proof: opening_proof.decompress::(challenges, fri_inferred_elements, params), + opening_proof: opening_proof.decompress(challenges, fri_inferred_elements, params), } } } diff --git a/plonky2/src/plonk/prover.rs b/plonky2/src/plonk/prover.rs index f8f66ea1..f2b61226 100644 --- a/plonky2/src/plonk/prover.rs +++ b/plonky2/src/plonk/prover.rs @@ -420,7 +420,7 @@ fn compute_quotient_polys< public_inputs_hash, ); - let mut quotient_values_batch = eval_vanishing_poly_base_batch::( + let mut quotient_values_batch = eval_vanishing_poly_base_batch::( common_data, &indices_batch, &shifted_xs_batch, diff --git a/plonky2/src/plonk/vanishing_poly.rs b/plonky2/src/plonk/vanishing_poly.rs index 30473650..d1d06403 100644 --- a/plonky2/src/plonk/vanishing_poly.rs +++ b/plonky2/src/plonk/vanishing_poly.rs @@ -10,7 +10,6 @@ use crate::iop::ext_target::ExtensionTarget; use crate::iop::target::Target; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::circuit_data::CommonCircuitData; -use crate::plonk::config::GenericConfig; use crate::plonk::plonk_common; use crate::plonk::plonk_common::eval_l_0_circuit; use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBaseBatch}; @@ -22,11 +21,7 @@ use crate::with_context; /// Evaluate the vanishing polynomial at `x`. In this context, the vanishing polynomial is a random /// linear combination of gate constraints, plus some other terms relating to the permutation /// argument. All such terms should vanish on `H`. -pub(crate) fn eval_vanishing_poly< - F: RichField + Extendable, - C: GenericConfig, - const D: usize, ->( +pub(crate) fn eval_vanishing_poly, const D: usize>( common_data: &CommonCircuitData, x: F::Extension, vars: EvaluationVars, @@ -41,7 +36,7 @@ pub(crate) fn eval_vanishing_poly< let max_degree = common_data.quotient_degree_factor; let num_prods = common_data.num_partial_products; - let constraint_terms = evaluate_gate_constraints::(common_data, vars); + let constraint_terms = evaluate_gate_constraints::(common_data, vars); // The L_0(x) (Z(x) - 1) vanishing terms. let mut vanishing_z_1_terms = Vec::new(); @@ -97,11 +92,7 @@ pub(crate) fn eval_vanishing_poly< } /// Like `eval_vanishing_poly`, but specialized for base field points. Batched. -pub(crate) fn eval_vanishing_poly_base_batch< - F: RichField + Extendable, - C: GenericConfig, - const D: usize, ->( +pub(crate) fn eval_vanishing_poly_base_batch, const D: usize>( common_data: &CommonCircuitData, indices_batch: &[usize], xs_batch: &[F], @@ -129,7 +120,7 @@ pub(crate) fn eval_vanishing_poly_base_batch< let num_gate_constraints = common_data.num_gate_constraints; let constraint_terms_batch = - evaluate_gate_constraints_base_batch::(common_data, vars_batch); + evaluate_gate_constraints_base_batch::(common_data, vars_batch); debug_assert!(constraint_terms_batch.len() == n * num_gate_constraints); let num_challenges = common_data.config.num_challenges; @@ -208,11 +199,7 @@ pub(crate) fn eval_vanishing_poly_base_batch< /// `num_gate_constraints` is the largest number of constraints imposed by any gate. It is not /// strictly necessary, but it helps performance by ensuring that we allocate a vector with exactly /// the capacity that we need. -pub fn evaluate_gate_constraints< - F: RichField + Extendable, - C: GenericConfig, - const D: usize, ->( +pub fn evaluate_gate_constraints, const D: usize>( common_data: &CommonCircuitData, vars: EvaluationVars, ) -> Vec { @@ -242,11 +229,7 @@ pub fn evaluate_gate_constraints< /// Returns a vector of `num_gate_constraints * vars_batch.len()` field elements. The constraints /// corresponding to `vars_batch[i]` are found in `result[i], result[vars_batch.len() + i], /// result[2 * vars_batch.len() + i], ...`. -pub fn evaluate_gate_constraints_base_batch< - F: RichField + Extendable, - C: GenericConfig, - const D: usize, ->( +pub fn evaluate_gate_constraints_base_batch, const D: usize>( common_data: &CommonCircuitData, vars_batch: EvaluationVarsBaseBatch, ) -> Vec { @@ -273,11 +256,7 @@ pub fn evaluate_gate_constraints_base_batch< constraints_batch } -pub fn evaluate_gate_constraints_circuit< - F: RichField + Extendable, - C: GenericConfig, - const D: usize, ->( +pub fn evaluate_gate_constraints_circuit, const D: usize>( builder: &mut CircuitBuilder, common_data: &CommonCircuitData, vars: EvaluationTargets, @@ -308,11 +287,7 @@ pub fn evaluate_gate_constraints_circuit< /// /// Assumes `x != 1`; if `x` could be 1 then this is unsound. This is fine if `x` is a random /// variable drawn from a sufficiently large domain. -pub(crate) fn eval_vanishing_poly_circuit< - F: RichField + Extendable, - C: GenericConfig, - const D: usize, ->( +pub(crate) fn eval_vanishing_poly_circuit, const D: usize>( builder: &mut CircuitBuilder, common_data: &CommonCircuitData, x: ExtensionTarget, @@ -332,7 +307,7 @@ pub(crate) fn eval_vanishing_poly_circuit< let constraint_terms = with_context!( builder, "evaluate gate constraints", - evaluate_gate_constraints_circuit::(builder, common_data, vars,) + evaluate_gate_constraints_circuit::(builder, common_data, vars,) ); // The L_0(x) (Z(x) - 1) vanishing terms. diff --git a/plonky2/src/plonk/verifier.rs b/plonky2/src/plonk/verifier.rs index 893720c6..7e68e59e 100644 --- a/plonky2/src/plonk/verifier.rs +++ b/plonky2/src/plonk/verifier.rs @@ -59,7 +59,7 @@ pub(crate) fn verify_with_challenges< let partial_products = &proof.openings.partial_products; // Evaluate the vanishing polynomial at our challenge point, zeta. - let vanishing_polys_zeta = eval_vanishing_poly::( + let vanishing_polys_zeta = eval_vanishing_poly::( common_data, challenges.plonk_zeta, vars, diff --git a/plonky2/src/recursion/conditional_recursive_verifier.rs b/plonky2/src/recursion/conditional_recursive_verifier.rs index 2596e1f0..6331118b 100644 --- a/plonky2/src/recursion/conditional_recursive_verifier.rs +++ b/plonky2/src/recursion/conditional_recursive_verifier.rs @@ -374,9 +374,9 @@ mod tests { // Conditionally verify the two proofs. let mut builder = CircuitBuilder::::new(config); let mut pw = PartialWitness::new(); - let pt = builder.add_virtual_proof_with_pis::(&data.common); + let pt = builder.add_virtual_proof_with_pis(&data.common); pw.set_proof_with_pis_target(&pt, &proof); - let dummy_pt = builder.add_virtual_proof_with_pis::(&data.common); + let dummy_pt = builder.add_virtual_proof_with_pis(&data.common); pw.set_proof_with_pis_target::(&dummy_pt, &dummy_proof); let inner_data = builder.add_virtual_verifier_data(data.common.config.fri_config.cap_height); diff --git a/plonky2/src/recursion/cyclic_recursion.rs b/plonky2/src/recursion/cyclic_recursion.rs index 656ff3b9..9b305626 100644 --- a/plonky2/src/recursion/cyclic_recursion.rs +++ b/plonky2/src/recursion/cyclic_recursion.rs @@ -40,7 +40,7 @@ impl, const D: usize> VerifierOnlyCircuitData { } impl VerifierCircuitTarget { - fn from_slice, C: GenericConfig, const D: usize>( + fn from_slice, const D: usize>( slice: &[Target], common_data: &CommonCircuitData, ) -> Result { @@ -101,7 +101,7 @@ impl, const D: usize> CircuitBuilder { self.goal_common_data = Some(common_data.clone()); } - let inner_cyclic_pis = VerifierCircuitTarget::from_slice::( + let inner_cyclic_pis = VerifierCircuitTarget::from_slice::( &cyclic_proof_with_pis.public_inputs, common_data, )?; @@ -207,7 +207,7 @@ mod tests { let data = builder.build::(); let config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(config); - let proof = builder.add_virtual_proof_with_pis::(&data.common); + let proof = builder.add_virtual_proof_with_pis(&data.common); let verifier_data = builder.add_virtual_verifier_data(data.common.config.fri_config.cap_height); builder.verify_proof::(&proof, &verifier_data, &data.common); @@ -215,7 +215,7 @@ mod tests { let config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(config); - let proof = builder.add_virtual_proof_with_pis::(&data.common); + let proof = builder.add_virtual_proof_with_pis(&data.common); let verifier_data = builder.add_virtual_verifier_data(data.common.config.fri_config.cap_height); builder.verify_proof::(&proof, &verifier_data, &data.common); @@ -257,7 +257,7 @@ mod tests { let condition = builder.add_virtual_bool_target_safe(); // Unpack inner proof's public inputs. - let inner_cyclic_proof_with_pis = builder.add_virtual_proof_with_pis::(&common_data); + let inner_cyclic_proof_with_pis = builder.add_virtual_proof_with_pis(&common_data); let inner_cyclic_pis = &inner_cyclic_proof_with_pis.public_inputs; let inner_cyclic_initial_hash = HashOutTarget::try_from(&inner_cyclic_pis[0..4]).unwrap(); let inner_cyclic_latest_hash = HashOutTarget::try_from(&inner_cyclic_pis[4..8]).unwrap(); diff --git a/plonky2/src/recursion/dummy_circuit.rs b/plonky2/src/recursion/dummy_circuit.rs index c8b98a96..53fa0bb0 100644 --- a/plonky2/src/recursion/dummy_circuit.rs +++ b/plonky2/src/recursion/dummy_circuit.rs @@ -113,7 +113,7 @@ impl, const D: usize> CircuitBuilder { { let dummy_circuit = dummy_circuit::(common_data); let dummy_proof_with_pis = dummy_proof(&dummy_circuit, HashMap::new())?; - let dummy_proof_with_pis_target = self.add_virtual_proof_with_pis::(common_data); + let dummy_proof_with_pis_target = self.add_virtual_proof_with_pis(common_data); let dummy_verifier_data_target = self.add_virtual_verifier_data(self.config.fri_config.cap_height); diff --git a/plonky2/src/recursion/recursive_verifier.rs b/plonky2/src/recursion/recursive_verifier.rs index 9aafb1f5..91beb049 100644 --- a/plonky2/src/recursion/recursive_verifier.rs +++ b/plonky2/src/recursion/recursive_verifier.rs @@ -74,7 +74,7 @@ impl, const D: usize> CircuitBuilder { let vanishing_polys_zeta = with_context!( self, "evaluate the vanishing polynomial at our challenge point, zeta.", - eval_vanishing_poly_circuit::( + eval_vanishing_poly_circuit::( self, inner_common_data, challenges.plonk_zeta, @@ -126,11 +126,11 @@ impl, const D: usize> CircuitBuilder { ); } - pub fn add_virtual_proof_with_pis>( + pub fn add_virtual_proof_with_pis( &mut self, common_data: &CommonCircuitData, ) -> ProofWithPublicInputsTarget { - let proof = self.add_virtual_proof::(common_data); + let proof = self.add_virtual_proof(common_data); let public_inputs = self.add_virtual_targets(common_data.num_public_inputs); ProofWithPublicInputsTarget { proof, @@ -138,10 +138,7 @@ impl, const D: usize> CircuitBuilder { } } - fn add_virtual_proof>( - &mut self, - common_data: &CommonCircuitData, - ) -> ProofTarget { + fn add_virtual_proof(&mut self, common_data: &CommonCircuitData) -> ProofTarget { let config = &common_data.config; let fri_params = &common_data.fri_params; let cap_height = fri_params.config.cap_height; @@ -158,15 +155,12 @@ impl, const D: usize> CircuitBuilder { wires_cap: self.add_virtual_cap(cap_height), plonk_zs_partial_products_cap: self.add_virtual_cap(cap_height), quotient_polys_cap: self.add_virtual_cap(cap_height), - openings: self.add_opening_set::(common_data), + openings: self.add_opening_set(common_data), opening_proof: self.add_virtual_fri_proof(num_leaves_per_oracle, fri_params), } } - fn add_opening_set>( - &mut self, - common_data: &CommonCircuitData, - ) -> OpeningSetTarget { + fn add_opening_set(&mut self, common_data: &CommonCircuitData) -> OpeningSetTarget { let config = &common_data.config; let num_challenges = config.num_challenges; let total_partial_products = num_challenges * common_data.num_partial_products; @@ -363,7 +357,7 @@ mod tests { { let mut builder = CircuitBuilder::::new(config.clone()); let mut pw = PartialWitness::new(); - let pt = builder.add_virtual_proof_with_pis::(&inner_cd); + let pt = builder.add_virtual_proof_with_pis(&inner_cd); pw.set_proof_with_pis_target(&pt, &inner_proof); let inner_data = builder.add_virtual_verifier_data(inner_cd.config.fri_config.cap_height); diff --git a/starky/src/permutation.rs b/starky/src/permutation.rs index bba42712..4f5d2921 100644 --- a/starky/src/permutation.rs +++ b/starky/src/permutation.rs @@ -14,7 +14,7 @@ use plonky2::iop::challenger::{Challenger, RecursiveChallenger}; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::iop::target::Target; use plonky2::plonk::circuit_builder::CircuitBuilder; -use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher}; +use plonky2::plonk::config::{AlgebraicHasher, Hasher}; use plonky2::util::reducing::{ReducingFactor, ReducingFactorTarget}; use plonky2_maybe_rayon::*; @@ -63,7 +63,7 @@ pub(crate) struct PermutationChallengeSet { } /// Compute all Z polynomials (for permutation arguments). -pub(crate) fn compute_permutation_z_polys( +pub(crate) fn compute_permutation_z_polys( stark: &S, config: &StarkConfig, trace_poly_values: &[PolynomialValues], @@ -71,7 +71,6 @@ pub(crate) fn compute_permutation_z_polys( ) -> Vec> where F: RichField + Extendable, - C: GenericConfig, S: Stark, { let permutation_pairs = stark.permutation_pairs(); @@ -260,7 +259,7 @@ where pub(crate) permutation_challenge_sets: Vec>, } -pub(crate) fn eval_permutation_checks( +pub(crate) fn eval_permutation_checks( stark: &S, config: &StarkConfig, vars: StarkEvaluationVars, @@ -270,7 +269,6 @@ pub(crate) fn eval_permutation_checks, FE: FieldExtension, P: PackedField, - C: GenericConfig, S: Stark, [(); S::COLUMNS]:, [(); S::PUBLIC_INPUTS]:, diff --git a/starky/src/prover.rs b/starky/src/prover.rs index 2350830c..2f541d56 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -80,7 +80,7 @@ where config.num_challenges, stark.permutation_batch_size(), ); - let permutation_z_polys = compute_permutation_z_polys::( + let permutation_z_polys = compute_permutation_z_polys::( &stark, config, &trace_poly_values, @@ -285,7 +285,7 @@ where permutation_challenge_sets: permutation_challenge_sets.to_vec(), }, ); - eval_vanishing_poly::( + eval_vanishing_poly::( stark, config, vars, @@ -303,7 +303,7 @@ where let num_challenges = alphas.len(); - (0..P::WIDTH).into_iter().map(move |i| { + (0..P::WIDTH).map(move |i| { (0..num_challenges) .map(|j| constraints_evals[j].as_slice()[i]) .collect() diff --git a/starky/src/recursive_verifier.rs b/starky/src/recursive_verifier.rs index d080f80f..11d83479 100644 --- a/starky/src/recursive_verifier.rs +++ b/starky/src/recursive_verifier.rs @@ -128,7 +128,7 @@ fn verify_stark_proof_with_challenges_circuit< with_context!( builder, "evaluate vanishing polynomial", - eval_vanishing_poly_circuit::( + eval_vanishing_poly_circuit::( builder, &stark, inner_config, diff --git a/starky/src/vanishing_poly.rs b/starky/src/vanishing_poly.rs index 906b8980..a3fe753e 100644 --- a/starky/src/vanishing_poly.rs +++ b/starky/src/vanishing_poly.rs @@ -2,7 +2,6 @@ use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::field::packed::PackedField; use plonky2::hash::hash_types::RichField; use plonky2::plonk::circuit_builder::CircuitBuilder; -use plonky2::plonk::config::GenericConfig; use crate::config::StarkConfig; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; @@ -13,7 +12,7 @@ use crate::permutation::{ use crate::stark::Stark; use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; -pub(crate) fn eval_vanishing_poly( +pub(crate) fn eval_vanishing_poly( stark: &S, config: &StarkConfig, vars: StarkEvaluationVars, @@ -23,14 +22,13 @@ pub(crate) fn eval_vanishing_poly, FE: FieldExtension, P: PackedField, - C: GenericConfig, S: Stark, [(); S::COLUMNS]:, [(); S::PUBLIC_INPUTS]:, { stark.eval_packed_generic(vars, consumer); if let Some(permutation_data) = permutation_data { - eval_permutation_checks::( + eval_permutation_checks::( stark, config, vars, @@ -40,7 +38,7 @@ pub(crate) fn eval_vanishing_poly( +pub(crate) fn eval_vanishing_poly_circuit( builder: &mut CircuitBuilder, stark: &S, config: &StarkConfig, @@ -49,7 +47,6 @@ pub(crate) fn eval_vanishing_poly_circuit( consumer: &mut RecursiveConstraintConsumer, ) where F: RichField + Extendable, - C: GenericConfig, S: Stark, [(); S::COLUMNS]:, [(); S::PUBLIC_INPUTS]:, diff --git a/starky/src/verifier.rs b/starky/src/verifier.rs index 443fcb31..6930c7cb 100644 --- a/starky/src/verifier.rs +++ b/starky/src/verifier.rs @@ -98,7 +98,7 @@ where next_zs: permutation_zs_next.as_ref().unwrap().clone(), permutation_challenge_sets: challenges.permutation_challenge_sets.unwrap(), }); - eval_vanishing_poly::( + eval_vanishing_poly::( &stark, config, vars, From ac40bd5f5da3c5c2af52cf7115bef509aaea9b3a Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 13 Feb 2023 20:11:32 +0100 Subject: [PATCH 4/4] Optimize `ecMul` precompile (scalar multiplication on BN254) (#852) * windowed mul * Working * Window of 4 bits * Fix * Comments * Unroll loop * Unroll loop * remove global * Minor * Minor * Implement `CALLVALUE, CALLDATALOAD, CALLDATASIZE, CALLDATACOPY` in interpreter * Minor * Doesn't work * Minor * Minor * wnaf msm * Working hardcoded values: 28657 opcodes * Working wnaf * Small wnaf optim * Precompute works * Working together * Bump to 129 bits * Working glv decomposition * Working MSM with GLV * Almost working * Working * ECC test folder * Working with real sig data * Fix tests + Clippy * Minor * Cleaning * Comments * Cleaning * Smaller glv test file * Print opcode count at the end of interpreter run * More constants * Add z3 proof that the GLV scalars are 129-bit or less * Minor change to z3 proof * Move files and renaming fns * Testing * Fix BN GLV * BN precompute table * Working precompute * Working bn tests * Working * Minor * Minor * Use MULFP254 * Minor * Merge conflicts * Remove unused asm file * ECC fns renaming (#874) * PR feedback --- evm/src/cpu/kernel/aggregator.rs | 4 + .../cpu/kernel/asm/curve/bn254/curve_add.asm | 64 +- .../cpu/kernel/asm/curve/bn254/curve_mul.asm | 98 +- evm/src/cpu/kernel/asm/curve/bn254/glv.asm | 97 ++ evm/src/cpu/kernel/asm/curve/bn254/msm.asm | 73 ++ .../kernel/asm/curve/bn254/precomputation.asm | 35 + evm/src/cpu/kernel/asm/curve/common.asm | 14 + .../kernel/asm/curve/secp256k1/curve_add.asm | 38 +- .../kernel/asm/curve/secp256k1/ecrecover.asm | 10 +- .../cpu/kernel/asm/curve/secp256k1/glv.asm | 4 +- .../asm/curve/secp256k1/precomputation.asm | 10 +- evm/src/cpu/kernel/asm/curve/wnaf.asm | 69 ++ evm/src/cpu/kernel/constants/mod.rs | 35 +- evm/src/cpu/kernel/tests/ecc/bn_glv_test_data | 1049 +++++++++++++++++ evm/src/cpu/kernel/tests/ecc/curve_ops.rs | 108 +- .../ecc/{glv_test_data => secp_glv_test_data} | 0 evm/src/memory/segments.rs | 14 +- 17 files changed, 1566 insertions(+), 156 deletions(-) create mode 100644 evm/src/cpu/kernel/asm/curve/bn254/glv.asm create mode 100644 evm/src/cpu/kernel/asm/curve/bn254/msm.asm create mode 100644 evm/src/cpu/kernel/asm/curve/bn254/precomputation.asm create mode 100644 evm/src/cpu/kernel/asm/curve/wnaf.asm create mode 100644 evm/src/cpu/kernel/tests/ecc/bn_glv_test_data rename evm/src/cpu/kernel/tests/ecc/{glv_test_data => secp_glv_test_data} (100%) diff --git a/evm/src/cpu/kernel/aggregator.rs b/evm/src/cpu/kernel/aggregator.rs index 0ff48666..a5eeaf32 100644 --- a/evm/src/cpu/kernel/aggregator.rs +++ b/evm/src/cpu/kernel/aggregator.rs @@ -27,6 +27,9 @@ pub(crate) fn combined_kernel() -> Kernel { include_str!("asm/curve/bn254/curve_add.asm"), include_str!("asm/curve/bn254/curve_mul.asm"), include_str!("asm/curve/bn254/moddiv.asm"), + include_str!("asm/curve/bn254/glv.asm"), + include_str!("asm/curve/bn254/msm.asm"), + include_str!("asm/curve/bn254/precomputation.asm"), include_str!("asm/curve/common.asm"), include_str!("asm/curve/secp256k1/curve_add.asm"), include_str!("asm/curve/secp256k1/ecrecover.asm"), @@ -35,6 +38,7 @@ pub(crate) fn combined_kernel() -> Kernel { include_str!("asm/curve/secp256k1/moddiv.asm"), include_str!("asm/curve/secp256k1/glv.asm"), include_str!("asm/curve/secp256k1/precomputation.asm"), + include_str!("asm/curve/wnaf.asm"), include_str!("asm/exp.asm"), include_str!("asm/fields/fp6_macros.asm"), include_str!("asm/fields/fp6_mul.asm"), diff --git a/evm/src/cpu/kernel/asm/curve/bn254/curve_add.asm b/evm/src/cpu/kernel/asm/curve/bn254/curve_add.asm index dda82109..3e917120 100644 --- a/evm/src/cpu/kernel/asm/curve/bn254/curve_add.asm +++ b/evm/src/cpu/kernel/asm/curve/bn254/curve_add.asm @@ -2,7 +2,7 @@ // BN254 elliptic curve addition. // Uses the standard affine addition formula. -global ec_add: +global bn_add: // Uncomment for test inputs. // PUSH 0xdeadbeef // PUSH 2 @@ -16,27 +16,27 @@ global ec_add: // stack: y0, x0, y0, x1, y1, retdest DUP2 // stack: x0, y0, x0, y0, x1, y1, retdest - %ec_check + %bn_check // stack: isValid(x0, y0), x0, y0, x1, y1, retdest DUP5 // stack: x1, isValid(x0, y0), x0, y0, x1, y1, retdest DUP5 // stack: x1, y1, isValid(x0, y0), x0, y0, x1, y1, retdest - %ec_check + %bn_check // stack: isValid(x1, y1), isValid(x0, y0), x0, y0, x1, y1, retdest AND // stack: isValid(x1, y1) & isValid(x0, y0), x0, y0, x1, y1, retdest - %jumpi(ec_add_valid_points) + %jumpi(bn_add_valid_points) // stack: x0, y0, x1, y1, retdest // Otherwise return %pop4 // stack: retdest - %ec_invalid_input + %bn_invalid_input // BN254 elliptic curve addition. // Assumption: (x0,y0) and (x1,y1) are valid points. -global ec_add_valid_points: +global bn_add_valid_points: // stack: x0, y0, x1, y1, retdest // Check if the first point is the identity. @@ -46,17 +46,17 @@ global ec_add_valid_points: // stack: x0, y0, x0, y0, x1, y1, retdest %ec_isidentity // stack: (x0,y0)==(0,0), x0, y0, x1, y1, retdest - %jumpi(ec_add_first_zero) + %jumpi(bn_add_first_zero) // stack: x0, y0, x1, y1, retdest - // Check if the first point is the identity. + // Check if the second point is the identity. DUP4 // stack: y1, x0, y0, x1, y1, retdest DUP4 // stack: x1, y1, x0, y0, x1, y1, retdest %ec_isidentity // stack: (x1,y1)==(0,0), x0, y0, x1, y1, retdest - %jumpi(ec_add_snd_zero) + %jumpi(bn_add_snd_zero) // stack: x0, y0, x1, y1, retdest // Check if both points have the same x-coordinate. @@ -66,7 +66,7 @@ global ec_add_valid_points: // stack: x0, x1, x0, y0, x1, y1, retdest EQ // stack: x0 == x1, x0, y0, x1, y1, retdest - %jumpi(ec_add_equal_first_coord) + %jumpi(bn_add_equal_first_coord) // stack: x0, y0, x1, y1, retdest // Otherwise, we can use the standard formula. @@ -85,11 +85,11 @@ global ec_add_valid_points: // stack: x0 - x1, y0 - y1, x0, y0, x1, y1, retdest %moddiv // stack: lambda, x0, y0, x1, y1, retdest - %jump(ec_add_valid_points_with_lambda) + %jump(bn_add_valid_points_with_lambda) // BN254 elliptic curve addition. // Assumption: (x0,y0) == (0,0) -ec_add_first_zero: +bn_add_first_zero: // stack: x0, y0, x1, y1, retdest // Just return (x1,y1) %stack (x0, y0, x1, y1, retdest) -> (retdest, x1, y1) @@ -97,7 +97,7 @@ ec_add_first_zero: // BN254 elliptic curve addition. // Assumption: (x1,y1) == (0,0) -ec_add_snd_zero: +bn_add_snd_zero: // stack: x0, y0, x1, y1, retdest // Just return (x0,y0) @@ -106,7 +106,7 @@ ec_add_snd_zero: // BN254 elliptic curve addition. // Assumption: lambda = (y0 - y1)/(x0 - x1) -ec_add_valid_points_with_lambda: +bn_add_valid_points_with_lambda: // stack: lambda, x0, y0, x1, y1, retdest // Compute x2 = lambda^2 - x1 - x0 @@ -153,7 +153,7 @@ ec_add_valid_points_with_lambda: // BN254 elliptic curve addition. // Assumption: (x0,y0) and (x1,y1) are valid points and x0 == x1 -ec_add_equal_first_coord: +bn_add_equal_first_coord: // stack: x0, y0, x1, y1, retdest with x0 == x1 // Check if the points are equal @@ -163,7 +163,7 @@ ec_add_equal_first_coord: // stack: y1, y0, x0, y0, x1, y1, retdest EQ // stack: y1 == y0, x0, y0, x1, y1, retdest - %jumpi(ec_add_equal_points) + %jumpi(bn_add_equal_points) // stack: x0, y0, x1, y1, retdest // Otherwise, one is the negation of the other so we can return (0,0). @@ -181,7 +181,7 @@ ec_add_equal_first_coord: // BN254 elliptic curve addition. // Assumption: x0 == x1 and y0 == y1 // Standard doubling formula. -ec_add_equal_points: +bn_add_equal_points: // stack: x0, y0, x1, y1, retdest // Compute lambda = 3/2 * x0^2 / y0 @@ -203,18 +203,19 @@ ec_add_equal_points: // stack: y0, 3/2 * x0^2, x0, y0, x1, y1, retdest %moddiv // stack: lambda, x0, y0, x1, y1, retdest - %jump(ec_add_valid_points_with_lambda) + %jump(bn_add_valid_points_with_lambda) // BN254 elliptic curve doubling. // Assumption: (x0,y0) is a valid point. // Standard doubling formula. -global ec_double: - // stack: x0, y0, retdest - DUP2 - // stack: y0, x0, y0, retdest - DUP2 - // stack: x0, y0, x0, y0, retdest - %jump(ec_add_equal_points) +global bn_double: + // stack: x, y, retdest + DUP2 DUP2 %ec_isidentity + // stack: (x,y)==(0,0), x, y, retdest + %jumpi(ec_double_retself) + DUP2 DUP2 + // stack: x, y, x, y, retdest + %jump(bn_add_equal_points) // Push the order of the BN254 base field. %macro bn_base @@ -241,7 +242,7 @@ global ec_double: // Check if (x,y) is a valid curve point. // Puts y^2 % N == (x^3 + 3) % N & (x < N) & (y < N) || (x,y)==(0,0) on top of the stack. -%macro ec_check +%macro bn_check // stack: x, y %bn_base // stack: N, x, y @@ -291,17 +292,8 @@ global ec_double: // stack: y^2 % N == (x^3 + 3) % N & (x < N) & (y < N) || (x,y)==(0,0) %endmacro -// Check if (x,y)==(0,0) -%macro ec_isidentity - // stack: x, y - OR - // stack: x | y - ISZERO - // stack: (x,y) == (0,0) -%endmacro - // Return (u256::MAX, u256::MAX) which is used to indicate the input was invalid. -%macro ec_invalid_input +%macro bn_invalid_input // stack: retdest PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // stack: u256::MAX, retdest diff --git a/evm/src/cpu/kernel/asm/curve/bn254/curve_mul.asm b/evm/src/cpu/kernel/asm/curve/bn254/curve_mul.asm index b1472812..ecbb3de0 100644 --- a/evm/src/cpu/kernel/asm/curve/bn254/curve_mul.asm +++ b/evm/src/cpu/kernel/asm/curve/bn254/curve_mul.asm @@ -1,11 +1,6 @@ // BN254 elliptic curve scalar multiplication. -// Recursive implementation, same algorithm as in `exp.asm`. -global ec_mul: - // Uncomment for test inputs. - // PUSH 0xdeadbeef - // PUSH 0xd - // PUSH 2 - // PUSH 1 +// Uses GLV, wNAF with w=5, and a MSM algorithm. +global bn_mul: // stack: x, y, s, retdest DUP2 // stack: y, x, y, s, retdest @@ -19,77 +14,28 @@ global ec_mul: // stack: y, x, y, s, retdest DUP2 // stack: x, y, x, y, s, retdest - %ec_check + %bn_check // stack: isValid(x, y), x, y, s, retdest - %jumpi(ec_mul_valid_point) + %jumpi(bn_mul_valid_point) // stack: x, y, s, retdest %pop3 - %ec_invalid_input + %bn_invalid_input -// Same algorithm as in `exp.asm` -ec_mul_valid_point: - // stack: x, y, s, retdest - DUP3 - // stack: s, x, y, s, retdest - %jumpi(step_case) - // stack: x, y, s, retdest - %jump(ret_zero_ec_mul) - -step_case: - // stack: x, y, s, retdest - PUSH recursion_return - // stack: recursion_return, x, y, s, retdest - PUSH 2 - // stack: 2, recursion_return, x, y, s, retdest - DUP5 - // stack: s, 2, recursion_return, x, y, s, retdest - DIV - // stack: s / 2, recursion_return, x, y, s, retdest - PUSH step_case_contd - // stack: step_case_contd, s / 2, recursion_return, x, y, s, retdest - DUP5 - // stack: y, step_case_contd, s / 2, recursion_return, x, y, s, retdest - DUP5 - // stack: x, y, step_case_contd, s / 2, recursion_return, x, y, s, retdest - %jump(ec_double) - -// Assumption: 2(x,y) = (x',y') -step_case_contd: - // stack: x', y', s / 2, recursion_return, x, y, s, retdest - %jump(ec_mul_valid_point) - -recursion_return: - // stack: x', y', x, y, s, retdest - SWAP4 - // stack: s, y', x, y, x', retdest - PUSH 1 - // stack: 1, s, y', x, y, x', retdest - AND - // stack: s & 1, y', x, y, x', retdest - SWAP1 - // stack: y', s & 1, x, y, x', retdest - SWAP2 - // stack: x, s & 1, y', y, x', retdest - SWAP3 - // stack: y, s & 1, y', x, x', retdest - SWAP4 - // stack: x', s & 1, y', x, y, retdest - SWAP1 - // stack: s & 1, x', y', x, y, retdest - %jumpi(odd_scalar) - // stack: x', y', x, y, retdest - SWAP3 - // stack: y, y', x, x', retdest - POP - // stack: y', x, x', retdest - SWAP1 - // stack: x, y', x', retdest - POP - // stack: y', x', retdest - SWAP2 - // stack: retdest, x', y' +bn_mul_valid_point: + %stack (x, y, s, retdest) -> (s, bn_mul_after_glv, x, y, bn_msm, bn_mul_end, retdest) + %jump(bn_glv_decompose) +bn_mul_after_glv: + // stack: bneg, a, b, x, y, bn_msm, bn_mul_end, retdest + // Store bneg at this (otherwise unused) location. Will be used later in the MSM. + %mstore_kernel(@SEGMENT_KERNEL_BN_TABLE_Q, @BN_BNEG_LOC) + // stack: a, b, x, y, bn_msm, bn_mul_end, retdest + PUSH bn_mul_after_a SWAP1 PUSH @SEGMENT_KERNEL_BN_WNAF_A PUSH @BN_SCALAR %jump(wnaf) +bn_mul_after_a: + // stack: b, x, y, bn_msm, bn_mul_end, retdest + PUSH bn_mul_after_b SWAP1 PUSH @SEGMENT_KERNEL_BN_WNAF_B PUSH @BN_SCALAR %jump(wnaf) +bn_mul_after_b: + // stack: x, y, bn_msm, bn_mul_end, retdest + %jump(bn_precompute_table) +bn_mul_end: + %stack (Ax, Ay, retdest) -> (retdest, Ax, Ay) JUMP - -odd_scalar: - // stack: x', y', x, y, retdest - %jump(ec_add_valid_points) diff --git a/evm/src/cpu/kernel/asm/curve/bn254/glv.asm b/evm/src/cpu/kernel/asm/curve/bn254/glv.asm new file mode 100644 index 00000000..c29d8f14 --- /dev/null +++ b/evm/src/cpu/kernel/asm/curve/bn254/glv.asm @@ -0,0 +1,97 @@ +// Inspired by https://github.com/AztecProtocol/weierstrudel/blob/master/huff_modules/endomorphism.huff +// See also Sage code in evm/src/cpu/kernel/tests/ecc/bn_glv_test_data +// Given scalar `k ∈ Bn254::ScalarField`, return `u, k1, k2` with `k1,k2 < 2^127` and such that +// `k = k1 - s*k2` if `u==0` otherwise `k = k1 + s*k2`, where `s` is the scalar value representing the endomorphism. +// In the comments below, N means @BN_SCALAR +// +// Z3 proof that the resulting `k1, k2` satisfy `k1>0`, `k1 < 2^127` and `|k2| < 2^127`. +// ```python +// from z3 import Solver, Int, Or, unsat +// q = 0x30644E72E131A029B85045B68181585D2833E84879B9709143E1F593F0000001 +// glv_s = 0xB3C4D79D41A917585BFC41088D8DAAA78B17EA66B99C90DD +// +// b2 = 0x89D3256894D213E3 +// b1 = -0x6F4D8248EEB859FC8211BBEB7D4F1128 +// +// g1 = 0x24CCEF014A773D2CF7A7BD9D4391EB18D +// g2 = 0x2D91D232EC7E0B3D7 +// k = Int("k") +// c1 = Int("c1") +// c2 = Int("c2") +// s = Solver() +// +// c2p = -c2 +// s.add(k < q) +// s.add(0 < k) +// s.add(c1 * (2**256) <= g2 * k) +// s.add((c1 + 1) * (2**256) > g2 * k) +// s.add(c2p * (2**256) <= g1 * k) +// s.add((c2p + 1) * (2**256) > g1 * k) +// +// q1 = c1 * b1 +// q2 = c2 * b2 +// +// k2 = q2 - q1 +// k2L = (glv_s * k2) % q +// k1 = k - k2L +// k2 = -k2 +// +// s.add(Or((k2 >= 2**127), (-k2 >= 2**127), (k1 >= 2**127), (k1 < 0))) +// +// assert s.check() == unsat +// ``` +global bn_glv_decompose: + // stack: k, retdest + PUSH @BN_SCALAR DUP1 DUP1 + // Compute c2 which is the top 256 bits of k*g1. Use asm from https://medium.com/wicketh/mathemagic-full-multiply-27650fec525d. + PUSH @U256_MAX + // stack: -1, N, N, N, k, retdest + PUSH @BN_GLV_MINUS_G1 DUP6 + // stack: k, g1, -1, N, N, N, k, retdest + MULMOD + // stack: (k * g1 % -1), N, N, N, k, retdest + PUSH @BN_GLV_MINUS_G1 DUP6 + // stack: k, g1, (k * g1 % -1), N, N, N, k, retdest + MUL + // stack: bottom = (k * g1), (k * g1 % -1), N, N, N, k, retdest + DUP1 DUP3 + // stack: (k * g1 % -1), bottom, bottom, (k * g1 % -1), N, N, N, k, retdest + LT SWAP2 SUB SUB + // stack: c2, N, N, N, k, retdest + PUSH @BN_GLV_B2 MULMOD + // stack: q2=c2*b2, N, N, k, retdest + + // Use the same trick to compute c1 = top 256 bits of g2*k. + PUSH @BN_SCALAR PUSH @U256_MAX + PUSH @BN_GLV_G2 DUP7 MULMOD + PUSH @BN_GLV_G2 DUP7 MUL + DUP1 DUP3 LT + SWAP2 SUB SUB + // stack: c1, N, q2, N, N, k, retdest + PUSH @BN_GLV_B1 MULMOD + // stack: q1, q2, N, N, k, retdest + + // We compute k2 = q1 + q2 - N, but we check for underflow and return N-q1-q2 instead if there is one, + // along with a flag `underflow` set to 1 if there is an underflow, 0 otherwise. + ADD %sub_check_underflow + // stack: k2, underflow, N, k, retdest + SWAP3 PUSH @BN_SCALAR DUP5 PUSH @BN_GLV_S + // stack: s, k2, N, k, underflow, N, k2, retdest + MULMOD + // stack: s*k2, k, underflow, N, k2, retdest + // Need to return `k + s*k2` if no underflow occur, otherwise return `k - s*k2` which is done in the `underflowed` fn. + SWAP2 DUP1 %jumpi(underflowed) + %stack (underflow, k, x, N, k2) -> (k, x, N, k2, underflow) + ADDMOD + %stack (k1, k2, underflow, retdest) -> (retdest, underflow, k1, k2) + JUMP + +underflowed: + // stack: underflow, k, s*k2, N, k2 + // Compute (k-s*k2)%N. TODO: Use SUBMOD here when ready + %stack (u, k, x, N, k2) -> (N, x, k, N, k2, u) + SUB ADDMOD + %stack (k1, k2, underflow, retdest) -> (retdest, underflow, k1, k2) + JUMP + + diff --git a/evm/src/cpu/kernel/asm/curve/bn254/msm.asm b/evm/src/cpu/kernel/asm/curve/bn254/msm.asm new file mode 100644 index 00000000..10362287 --- /dev/null +++ b/evm/src/cpu/kernel/asm/curve/bn254/msm.asm @@ -0,0 +1,73 @@ +// Computes the multiplication `a*G` using a standard MSM with the GLV decomposition of `a`. +// see there for a detailed description. +global bn_msm: + // stack: retdest + PUSH 0 PUSH 0 PUSH 0 +global bn_msm_loop: + // stack: accx, accy, i, retdest + DUP3 %bn_mload_wnaf_a + // stack: w, accx, accy, i, retdest + DUP1 %jumpi(bn_msm_loop_add_a_nonzero) + POP +msm_loop_add_b: + //stack: accx, accy, i, retdest + DUP3 %bn_mload_wnaf_b + // stack: w, accx, accy, i, retdest + DUP1 %jumpi(bn_msm_loop_add_b_nonzero) + POP +msm_loop_contd: + %stack (accx, accy, i, retdest) -> (i, i, accx, accy, retdest) + // TODO: the GLV scalars for the BN curve are 127-bit, so could use 127 here. But this would require modifying `wnaf.asm`. Not sure it's worth it... + %eq_const(129) %jumpi(msm_end) + %increment + //stack: i+1, accx, accy, retdest + %stack (i, accx, accy, retdest) -> (accx, accy, bn_msm_loop, i, retdest) + %jump(bn_double) + +msm_end: + %stack (i, accx, accy, retdest) -> (retdest, accx, accy) + JUMP + +bn_msm_loop_add_a_nonzero: + %stack (w, accx, accy, i, retdest) -> (w, accx, accy, msm_loop_add_b, i, retdest) + %bn_mload_point_a + // stack: px, py, accx, accy, msm_loop_add_b, i, retdest + %jump(bn_add_valid_points) + +bn_msm_loop_add_b_nonzero: + %stack (w, accx, accy, i, retdest) -> (w, accx, accy, msm_loop_contd, i, retdest) + %bn_mload_point_b + // stack: px, py, accx, accy, msm_loop_contd, i, retdest + %jump(bn_add_valid_points) + +%macro bn_mload_wnaf_a + // stack: i + %mload_kernel(@SEGMENT_KERNEL_BN_WNAF_A) +%endmacro + +%macro bn_mload_wnaf_b + // stack: i + %mload_kernel(@SEGMENT_KERNEL_BN_WNAF_B) +%endmacro + +%macro bn_mload_point_a + // stack: w + DUP1 + %mload_kernel(@SEGMENT_KERNEL_BN_TABLE_Q) + //stack: Gy, w + SWAP1 %decrement %mload_kernel(@SEGMENT_KERNEL_BN_TABLE_Q) + //stack: Gx, Gy +%endmacro + +%macro bn_mload_point_b + // stack: w + DUP1 + %mload_kernel(@SEGMENT_KERNEL_BN_TABLE_Q) + PUSH @BN_BNEG_LOC %mload_kernel(@SEGMENT_KERNEL_BN_TABLE_Q) + %stack (bneg, Gy, w) -> (@BN_BASE, Gy, bneg, bneg, Gy, w) + SUB SWAP1 ISZERO MUL SWAP2 MUL ADD + SWAP1 %decrement %mload_kernel(@SEGMENT_KERNEL_BN_TABLE_Q) + //stack: Gx, Gy + PUSH @BN_GLV_BETA + MULFP254 +%endmacro diff --git a/evm/src/cpu/kernel/asm/curve/bn254/precomputation.asm b/evm/src/cpu/kernel/asm/curve/bn254/precomputation.asm new file mode 100644 index 00000000..a8c6ada9 --- /dev/null +++ b/evm/src/cpu/kernel/asm/curve/bn254/precomputation.asm @@ -0,0 +1,35 @@ +// Precompute a table of multiples of the BN254 point `Q = (Qx, Qy)`. +// Let `(Qxi, Qyi) = i * Q`, then store in the `SEGMENT_KERNEL_BN_TABLE_Q` segment of memory the values +// `i-1 => Qxi`, `i => Qyi if i < 16 else -Qy(32-i)` for `i in range(1, 32, 2)`. +global bn_precompute_table: + // stack: Qx, Qy, retdest + PUSH precompute_table_contd DUP3 DUP3 + %jump(bn_double) +precompute_table_contd: + // stack: Qx2, Qy2, Qx, Qy, retdest + PUSH 1 +bn_precompute_table_loop: + // stack i, Qx2, Qy2, Qx, Qy, retdest + PUSH 1 DUP2 SUB + %stack (im, i, Qx2, Qy2, Qx, Qy, retdest) -> (i, Qy, im, Qx, i, Qx2, Qy2, Qx, Qy, retdest) + %mstore_kernel(@SEGMENT_KERNEL_BN_TABLE_Q) %mstore_kernel(@SEGMENT_KERNEL_BN_TABLE_Q) + // stack: i, Qx2, Qy2, Qx, Qy, retdest + DUP1 PUSH 32 SUB PUSH 1 DUP2 SUB + // stack: 31-i, 32-i, i, Qx2, Qy2, Qx, Qy, retdest + DUP7 PUSH @BN_BASE SUB + // TODO: Could maybe avoid storing Qx a second time here, not sure if it would be more efficient. + %stack (Qyy, iii, ii, i, Qx2, Qy2, Qx, Qy, retdest) -> (iii, Qx, ii, Qyy, i, Qx2, Qy2, Qx, Qy, retdest) + %mstore_kernel(@SEGMENT_KERNEL_BN_TABLE_Q) %mstore_kernel(@SEGMENT_KERNEL_BN_TABLE_Q) + // stack: i, Qx2, Qy2, Qx, Qy, retdest + PUSH 2 ADD + // stack: i+2, Qx2, Qy2, Qx, Qy, retdest + DUP1 PUSH 16 LT %jumpi(precompute_table_end) + %stack (i, Qx2, Qy2, Qx, Qy, retdest) -> (Qx, Qy, Qx2, Qy2, precompute_table_loop_contd, i, Qx2, Qy2, retdest) + %jump(bn_add_valid_points) +precompute_table_loop_contd: + %stack (Qx, Qy, i, Qx2, Qy2, retdest) -> (i, Qx2, Qy2, Qx, Qy, retdest) + %jump(bn_precompute_table_loop) + +precompute_table_end: + // stack: i, Qx2, Qy2, Qx, Qy, retdest + %pop5 JUMP diff --git a/evm/src/cpu/kernel/asm/curve/common.asm b/evm/src/cpu/kernel/asm/curve/common.asm index 9e273c15..50f174fa 100644 --- a/evm/src/cpu/kernel/asm/curve/common.asm +++ b/evm/src/cpu/kernel/asm/curve/common.asm @@ -9,3 +9,17 @@ global ret_zero_ec_mul: SWAP2 // stack: retdest, 0, 0 JUMP + +global ec_double_retself: + %stack (x, y, retdest) -> (retdest, x, y) + JUMP + +// Check if (x,y)==(0,0) +%macro ec_isidentity + // stack: x, y + OR + // stack: x | y + ISZERO + // stack: (x,y) == (0,0) +%endmacro + diff --git a/evm/src/cpu/kernel/asm/curve/secp256k1/curve_add.asm b/evm/src/cpu/kernel/asm/curve/secp256k1/curve_add.asm index 17fd5b9a..9e416e66 100644 --- a/evm/src/cpu/kernel/asm/curve/secp256k1/curve_add.asm +++ b/evm/src/cpu/kernel/asm/curve/secp256k1/curve_add.asm @@ -2,7 +2,7 @@ // Secp256k1 elliptic curve addition. // Assumption: (x0,y0) and (x1,y1) are valid points. -global ec_add_valid_points_secp: +global secp_add_valid_points: // stack: x0, y0, x1, y1, retdest // Check if the first point is the identity. @@ -12,7 +12,7 @@ global ec_add_valid_points_secp: // stack: x0, y0, x0, y0, x1, y1, retdest %ec_isidentity // stack: (x0,y0)==(0,0), x0, y0, x1, y1, retdest - %jumpi(ec_add_first_zero) + %jumpi(secp_add_first_zero) // stack: x0, y0, x1, y1, retdest // Check if the second point is the identity. @@ -22,7 +22,7 @@ global ec_add_valid_points_secp: // stack: x1, y1, x0, y0, x1, y1, retdest %ec_isidentity // stack: (x1,y1)==(0,0), x0, y0, x1, y1, retdest - %jumpi(ec_add_snd_zero) + %jumpi(secp_add_snd_zero) // stack: x0, y0, x1, y1, retdest // Check if both points have the same x-coordinate. @@ -32,9 +32,9 @@ global ec_add_valid_points_secp: // stack: x0, x1, x0, y0, x1, y1, retdest EQ // stack: x0 == x1, x0, y0, x1, y1, retdest - %jumpi(ec_add_equal_first_coord) + %jumpi(secp_add_equal_first_coord) // Standard affine addition formula. -global ec_add_valid_points_no_edge_case_secp: +global secp_add_valid_points_no_edge_case: // stack: x0, y0, x1, y1, retdest // Compute lambda = (y0 - y1)/(x0 - x1) DUP4 @@ -51,11 +51,11 @@ global ec_add_valid_points_no_edge_case_secp: // stack: x0 - x1, y0 - y1, x0, y0, x1, y1, retdest %moddiv_secp_base // stack: lambda, x0, y0, x1, y1, retdest - %jump(ec_add_valid_points_with_lambda) + %jump(secp_add_valid_points_with_lambda) // Secp256k1 elliptic curve addition. // Assumption: (x0,y0) == (0,0) -ec_add_first_zero: +secp_add_first_zero: // stack: x0, y0, x1, y1, retdest // Just return (x1,y1) @@ -69,7 +69,7 @@ ec_add_first_zero: // Secp256k1 elliptic curve addition. // Assumption: (x1,y1) == (0,0) -ec_add_snd_zero: +secp_add_snd_zero: // stack: x0, y0, x1, y1, retdest // Just return (x1,y1) @@ -89,7 +89,7 @@ ec_add_snd_zero: // Secp256k1 elliptic curve addition. // Assumption: lambda = (y0 - y1)/(x0 - x1) -ec_add_valid_points_with_lambda: +secp_add_valid_points_with_lambda: // stack: lambda, x0, y0, x1, y1, retdest // Compute x2 = lambda^2 - x1 - x0 @@ -145,7 +145,7 @@ ec_add_valid_points_with_lambda: // Secp256k1 elliptic curve addition. // Assumption: (x0,y0) and (x1,y1) are valid points and x0 == x1 -ec_add_equal_first_coord: +secp_add_equal_first_coord: // stack: x0, y0, x1, y1, retdest with x0 == x1 // Check if the points are equal @@ -155,7 +155,7 @@ ec_add_equal_first_coord: // stack: y1, y0, x0, y0, x1, y1, retdest EQ // stack: y1 == y0, x0, y0, x1, y1, retdest - %jumpi(ec_add_equal_points) + %jumpi(secp_add_equal_points) // stack: x0, y0, x1, y1, retdest // Otherwise, one is the negation of the other so we can return (0,0). @@ -173,7 +173,7 @@ ec_add_equal_first_coord: // Secp256k1 elliptic curve addition. // Assumption: x0 == x1 and y0 == y1 // Standard doubling formula. -ec_add_equal_points: +secp_add_equal_points: // Compute lambda = 3/2 * x0^2 / y0 %stack (x0, y0, x1, y1, retdest) -> (x0, x0, @SECP_BASE, @SECP_BASE, x0, y0, x1, y1, retdest) MULMOD @@ -181,16 +181,16 @@ ec_add_equal_points: MULMOD DUP3 %moddiv_secp_base - %jump(ec_add_valid_points_with_lambda) + %jump(secp_add_valid_points_with_lambda) // Secp256k1 elliptic curve doubling. // Assumption: (x,y) is a valid point. // Standard doubling formula. -global ec_double_secp: +global secp_double: // stack: x, y, retdest DUP2 DUP2 %ec_isidentity // stack: (x,y)==(0,0), x, y, retdest - %jumpi(retself) + %jumpi(ec_double_retself) // Compute lambda = 3/2 * x0^2 / y0 %stack (x, y, retdest) -> (x, x, @SECP_BASE, @SECP_BASE, x, y, retdest) @@ -200,11 +200,7 @@ global ec_double_secp: DUP3 %moddiv_secp_base %stack (lambda, x, y, retdest) -> (lambda, x, y, x, y, retdest) - %jump(ec_add_valid_points_with_lambda) - -retself: - %stack (x, y, retdest) -> (retdest, x, y) - JUMP + %jump(secp_add_valid_points_with_lambda) // Push the order of the Secp256k1 scalar field. %macro secp_base @@ -221,7 +217,7 @@ retself: // Check if (x,y) is a valid curve point. // Puts y^2 % N == (x^3 + 3) % N & (x < N) & (y < N) || (x,y)==(0,0) on top of the stack. -%macro ec_check_secp +%macro secp_check // stack: x, y %secp_base // stack: N, x, y diff --git a/evm/src/cpu/kernel/asm/curve/secp256k1/ecrecover.asm b/evm/src/cpu/kernel/asm/curve/secp256k1/ecrecover.asm index 3aa98948..dd2b86f6 100644 --- a/evm/src/cpu/kernel/asm/curve/secp256k1/ecrecover.asm +++ b/evm/src/cpu/kernel/asm/curve/secp256k1/ecrecover.asm @@ -64,13 +64,13 @@ ecrecover_valid_input: // return msm_with_precomputation([a0, a1, b0, b1], [G, phi(G), Q, phi(Q)]) -- phi is the Secp endomorphism. ecdsa_msm_with_glv: %stack (a, b, Qx, Qy, retdest) -> (a, ecdsa_after_glv_a, b, Qx, Qy, retdest) - %jump(glv_decompose) + %jump(secp_glv_decompose) ecdsa_after_glv_a: %stack (a1neg, a0, a1, b, Qx, Qy, retdest) -> (b, ecdsa_after_glv_b, a1neg, a0, a1, Qx, Qy, retdest) - %jump(glv_decompose) + %jump(secp_glv_decompose) ecdsa_after_glv_b: %stack (b1neg, b0, b1, a1neg, a0, a1, Qx, Qy, retdest) -> (a1neg, b1neg, Qx, Qy, ecdsa_after_precompute, a0, a1, b0, b1, retdest) - %jump(precompute_table) + %jump(secp_precompute_table) ecdsa_after_precompute: // stack: a0, a1, b0, b1, retdest PUSH 0 PUSH 0 PUSH 129 // 129 is the bit length of the GLV exponents @@ -91,11 +91,11 @@ ecdsa_after_precompute_loop: SWAP1 %mul_const(2) %mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) %stack (Px, Py, i, accx, accy, a0, a1, b0, b1, retdest) -> (Px, Py, accx, accy, ecdsa_after_precompute_loop_contd, i, a0, a1, b0, b1, retdest) - %jump(ec_add_valid_points_secp) + %jump(secp_add_valid_points) ecdsa_after_precompute_loop_contd: %stack (accx, accy, i, a0, a1, b0, b1, retdest) -> (i, accx, accy, ecdsa_after_precompute_loop_contd2, i, a0, a1, b0, b1, retdest) ISZERO %jumpi(ecdsa_after_precompute_loop_end) - %jump(ec_double_secp) + %jump(secp_double) ecdsa_after_precompute_loop_contd2: %stack (accx, accy, i, a0, a1, b0, b1, retdest) -> (i, accx, accy, a0, a1, b0, b1, retdest) %decrement %jump(ecdsa_after_precompute_loop) diff --git a/evm/src/cpu/kernel/asm/curve/secp256k1/glv.asm b/evm/src/cpu/kernel/asm/curve/secp256k1/glv.asm index 3c380054..eee0dc63 100644 --- a/evm/src/cpu/kernel/asm/curve/secp256k1/glv.asm +++ b/evm/src/cpu/kernel/asm/curve/secp256k1/glv.asm @@ -1,5 +1,5 @@ // Inspired by https://github.com/AztecProtocol/weierstrudel/blob/master/huff_modules/endomorphism.huff -// See also Sage code in evm/src/cpu/kernel/tests/ecc/glv_test_data +// See also Sage code in evm/src/cpu/kernel/tests/ecc/secp_glv_test_data // Given scalar `k ∈ Secp256k1::ScalarField`, return `u, k1, k2` with `k1,k2 < 2^129` and such that // `k = k1 - s*k2` if `u==0` otherwise `k = k1 + s*k2`, where `s` is the scalar value representing the endomorphism. // In the comments below, N means @SECP_SCALAR @@ -36,7 +36,7 @@ // s.add(Or((k2 >= 2**129), (-k2 >= 2**129), (k1 >= 2**129), (k1 < 0))) // assert s.check() == unsat // ``` -global glv_decompose: +global secp_glv_decompose: // stack: k, retdest PUSH @SECP_SCALAR DUP1 DUP1 // Compute c2 which is the top 256 bits of k*g1. Use asm from https://medium.com/wicketh/mathemagic-full-multiply-27650fec525d. diff --git a/evm/src/cpu/kernel/asm/curve/secp256k1/precomputation.asm b/evm/src/cpu/kernel/asm/curve/secp256k1/precomputation.asm index 6ecdd6a0..3cea0315 100644 --- a/evm/src/cpu/kernel/asm/curve/secp256k1/precomputation.asm +++ b/evm/src/cpu/kernel/asm/curve/secp256k1/precomputation.asm @@ -1,6 +1,6 @@ // Initial stack: Gneg, Qneg, Qx, Qy, retdest // Compute a*G ± b*phi(G) + c*Q ± d*phi(Q) for a,b,c,d in {0,1}^4 and store its x-coordinate at location `2*(8a+4b+2c+d)` and its y-coordinate at location `2*(8a+4b+2c+d)+1` in the SEGMENT_KERNEL_ECDSA_TABLE segment. -global precompute_table: +global secp_precompute_table: // First store G, ± phi(G), G ± phi(G) // Use Gneg for the ±, e.g., ±phi(G) is computed as `Gneg * (-phi(G)) + (1-Gneg)*phi(G)` (note only the y-coordinate needs to be filtered). // stack: Gneg, Qneg, Qx, Qy, retdest @@ -30,7 +30,7 @@ global precompute_table: DUP5 PUSH @SECP_BASE SUB MUL ADD %stack (selectQy, betaQx, Qx, Qy, retdest) -> (2, betaQx, 3, selectQy, betaQx, selectQy, Qx, Qy, precompute_table_contd, retdest) %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) - %jump(ec_add_valid_points_no_edge_case_secp) + %jump(secp_add_valid_points_no_edge_case) precompute_table_contd: %stack (x, y, retdest) -> (6, x, 7, y, retdest) %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) @@ -46,7 +46,7 @@ precompute_table_loop: PUSH 9 %mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) PUSH 8 %mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) // stack: Gx, Gy, x, y, precompute_table_loop_contd, x, y, i, retdest - %jump(ec_add_valid_points_secp) + %jump(secp_add_valid_points) precompute_table_loop_contd: %stack (Rx, Ry, x, y, i, retdest) -> (i, 8, Rx, i, 9, Ry, x, y, i, retdest) ADD %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) ADD %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) @@ -54,14 +54,14 @@ precompute_table_loop_contd: PUSH 17 %mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) PUSH 16 %mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) %stack (Gx, Gy, x, y, x, y, i, retdest) -> (Gx, Gy, x, y, precompute_table_loop_contd2, x, y, i, retdest) - %jump(ec_add_valid_points_secp) + %jump(secp_add_valid_points) precompute_table_loop_contd2: %stack (Rx, Ry, x, y, i, retdest) -> (i, 16, Rx, i, 17, Ry, x, y, i, retdest) ADD %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) ADD %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) PUSH 25 %mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) PUSH 24 %mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) %stack (Gx, Gy, x, y, i, retdest) -> (Gx, Gy, x, y, precompute_table_loop_contd3, i, retdest) - %jump(ec_add_valid_points_secp) + %jump(secp_add_valid_points) precompute_table_loop_contd3: %stack (Rx, Ry, i, retdest) -> (i, 24, Rx, i, 25, Ry, i, retdest) ADD %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) ADD %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) diff --git a/evm/src/cpu/kernel/asm/curve/wnaf.asm b/evm/src/cpu/kernel/asm/curve/wnaf.asm new file mode 100644 index 00000000..555c9c84 --- /dev/null +++ b/evm/src/cpu/kernel/asm/curve/wnaf.asm @@ -0,0 +1,69 @@ +// wNAF expansion with w=5. +// Stores the reversed expansion of the given scalar in memory at the given segment and offsets 0..130. +// Should be called with scalars of bit length <= 129, which is the case when using GLV. +// Pseudo-code: +// def wnaf(n): +// ans = [0 for _ in range(130)] +// o = 0 +// while n != 0: +// i = n.trailing_zero_bits() +// o += i +// n >>= i +// m = n & 31 +// ans[o] = m +// if m > 16: +// ne += 32 +// ne -= m +// return ans +global wnaf: + // stack: N, segment, n, retdest (N is the size of the group in which the mul is taking place) + DUP3 MOD ISZERO %jumpi(wnaf_zero_scalar) + PUSH 0 +wnaf_loop: + %stack (o, segment, n, retdest) -> (n, wnaf_loop_contd, o, segment, retdest) + %jump(trailing_zeros) +wnaf_loop_contd: + %stack (n, i, o, segment, retdest) -> (o, i, n, segment, retdest) + ADD + %stack (o, n, segment, retdest) -> (n, segment, o, retdest) + DUP1 %and_const(31) SWAP1 + PUSH 16 DUP3 GT + // stack: m>16, n, m, segment, o, retdest + %mul_const(32) ADD + // stack: n, m, segment, o, retdest + DUP2 SWAP1 SUB + %stack (n, m, segment, o, retdest) -> (129, o, m, o, segment, n, retdest) + SUB + %stack (i, m, o, segment, n, retdest) -> (0, segment, i, m, o, segment, n, retdest) + MSTORE_GENERAL + // stack: o, segment, n, retdest + DUP3 ISZERO %jumpi(wnaf_end) + // stack: o, segment, n, retdest + %jump(wnaf_loop) + +wnaf_end: + // stack: o, segment, n, retdest + %pop3 JUMP + +wnaf_zero_scalar: + // stack: segment, n, retdest + %pop2 JUMP + + + +// Number of trailing zeros computed with a simple loop and returning the scalar without its lsb zeros. +trailing_zeros: + // stack: x, retdest + PUSH 0 +trailing_zeros_loop: + // stack: count, x, retdest + PUSH 1 DUP3 AND + // stack: x&1, count, x, retdest + %jumpi(trailing_zeros_end) + // stack: count, x, retdest + %increment SWAP1 PUSH 1 SHR SWAP1 + // stack: count, x>>1, retdest + %jump(trailing_zeros_loop) +trailing_zeros_end: + %stack (count, x, retdest) -> (retdest, x, count) + JUMP diff --git a/evm/src/cpu/kernel/constants/mod.rs b/evm/src/cpu/kernel/constants/mod.rs index 26b5fb0d..afabd955 100644 --- a/evm/src/cpu/kernel/constants/mod.rs +++ b/evm/src/cpu/kernel/constants/mod.rs @@ -63,7 +63,7 @@ const HASH_CONSTANTS: [(&str, [u8; 32]); 2] = [ ), ]; -const EC_CONSTANTS: [(&str, [u8; 32]); 10] = [ +const EC_CONSTANTS: [(&str, [u8; 32]); 18] = [ ( "U256_MAX", hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), @@ -72,6 +72,39 @@ const EC_CONSTANTS: [(&str, [u8; 32]); 10] = [ "BN_BASE", hex!("30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47"), ), + ( + "BN_SCALAR", + hex!("30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"), + ), + ( + "BN_GLV_BETA", + hex!("000000000000000059e26bcea0d48bacd4f263f1acdb5c4f5763473177fffffe"), + ), + ( + "BN_GLV_S", + hex!("0000000000000000b3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd"), + ), + ( + "BN_GLV_MINUS_G1", + hex!("000000000000000000000000000000024ccef014a773d2cf7a7bd9d4391eb18d"), + ), + ( + "BN_GLV_G2", + hex!("000000000000000000000000000000000000000000000002d91d232ec7e0b3d7"), + ), + ( + "BN_GLV_B1", + hex!("30644e72e131a029b85045b68181585cb8e665ff8b011694c1d039a872b0eed9"), + ), + ( + "BN_GLV_B2", + hex!("00000000000000000000000000000000000000000000000089d3256894d213e3"), + ), + ( + "BN_BNEG_LOC", + // This just needs to be large enough to not interfere with anything else in SEGMENT_KERNEL_BN_TABLE_Q. + hex!("0000000000000000000000000000000000000000000000000000000000001337"), + ), ( "SECP_BASE", hex!("fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"), diff --git a/evm/src/cpu/kernel/tests/ecc/bn_glv_test_data b/evm/src/cpu/kernel/tests/ecc/bn_glv_test_data new file mode 100644 index 00000000..db38ac8c --- /dev/null +++ b/evm/src/cpu/kernel/tests/ecc/bn_glv_test_data @@ -0,0 +1,1049 @@ +// Sage code to reproduce this: +// ```sage +// p = 21888242871839275222246405745257275088696311157297823662689037894645226208583 +// F = GF(p) +// E = EllipticCurve(F, [0, 3]) +// q = E.order() +// SF = GF(q) +// +// P = E.random_point() +// s = 0xb3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd +// beta = 0x59e26bcea0d48bacd4f263f1acdb5c4f5763473177fffffe +// +// # a1 = 64502973549206556628585045361533709077 +// # a2 = 367917413016453100223835821029139468248 +// b2 = 0x89d3256894d213e3 +// b1 = 0x30644e72e131a029b85045b68181585cb8e665ff8b011694c1d039a872b0eed9 +// b1 = -0x6f4d8248eeb859fc8211bbeb7d4f1128 +// +// g1 = -0x24ccef014a773d2cf7a7bd9d4391eb18d +// g2 = 0x2d91d232ec7e0b3d7 +// +// def decomp(k): +// c1 = (g2 * k) >> 256 +// c2 = -(-(g1 * k) >> 256) +// +// q1 = c1 * b1 +// q2 = c2 * b2 +// +// k2 = q2 - q1 +// k2L = (s*k2)%q +// k1 = k - k2L +// return k1, -k2 +// +// f = open('bnout', 'w') +// for i in range(1000): +// k = randint(0, 1<<256) % q +// k1, k2 = decomp(k) +// if k2 < 0: +// f.write(f"{k} 1 {k1} {-k2}\n") +// else: +// f.write(f"{k} 0 {k1} {k2}\n") +// assert k1 > 0 +// assert k1 < 1<<127 +// assert abs(k2) < 1<<127 +// assert (k1 - s*k2)%q == k +// +// f.close() +// ```sage +// +2013903480656938991561360820573915418551322753594945075887137499154548905374 0 1882462671847353709795309622886516129 42967685287677743822535440511395041270 +1268423676977918116861975711668021910978037329160197471654664118597257530440 0 34025934268629390987380972573718725934 9100169853444546116471837216426965693 +6668508633972199535163357076801579139221271059568834560363130537981332354695 0 79749746257933558980012311973715305501 116307227499605143480534356082216636552 +17768755383659786872958372620209327055213100458468309610137832646098313388946 0 90492014861931952583240494324496011409 17098585553523121578298898637157199310 +139915108452039320442032675295009888921418533694625463999489248643447547676 0 130863349746563396365758476405085169156 41092388482931584351521374815106346033 +13128053102434855341416433272324981024795488352499750050774381107936031805518 0 111993216900539341442706137325818807666 137565870375219228049024477065823682246 +1702057401067803897344460009125950084354506608147461765081028590098722533147 0 89320927675710536555702924851905599673 57402933744947499056148386684720925644 +17005695837813689466883871391635184470112639313859656064329991355435674991930 0 116398947678020711160080424096010423750 108747437388035888582963622260003883653 +7086100296664440681555493186326798897740340267847656106744873676028945469395 0 96972415532060635551093959933438242603 23257729070477712317114923418552439671 +12338378408039930925830626640419058948417759069530323857857041896165107321772 0 108739618626932594868239374750975886648 93989709738727518510089540785140463212 +16074868467455275153656399682320783657344238462740245950989214767804285156465 0 124945325095228305422526147528118621991 131043774062590891741685091793265303217 +19195889723945246569833633488811643062395336305972323797307038899519949045674 0 91406940890735908818407540652368068077 22946665784711399034024095613215284843 +16878059357100929177394086610398884126944484626015801518271668980848753530118 0 158685935518915245640238947169032886610 134793215726165740486246702952287496441 +8325603801922742344078036768480191507441897100827441324946084397116617032 0 41490138803893230607633049416882226619 124673980134955039118584382178102696778 +9325883179127897386123774699052925144305338736647350470583133656939432200831 0 139931126646594491806914429391920585426 78225769979522689473266770343704109358 +12501089776369792137119969478904813552547470850596243618311747107167923871567 0 13738337333063011247408533974722991257 89607995746647237436128016511595360803 +13327709821112824364418761072674816333107719040597511409847659950586323405211 0 41359556861476606716468299725809967559 24779620677574801712087901097075555658 +10072034493273912357208008082234894141200648132501530946575291007116504474912 0 76781485796521395175099070381733460818 54003671595202200644291460122856371574 +12415707277543767503114740191518652765197283788755102445275521613377410029393 0 135753421486585699744195544219845252608 150841019132250760761613924944579333486 +4104241302194832202060868109825893266856089460759866147130306967207840490353 0 72632096389954696026051518267652621825 58311233169600549592501119543441840024 +15641193331141795500404920739318637702175291243746414267137813367649672781597 0 106221019150617401856867137784525615810 129892506056817991810573577291747352408 +816554620499999456760417653930347998485540936848720176009878112766956695282 0 42578171369173589216953618502787776664 69985919394579153590243135093861734968 +3618999731369743698037020169549086621735062715757041260202007626249856285140 0 70479413959604976938400136995735249077 92018284790473177743612029079885903592 +8282392752418738514738037266717884679552851530383021352949808777489106022741 0 59083605669051103901981945167708559129 129434131571940229941736941249681818611 +21096192386638039916785050771127398604333119795524445061361888861891075829734 0 79534567798130056474033051034989262504 142946613134803093195740442669810857018 +10101498357252415148502309917342751131398582557621135785780805133283261024792 0 44906858186234193843102296541527217304 59552430467714119585567120582577203775 +20112769534491828475443523184517183213350315644637000504826261991625160232783 0 30234675555615032630119660875540291761 92045956112742459930398599086356266320 +8639311409233893057930501182535689195828478336034331066587279382603140380739 0 14245908125083619550479629450177117702 86437021859600078711727011527677011621 +20737815980509591915939868090463649964873906948333953535316209463938421468705 0 24312039971287035889348447665361865041 158277629640412196848585265468369025037 +6197227419391822835076932637236297541290374789786376326423788662479152361519 0 76496967785165538463245128754723851558 95440788607075906560233133330659767073 +3507837857872618399440486221614100493560628544763221937027684571500846183502 0 41672597601356600184765766690700743898 117834431255450129291931344965405662590 +6720268116387614937034135177878569264237612293675442003174314662967937918785 0 94615070668075626786314506076954480195 33298452450813520294111985372067233343 +14381517248617759701593246607434622490677928737243562728457392454760929038372 0 85057647397170897262749171627080226797 11104452434052993064625158649147121451 +18954942053065263623163292291400812710941942380493557036867148924847683221037 0 116098565261915536859058214311956521496 107095647256320549952063177261127403384 +21245964711735305595085252100217757249100856147266220418669463943934469595130 0 89425554484564370645878058721897145904 133621090946116450164920821147036035676 +13855299627765189646426405419566467616787864634288548853387691880571357087369 0 129244261991663500678156375006569763204 23752809976752496066838187903227429081 +1822456584591336401017566069777014434419918310655791129780834422964576129637 0 32456389978783012215128545102555515113 49705974297727806054407959930778302884 +4242438794619008401291747736749432956107493618360325518596771015869397733374 0 23029258932599814890835391781825046572 89928059378813183298112746592689937319 +10710422245046232407168773592562830140888090033262640488839808950006259544048 0 20650005017952450165831427782919607521 17165572506712865129404111243666588898 +17788715985715636563000364448397908306199762866088859004364143176771982087214 0 66622507473979419553078178615485935316 148529310638469314645168859680663064154 +3377122939761792234122582696557032775007463517455574762776743916285544123113 0 111119740153320066061492788749667346493 22352679708662289197049477394263721555 +12770430942224004163887551725370542426656410602013908480168182825982574689534 0 93167006826122426252651851223571175062 149404785261504123950278206323102677923 +7914653125787114558556628418337662338669149259374219600811623019425744653392 0 41614548541415479621814517623833673714 74102977816367027728407106648858462248 +4855096168810305874154876410714123250949331990652068276527783418282158099378 0 61268271483078069249586798875958432219 6132364793062250794328123733082453691 +20862295479185535454366605523434105664116787544991207265552468533663403802641 0 59324675913770502986160223082594712677 132262198727986221695414173190980825522 +2432825820738285014056108364985748494713416241803005743649341312088498782766 0 30926822802637295777997499494191035930 92930397505363243590184937249135421332 +3086882891195079898104693558067544064689205710993459505482475246268331375135 0 19415751936365368220330060983112386764 21301207598482446250120145862542662972 +21020982200787130600925362703307382385477354307977468699485849855902476380854 0 99915419306844447307629095766691709425 67677588612254889343094551392956214295 +4001162074869575919138545291349837940721221168567182801370488685679145732119 0 112159988509949591181337894096592297549 96781312402450425354695542721635138394 +19424443275625802349749801394308225887795876978437482453151058760268602915125 0 81992891475808402974753876117857285441 92807876163894776274283067777560148243 +12356743738820508242869888992307044238455072028894484377315646327878524263805 0 77340846141437284070413915040903809532 55374150923287050299252352919895826513 +2816385703326540570060286639731723051637350659698794116445514687609233841562 0 98631533884964190926678125293065045565 59060370337134406756988948331354493466 +13679157396045672380204462197779227102178042225281356906927108324872331939937 0 153190639553726436515397063911822794726 134275143240074016056088210745284305065 +3534196524445723531846721744271446821648809697911317317272675137581691519546 0 56135160407783528244729065000605092715 17135730127830633383884883479931316765 +12857120393899251392664989154218479040197569694511349074262609952837607018852 0 36182320363689350492888780094353655621 85360177004210031599090920438480444333 +5069930643992342169771113434177678776838563613391697656781752379441172420408 0 103514174353199457699741123891662506342 67007682662069148782539844186855043961 +14236578055609805328341699966689023934313002726810699771608026708054661884878 0 29282622597492291931007555885098587384 39540551682545274426631996207899396257 +19559647535094692172647026727995170440442113211517315088606828511749336396891 0 36902252623096012269615736083636098314 129736364315867474600842169108607990931 +7492011590682813368947695242427005238460366670527007463701988535794831394720 0 108846454110272925206443119671691144702 145005988668085801504645320000090688291 +21694378248444338899242669118017467976309325472573244213105009910765238984288 0 141446042240176980349849770617330746172 89687245217393378864582176933416791087 +2022596022619437019490512858644223367393425728900892125939613273692738941049 0 33244535586513888755662388243785383077 25201212084910399522503678041217895565 +7443033656318178261624920923436283069218216637101619105369519917441800822169 0 117599034828781259783879514980170263285 4682244129366517672418147440411932120 +7674116104317128246243320006794844428457818538130243350023487951014925229451 0 99218841949086303169787314623069596589 147497643195573215285131901454286457034 +9454801294389857964873083905510975414996396481509063825764904652566507872940 0 19613186410261907049224966165884894925 76162987653417149048693633230383125776 +14427220640368555004153228601879953450107119825054957434025085192822814682429 0 27301384580836782816166396172425589741 30579952858711227751335056852869278393 +3626548879026433462459900323997219792597298762481157476567669947854725865034 0 69675686238789743868824091216979465171 75410807058380994737491068757588482255 +11484484326389654617630911458134798137517430692393929013668086709387963575405 0 84608001613363167506601048264537178457 140728442440839098169237413212370763898 +15327616190270804643968916167286892383621114779418019823126786886534724918552 0 18853044112394421051519948350411128282 16630645622232611349445200516451385272 +4066560358596882333579077316185067203375715547202265984757269762002429479915 0 132169671792307462476951171913567615566 124695614572693011495261695679187261793 +21622253506052368019563094489171363215029688777987462164016204506457047215410 0 81621331257848532444093970395282872350 47735454593038525228842454508518064756 +14673804697197050495686980927552760145316485913113403342712155660850922867161 0 97246858732616386439371494674637975736 97022608481548679851725319147656928135 +21648477644381130139022330392374747838821377951749456929882678573452872385459 0 37947203105261458064597004197150944466 80244718662459233139129624852285665635 +2447162147146976942610673124277772567734721323746017065456655370861430468054 0 144081914084468380293152626129836250004 129896001297238730075743827259748585451 +11243079166886829275820162795129236214428078266373885635009680831722843532930 0 103046553265162575613009303411421489078 23870983078488047184971744985117562736 +4445823807351183366945810664078605505745130495731399069776677343628516782959 0 20441637978714792313465835468524049456 144209327558546751806619925462393613402 +11567009681447050078017230575723103947493147810213653158268424203516034460512 0 48814298387192682745019884465601961687 104802001882621035189083610946773312461 +1425301607538293493802098468662327057988543750237085016588949378148626915323 0 86363832869193776028503242537913987204 95217995470718347044258619588025881789 +11070730452754177189791028544498934892810140199367008239627643228442543391269 0 155433320629549897332039105088934604083 58246184262613812388239965768891128519 +1423800753123844555623243115266520025893183388993975198691940843592474213925 0 136211659881183678135360841644795687471 147664878955414491561374341712050409794 +19978948423543790393608023292769760846180525023567354283212408907371396253245 0 159820297882067231703309873192443912164 105543377670290750689670247500926843994 +16153906840466643595056891629991018985299298466802827953938625367030205206698 0 154345389632998521513275970774947808926 28231654744301426899418123204257367229 +6351060107210141996129655144251706738309013727154833137060657876282153665656 0 8481046837054026056102147849708977620 103579595522851589326703388203161688479 +17468993932379690821016230740682655515164049469071024953348819549348625460362 0 109606600218454276515315057401174169430 44572594394908953847168098729945926357 +13397118011889355828031153891922369549839729713728181794537464183077040733792 0 86066135209603853473278981105646245940 143651354857918178303858551292988460215 +10956750739069221792200690046716838678898580501288832531130436262834486735572 0 62373052908445012073819011909555689487 35765782914477382989685090244901120782 +1244372474953766360583406906056258609906572494371767680831922303833601534135 0 67877803276831362783594984553572015648 72806657139353526301469142035519317405 +9422168168141646719197670065216458629473162716609125349048345887058150845335 0 126800617368060694681187251760162221598 32357210355825736284356087866665091242 +19747162394569586270540438228898464487134416049591358133130768840309999729495 0 22543721318635409339925968656779506907 148252336790362709183708090087313006176 +5774930904406054791732748257640549439037002427300521812075937968536777761611 0 129381728211979557062678642540996754136 111249011397445827206839947853986433282 +7869916380173247517156869041218342357997711325349874926110057244674574131909 0 131992721832302066607856323412916443818 16557417837910070673237009316717923331 +4474419926085223123810227499188667218966777367246218027620622550266381529509 0 101471270887218562681633364079092583940 10991361027276362368935256945740457778 +8542092826488922515152759017773231352750685105366129500128361269416272895348 0 137131837098860123484221471405180191649 53516084093595345655540752022100903969 +9199160124401302999190901020445526779293315344151384707849997595172739510363 0 102114291447143807615543445924175069494 83207061658716581862448524803652899992 +715246366928472824274187536457666677306633451880904218285233056803317616452 0 146216816488882317280346530046335798266 17962724980664094210671326130289900146 +15568824431016318695805352863265380489535684296488072833284765474077864623800 0 49645051037231964605284493743277689787 149604627341800180528969216929243839441 +1338867713698862065830761550370451274345785283413167833665762376498251436330 0 99212656197142454418481439965551861040 30884101454839991087998997618331596299 +17004820518282227105473119377761200367961298370037693850930064323936272579567 0 82588829328088937942436878278665990845 86156285119703754608436632557963004850 +16179760030090990028423956433770305788678458424105383838296980315187679563593 0 59636846357127821751591712202707705584 120829523793409329365364894220330768305 +6924708397966391644249530186799483394531630412723339847178972624473811796449 0 72278963410177471723404715683554764530 61322485086527826835900701155231171449 +16141849849016152886520880342219593082003007394148777508347929520080975302345 0 63210380762203176431008311576053660106 149810354430835714723024054326218926894 +9771827212340220336727480154734885585015291830607762588007070095666537103962 0 127315397384500630876510844690798453878 92552057065340187822463548165472431488 +9597646247006951895316265355240166469160406794212290526173803074233451924365 0 55146595698821253681311846837758645989 148310238810928113863301066648837453457 +8465848373442502380818243577280492179253105498594334720569681107869854730077 0 133451101048113592055683350499161563860 113814297572446544749603286922880855342 +16673653132857000928380275544635001111469936779348716678450636691093203696516 0 69687063336547937332693262189647635722 47028826852068083201823677597655630363 +6439181605556652858268027192998705119180395119385479569188815638889386510761 0 126138670378803625345448506998329160855 107944646789713346674164346055547107902 +20056058673653006507705533571250129762879103494772935697655099346528884534802 0 92413287522364558449265158799874786435 45090575593929323976899713538215218639 +9314553185445691038796152671593920186670629617007841115423653025024535280928 0 111563319160310361226696462105863433466 34369942960198135839478071371124608615 +4927675434407010434192908778773106727870672086930323927970327117311926375034 0 137349766657253194323844974907461417306 37228664802908822292018829815462857557 +9264891767971107361061286327073412219579237133400928397329691112742202125433 0 133504748080119742812332049899559380041 106876024359098608208959709090695091078 +21239035922607569282561439017160081658703303219193009803256765527243104507605 0 127941568848838500748177750257074188988 132638228075569925032547445468233451964 +12348842325917196668770715357329540713732012334626978860888026446015650716154 0 59921368473223641826414036215286175621 103676925947245928210808006140329563943 +2219753737768880444965101007895419042638758013618081774117535009674221177484 0 131105321366147001564210323283626409396 33844118733696235679416574262791596992 +7194878590710675804853640127699788077347439261881193512253497408962266483500 0 16895890533395261054793594838090039342 72879958967130103621501661973489085960 +3552107302046736728860832826845367692507127957155289383325418083014523965586 0 81762797647997437598834018510547229721 66390684988940248209820729176774785551 +8097339585552837075097398405090059521094315234009487252835576343329440746629 0 23438106175967728802564576995787850318 53665161244209246105725166303407116873 +11054655230957830463788298366001221770970021210219187490726792616837987212525 0 52969092383179458327199369831696866675 67805529082176385107890148118165476234 +9064866667960745373257820646534360448388547323092276681589928695163353091040 0 99172726408657783804074683913325358988 68803942570330168616946756775615332981 +4085335107483456228711824671996457880786468675151301012841509325129606692118 0 5076874654040630438377355836550074866 53291134791444064409764799175732009788 +21545893280878247711733355769933082259098699652736605062388906457727183460662 0 89925294926152795057540681496719378171 55015642404984501503284615922403722966 +10994109279014726151842434916489679456280661131923745661485392437996493913246 0 40547872343875397689098153888030255971 128789524655963197301799356621798437472 +325328659367328808788528141420775240975342055043215457323093425616834994203 0 136676199889783899158325735369127301515 55474496592456017618488940429395093152 +1135557847988974470521207430537173999026125032166403865318345875605053812381 0 86785193037007706895442557835049074939 92220067422604656439096665435019721725 +15977032490864705745048699247093657931474080983191721393678655457949976448786 0 39375567166348018555873146433030444598 74750171233387550707521181505447136074 +361326910890375200661671650877500479008126344742210328486200419794238345671 0 123426060461948520366049381891582168107 83879919682805792996172598821245711828 +19559677070376545258905222496646144115978499302755825275723760316912331777874 0 36614334297150401183780374760562601762 133009903579894460388360807537806661430 +17488700737364198621697992155588754947512702189285299503356183327277722525843 0 65125450092976248924289549654823049956 25011646935153146668371829156662937936 +871566286944860963216464569539630013233131214898555558844869278576972512997 0 120056929924226259891435240777202595511 139235896101896705020091270111475935449 +19105499698699079805644179149269446118098430827893722479344973101197228438363 0 93449295397218795445004481890546637061 136990983915308940190324823208183792349 +12639536535918800122289087078776776207605580060505736102546974173097053447364 0 142368542641361777919497490495935835174 28930341887514949962677588934626556449 +12941689993532443291244234609289112374938850820091441614232177967544669454044 0 137787613283925954815035625600536083953 27591979204107365233094501816618965243 +13672488782862781255107443104968919282980607748905063766797107010370672505331 0 154503881568772565690679238142114314013 63508563917623905754560191181191738514 +17852038973435952038895908568340877955977640810087574953315125738034752334458 0 99700659722539029108779794443086474996 94431444547030296024548665008418709106 +16196707856201816307274184588330147042888967150448811965184354608079542574020 0 20624707822164422750822321035728161150 108253928629329297740560957460014155975 +10836657133379777240757002076404099521651607039779879821958228141700213040553 0 64955586626317456220191651896243075218 9614008823427608906681785649859750566 +9357160088593123992492745000226668912811073852317238647741225976146353332509 0 48914721174081175180330017564915057789 53505141990047464806651804181921773215 +10360859978912045230352351273197155421194605867225882119905445052651267237551 0 14679723065688440073942069799467063945 136903159801119617680346897228747122098 +17184195440054036227577403120957521856113627638309781147051386338709387931169 0 143084428086244789626501508212920637909 142766228648017924039211953735545318321 +5231671032826216013588041224816763328582354679502539534279382806972232153167 0 76549485174311203286822433333421195070 35008100149831898286258728448541216257 +18013923370691199448498119777195948527748550463339082266178599105959358158376 0 48937553643084584236495740868570428331 108570180185106335740010338445455244126 +2594023029665829426587507339466964716711832469219245654266497378245336724346 0 87176374581060978456038611063509252295 145075871079989610818627169418081490383 +15813880012217868472290885007755667283331172093500805962233835930047767010022 0 76341518754716952090643131237821699243 152841098823094172639843790719987012581 +11030007264506472133522047405214043268610700398183287187077054115104162031768 0 98539670161180354555385477603628449848 138866820075145778256030927513738716292 +5014344856429909238620696167627272156826126385672949387292660503471628455219 0 129205907403882026619518219249529467483 111842048237692303178353120976220464395 +21818397386381224383948944981064366738399732753159855194595213680679460637867 0 112898324837235896605205694473102896483 137844457667770529012912860678760032064 +4663573950204524768697374423110746710987068952403394719791539296704222233806 0 14693157305907829718141729111704389050 22736661864953488365655403288488629519 +10584176581322903196813350291876257301583814727864548713487127908733928647464 0 67107044846095716384663788649683494183 57111129590606090187151414525406121749 +18255192976839660952468262300841295115759668865507919120755139987281153550778 0 111072422607371374756296101045564022052 71843789446340993341908873402752004813 +2710869776800316374088237234669294401279430007950731893989140872786742497414 0 87283101401926982181563218843134645912 96965739153919797829544047162883029501 +16785558509260211396947085843619419681952604668717979195480607704849905770154 0 86574180818726941322334354625353221467 87183348994808222308006352488206993599 +18839498282802519756597954051334217801364762082759004229253520771452128229415 0 143245983752233963700458040686136053363 68458052618644111102134214205446966928 +15321017192736180615652525436821239856783979404748589944860809432708136971981 0 67125350214635324755034124561299868967 61279512469407048522813428237701085623 +13256401208762650391365575054332774615021824535162043517287474411704977777645 0 50307356023239464609626553529823816528 84489883308087183462144020245452058306 +21173414008041660741030363315170044730457160654891953005433375589718429239293 0 33863353291716309359512256524750995706 56644407163298346364898119347030825830 +10804639560060734993192884844123818828687595344163800515137302264634995209564 0 143328589441682921677398212504101530990 74444808071053277603300274430414203178 +5348205434817893470868030296569679878932077399532666795366269319646606651848 0 137851272484841815757512929697430218187 146523028008947142326880627325452380300 +19530603536932206465939490881275193096540470101958038201676457215044716225340 0 43694319418705769826227275662625147398 82855275197553782054071196799143405835 +5300535386661996315555957045335806017402338796526851211447184225712197735724 0 75027690019928158426796400753158303324 130317535920960243743711796423266337046 +10665932148295832385949641619158716103034021992020147985355072030986254326307 0 38263589710219153568382460239045332496 153460984846029144109557902578896169089 +10904459941646047087376769753596577310025828845632928273246834149347523729548 0 20939290740636140754111417361113398984 104738760930598289897223308451374976729 +16354713404570837199224819482664979719184288151272191810228986048981588059397 0 89994292772087346468465093391835886377 39734310302482244762473407624815510266 +8231085457425517431202832617060508575716190315059566300157109014010660452680 0 97488246584892977583873659379571620811 74114572896660693743309816516270465417 +1537687776979058006958206957941601847593037098103588991748885547964839919261 0 120059448064698830627561646950784031124 39036398033966117300204938295563013619 +17902796865730477230830386688771948667084601899486197156944880685165740067290 0 28652188414502771591426890410376220085 89893506380868841016392954870943668768 +5332223431391826475478951108136188762115993683370336975195041131627493058290 0 24073713274945449461204102910421598994 133977082569636963661536243748874674928 +380425728201550138215351789373127451647263068693480282132206203145759562497 0 27503981163725134547393549487842538617 106655950854004080229839562159043433130 +9094643860625296844594308664695409221741190461242442666085415089166714663931 0 101677022370027161416229269145250757180 10051393153790412798450973796217927284 +21203967679945449352592782828971643324871647645162206201344636516862710618591 0 154127897902987532067098930143895419396 51504178275968771159120675068776512573 +17133600252146012243030474668420979239399370607492821993763983531019237565445 0 70308694845002266677519949536281334329 11805503323436673874637124311480955943 +13305563257661636670996062269161632612639496744786522983956894862519677602127 0 94665335061753143079504184239675016281 20925409294225377486785868836834080936 +1613748901154869520717117650886617827728069142599559522430272581620768715661 0 14777480879203433327236849178133755765 57939539320460333626581422187493797300 +13928550854916627446713701982386387985292338783600390745764424028230307268909 0 104286017462547062920529917297042695304 85274371591593962333920500882334594751 +20509653999520528537247055603293771653278677091204334788709740880806577482501 0 132318474170916746781253310667652617459 86605881423325269640689758931479547037 +15162344391903873401033512050899866129866496358878497826781641970321833909141 0 119804145663446223032481525576291673217 144980559094180050667749710227688674635 +13101955570895084188986497275864908437114063735465368418257530098164376875185 0 51148814320155217668394792127965801935 119898888569725654988624099619729220792 +5355563390629056554212554571108823311415845224036293448279901436572842086772 0 53565469120923140993993624287110071680 94326573691304836739013984252141672703 +8460384886989908310931602038754624597279199232306451460203832935391963275730 0 72808036176991799723902565373830021551 91081963300692027433870871841576739412 +8717419877073691350459054010875134474393198145900956673514072015422183708508 0 68109057986592176082665503404977410511 82263634564452764257371433904170629774 +8296295216291850582107565138437939174146943807368701204871159359097780144934 0 151508408068363433091201627357084969196 113151731256104610672393163768897937963 +6564883377382889581663612447069319891313546754138399681719243846986722067401 0 82189868332278135896329410489570004338 45469948924932843584207098713031364017 +16605717347878739756034220921507751766741411260008120145117235175935231847576 0 148428706327036989473094290734077690836 44047402041058623985364476996563391556 +14106886857392864791875919224397360005665452938134693269011975507389125229857 0 29793790497044769286304631982748050341 82493501707645103318042954555585483059 +2246047253408912235085083655274395467668594419865628017936189788144934530368 0 77332580107145948902041101598307275151 119095449663896845795157205296684150434 +8541330885880823606562343433735574136206543561798846634106977139246130578077 0 18130450293687792937260412944503587441 74070823801559044374830465647592903332 +10830663321456604435834668466031401344725984350210729634896300566709255407762 0 82401487273040912042020418845704792908 111369567623619451567135876069522446884 +4020326609668276996073640956383330916750640208947814097288238928485167485291 0 11473224745137505769934061119683114996 109844765878587234632777043479132057621 +11382383428200735218663675979424684882818546916837101443032961519479025234523 0 16732684788627534315372869731216043733 11824419373994411050179083656205324695 +3778983568431381112344973983915404135338337441402483786364364089326080798704 0 22861276458330617253082557992769795799 61656847682365110522863082980725179982 +12200297907550944034544355238570538284497195001527189629607825667003184412943 0 63803649384759128179819013133065696862 137843720109479004011499732739302855738 +4136343231231079340230807312151713148386887206615730969370321889189733692156 0 77140227424098371841521188814143985754 90871902801807314531490706563386772310 +20872772636232776961382163381968800720842994407280498982095155046863342733835 0 78252760105754541837596972675848315231 59253312955959096895556934359100067130 +16491269645153102126531948069760689466982614884791048128605209593318091455454 0 85854783206247269874144071395629343863 38578300455303327556573962606837376732 +14523761610169717398707441410223306113512688556351225514714303647511662415422 0 126850193214858275902290980616071940475 96280198091281556167005260755354318855 +21607002836839609718999282157949841114290607236029856850198779337137481650937 0 115171130883025479177739711849271168098 41592008365038939737683102136099968353 +10754411905711122082940954016221786878560405082055143973509343221109954976153 0 131305990708764326502974077736425790493 25658895735782098975980691688324595560 +11821881265919155299977527935403241136710867632573523651653207667913737290887 0 15127548028744942410347864892534389506 123154175945893779441153542906002934256 +8734319675692523938942576987691654984832408911092402944485714302746850723304 0 101623394709175281894543999984941198753 94704306886792286824000694116598203340 +5908640987105460946764029564253033076203310671852281178184244879655993285939 0 116434816153489935612430370791447436211 10110361918276723095894555089413699697 +18993125208180033040763465715736119912087275890056334309534379345409559419844 0 115488526091524726694289095251193971762 95203514949554076918252847416779961142 +9815783918349002570137687485319776396025655170344951394813839512054601870777 0 51259709531084359930387591163750977499 24335427874409311600009171721011600353 +5384906812420282958644962103221151908825559220252950216980796805076415643500 0 25443935448456960581437203471057515308 102566220934195301190074970156000524791 +6400510987305340661393210078830682274119647213879800994514271436935849070847 0 132295663451502777701821306904544745764 125607215373062291889170887244116159387 +7201766139498405181712054740340929744287152935803604773699555861545298967521 0 37878214286495837770973378950688857247 135768101671052318899102135458283219791 +3118499952875249233968746651844418613724365615781254273838239692846519619922 0 100894620751020339709143349453848962708 63923403064982604386669927273959137317 +11724334102378957985385474716829876290732939953335422196983190343257158614113 0 148796111496142259056976858846424734970 99216556442865547987750029946291263911 +9366668579881767849470700769571915587384674210114828607038420215849456902071 0 95295789068935480764435549759044485090 13522842882746308887902962191641792451 +11015157408529988204387817789931368592500373516820422216151802400309036893657 0 109914143065292007160908538129169278911 153341395985691233344888198984708299604 +18850009560756628040530375187744184623271158962494419496350723494961023073824 0 94326412555743262142390939833492038296 104510032694164085867177465356143971004 +18734261801010584456084758852354367862624605169431252710078686623151248474348 0 24784066262621967912778170022827001566 113834753515129124682193554830504731195 +20578444892636482330285650175885356631569695608562894008483373704703117883302 0 44110215897176076448881924832881113865 66932386786310875895300069407760332496 +17849943885052706215105835056249993390508913024922065285518719937156340337836 0 48250257380451185596555805528576398059 75305574378810730315081861113634074306 +5938772252081441124519458943374979800176757759354636629654383471577695859592 0 35784620897063611743231235935191724549 36228033508010560931910032926049439148 +9640528084233565312433233074579691588315341558423727889705484598249929640423 0 106363058386671074327784502987176112381 19713267478647601940208757277518755594 +13884104469949986574274321871654594004123859602856221079989131517768816961177 0 64016602471378937507151580362462818197 37288862997087902893162006469100852286 +2522778370106054011854043999228290900045944867032472715990136341473380611561 0 50908847229933752249104919495059758795 84936741747454192290242903893716711816 +1110828752747549233028340338535861462073903731571707131090441750541184621249 0 140759983783838408130155051398705888422 21336757583287227684930097721457856504 +15091255345851317612948642627684216251885355867496351633021474460225736413242 0 99638951391073541268121057526190676883 127187941468942571943609917419990636632 +1246855297888052769303039794829448127706189374038477240487936700369157736056 0 115045128399105180102187390771289908699 140675248595828303004007152042964550717 +6406492415962141759533484242131626297706147383729197703926592288426734598158 0 101450218325085809780937426131010288995 68543920462313323549107992748571176119 +17779695552743569624203313258519478236593801014182834776205308352377699951109 0 131899722164379162584144002088457830015 112414520742048925207756188488535138920 +5173452134673506308552517409595395170672787497908310251377019455720001909834 0 92087783164269448074277748901774579400 70278723434447720772855536557813745581 +21081108158274307380985295085467074775702889189774315618700094812807792381814 0 92724740622131631442205325895594181297 43623343251768665640980478916867143526 +18992805362747922602236582631190834388142787327777776301295824237233276851710 0 52333368359164758450077280341613263853 131339881172850359269980501625133425416 +17823129244549712442496414494126939607021265109141004041891386632337810212889 0 21624764614233352698781165156463876568 50319575662427503509127569907934584683 +12618903785163753692248841153850193676921730006546433521223825766939288778869 0 42529353661788273581473844897089029123 43476122652753907482673680415619420944 +9869344531120176959592522039982882281154661705749415739592608475161322976750 0 11581110786762613964742771210487476318 153064977872011211130372800082957961094 +6908776241742698728893214677326857161157767891751243215554954811382078863637 0 37278809156219159807867817621930262866 144687147153940935882206247558895440284 +3503272465968792861254197611407897715691203652409008947273389810221049594028 0 14511680545222569325083491318164438379 72163436930150569125615475262175244881 +1013802213410278070068746930054401107132187255570158773246902221908961727239 0 131481001971245604974571548375922331771 31124290444818692958190048856098366712 +5097009584534971213418953408193447245784608598936825506465816909559651645052 0 32778996884868213656625749231189927405 141739453746600140096266601558448113181 +121458253919564856937697421656074629846168862544226918241706407944254853886 0 146167096983714083248076146951315423587 99041498269109410379112198242212832534 +20432567191717382698015014203903614754882188115096528496075487286048747802595 0 58940837099709062051804352356771120299 150678220028566789820995262688485728345 +3392525470963260420241015700896905984254835236304378296486652688755839143837 0 80771894660729634407611544310462400427 117484812622555529112963411351517186751 +20163524356848622168724016788459468635156319154467230293919637601767176312443 0 30352366167830386350424961984242886706 113026781420627564828644620473983855230 +7862456127596990264207554719598000682341005634647421774225692513462531596070 0 146366739962674338763668158610197696900 86243802892245068765134667644239931924 +14777071507559502859785941638657765412773670096472823827664266208606478798922 0 64788846616680769196759702812798123287 20302306474905235338115559704158306711 +11451848921864704641883561484594778534911737556388136444647135893167548111826 0 91582290106320362045711126657421217648 28749034394465977045357913095898104903 +9208352562862853407282920913301592727138189149899578101922667663564253749223 0 97760915266541304313425096525238357741 130590858468334384059509822356710337779 +9268091218761492177583715716696779433038308182339393086148231801030698319440 0 19721264591239667810669607617281105678 128783216928016905142034904302592215788 +14591640320612336332905789229547111442008947404045206036432821357720697571919 0 31384052017792767194524214753529115417 138332549600592183615151134278967176980 +7601853154634203564044653872626742639354221502074706097305424773011903584240 0 130552691075832300886583504906293696417 124573838046849311493192581088755503521 +21210871507194957606962596349883567045692684057801146962546923863250367772534 0 98234466374326077519370339666803212425 67086934521209181915854859880972503586 +14849314134314970701618299336175806518810762170054745948360809428182139207859 0 40845321987348053441298223118193602456 24027121251963240589631942947753607994 +4220449271316218479557489066589501128695802839233358766459583030825849516486 0 45251278511860382820295175867712561870 33392895693992196830583271822286978091 +21366915562467875953331307333840312488764731531267997046940326768883351514153 0 23887438716715283991464991075185400766 78331797643030519360891082920683592590 +1303542206078167613094286301940546868534058324916639606935352830019203458745 0 60984339456630510488434991993402344075 123828907542667975674519369475385615048 +7967005283628898540787633664844343937689975498900967380371637721269180978077 0 143977894858947170155320371336011507908 141926991478650240189703023154207934021 +9706555806895475098970457436877188601313046461187097073732489072760280000203 0 54411315451861724669027248261538299324 41384251829352211472370461755237455701 +9199841874580515711165292464262427096995262963325430577055528106888012615508 0 126741036173334961875331562175709820077 74410584569752940484536145504464627188 +3216395039162543114421294126831054616705388405218181439048930615169948970066 0 144329512034692042285881127977459573269 90034660253504351089760399840873919000 +4936411971571051316748314230772635948714942372040617488022157627958087673317 0 14085124693244694416029561222060033710 40886157509263684078478902765486033935 +15441742250045612486335798071682269445257432434954694800342816540391739975243 0 82305004844443175761133327799942131178 18049868482541908973250696716680374224 +1338193987983460549127486341860713370855483669297806733208634271042130564642 0 56771082835338903732504051886623639001 77829555658027726859132498269168235251 +7679688112476689978746704670328154926363416399345865143143695412717713752846 0 137455884292842510755005559056722808360 136416977478285300664563703298548712101 +8417007674912536575576604907425987836458326367786138955156729621834824875592 0 18128551763050584019203282584912109665 63826510614252262957716050968194104284 +10497046889461106457440470338067855917178574378336650967861617288954661335787 0 88986978808005639355689232180577995218 43439218118163422518779185734491534516 +12375479139424628215371355527957112462002152397796740599521524875416859116969 0 98804197067113322075804967665882902895 63529284485050370142311104147867322478 +1286951160715832954007057273466368425733092353504458846612410695656851163305 0 7460627326327327770496450070599887262 63533007830810200726329450957034402073 +15487107433304978959634828448368485037691636047994318071382324540944089853712 0 35229021757440413835725537675408404095 147610620197173421400054464871573073831 +14111477523623801296192279777389994298063873255928066401101315394384848579597 0 31727452672081377858571829300700754628 138781233827558132125594412287839375597 +9826149083739615308950715971443508759289775352938832482298447032174875268014 0 71342470718734851073310613246875759208 40377219356637658765695074385618576257 +5524004859203045098005551394312295088138045137117443853928007080848498959340 0 147003423468160783646374057774752591602 6847604983412973136986723518920928970 +15636110155577795975961694473480195469207132165726286137201714083155635760308 0 146321951065145302288111852619506869214 11152401948514590568913771305915790023 +11096235329597805574011290633711464507250406932173113076445296942934721915048 0 86920132714130895171813344631972475353 132514312287343125701546142232919090289 +10011724829740986540034149986813351586776475772386473535223480932266436279781 0 13024930814653439307157908948102913907 77582961967793203240144062965954716556 +12526590664749994148478277304590738285818396266637896550765597622406212382126 0 140665007310002546777979921956613380748 99276817275578236271191291577354821696 +3139316484052398122082789441352231733554825362678562077323404439982247293640 0 19892114204797782313058145438797802512 28417658371358338571765308273739862595 +396659578877452924577386552145848163798201157474613714233362341110725265067 0 63334103691444488916948487345080021225 141234478347949915148487757894298592683 +14564896656135537368347781215462593901495402829811925260011182148525280194512 0 93531911137594563007327505460841217439 57264679345838475599677268964321653724 +14998487026235249693446241096973162288803705129770472781787634876891515433648 0 20303634214026115627576871494279373215 10589464848034593094679617394225501694 +17895401077571767713931422569672082583499292858923175624120044696354356306246 0 96664602599121051018893873010428584571 56464119243283406010805453707076209438 +13287124534642201712458372167001528760604701150927447836541641349452652092341 0 134889914361890456981962931257972165317 66690357951437779397168678127907414570 +4062817382622123995807486340903475336600775478651177232119867722082806160650 0 121088041054853405759288115938510440296 62714826665365344224816053265476177787 +13404386938961453065167775420867372677777738194730734725936091577847819731191 0 149675825785433130352319436771955461167 39424845111180113934532016935945039875 +6785201547190064508421559377393546722889646844458092324002523551569064635354 0 12261711783995457755716820042186699051 136854226834269288241071113613549320045 +8157088011749965682267752845813854154489860688578029096700633084234454930895 0 47409441082549775454831544206470148446 102557200371911237387644512592794584201 +19878549954527748137587167266409766035432216985902847683422388102671320933132 0 101675417845949067167151793345157028904 112209950980096503991228468268195731576 +18114709950162905187923797336486044742359036674714599547531728627010596995040 0 97115920229731892059916134253930043341 39334331941114198693866680918491000404 +4614271683197046255526280879394984439339899681728826314109277656377501536478 0 92283020497994383906639093908709938926 55239208858067317956116871425838117129 +5798183017347162040153990035449931112038970750767844342836316646222408411943 0 45623979615402631782333555334726971759 26118107488418738964145660512631113924 +3218397319892384756554424866231448323303095268258698315166574417709058224260 0 117875858602113809161982797692562916461 90542593272861620404416127995948153907 +17258127988052713733293942901662240808776310080471939702374436974402127784799 0 91146750110754458469839193633807819630 19982666039769239359873544994800433024 +5899994018140299817171534289060052486459959335482835759652450905433909029830 0 100810785394235970958092132874045051370 24351554744031124233596165185375395638 +9707476248663330132021943951349320951260114074822426629328600785413725674550 0 13623542770865657071335648925125530642 13898568272337847123921414107835522726 +2914410430014428406646624783984999062604359129325072140855686487843861088428 0 16890592582866463102316212172850155035 136898598495407491102083338849187879052 +17852017682801501783939981399761840747564603114085758689832278383539087743016 0 36428010591102881017442843293047610222 86299372118949039279793906451153234854 +2667253114728230772161692663838384460915158621183705075930640651261984121373 0 127694610826840621265914685408328733735 83230429005550278553464996768614259330 +18961982642453660928922842638710964836503023042234978191851911695513945007575 0 50776378715572071442031117831537634598 16370696366790283109885814293839183422 +18915879796706137556707999569461839715852905176179213922663243603692140365073 0 23731390888189940468161992938193028084 90520110383834940215133956222498007854 +4276079371547669228696263800380148036412289247999454331970217380681659489794 0 4474176622394030703392679285977238540 140987639915648666007274416146855906505 +7143847956207292740530284226760702268955961807856421148718248273908159700901 0 87828173773502697202083051503575474208 100150984449397059311937399511602435522 +12210696627827556223780611230469126951533461742035575661358388335553101003929 0 134956342446799719399597222219614373522 7376442351042602532510252024873474763 +19246056557047236501580192098747613860094587617276365789922724583272449410844 0 47842311545201094186208209407734651549 114985567979655340038352958248819054223 +3720484268586184653093674810725513516927192631671324784292008987442718525071 0 130639892539424353885253434129533665332 54908279722412166104705686400367941492 +21601724890380573268255735824303182089873037741570268492956067919259223012010 0 136187138814193196968787907035473497996 73645469707345581942309624160205745965 +12730861766225566495730692636378278224770291128148600794332742983122474038934 0 19654189419830833799010210544763819364 26351293641790609304325101582775383246 +1978050544416254967348858996351251145949645678415513982064691415973835892509 0 136280356217930237558376560797527056022 9641565040113698813799936031369377893 +3742012010186901223652658243318341722558902861545522632733182269810335201136 0 36629124225265040527790697850937624645 10859664423703725385422507301747032021 +20686071428799159524221801975735949991879333669319119023239212017727958757423 0 163034285836366131810396335522201945904 38897417341132695826724759964325324000 +9535815918867897702501270850257536590738137815857421484516539354121689817788 0 145745210755731218279920983345457076992 92125808502270603286888249598929506414 +12848650368730872018992464563154827318916033153912443575833862264420054911228 0 128896321461466188149098966643151361047 152096178018751815803561958657018153528 +4442606521133839844614004181331841848737970458887937624199963729266390179619 0 149011476005130133894527371951116568281 102846498488372665414294524793482560347 +357619330669663985617087732810176682129604932261308600875176219354652922982 0 103847442623177463969641245036597634340 105604682621653949661104847979189557110 +8307003406953701384585437389199525841241833535296650733737377478634717655873 0 56418186492345603318070195502169818869 136237392937372159893030502396851142196 +20581896955339499863314267925144369416323103325529894959563996879534793926280 0 84388001629418508110640776812938940260 97916645576634224609170498437400337212 +12416763829908165045235956549654915849485020697700603783851244934493720203380 0 69908974801444099135586511860613634466 49062598561538267261866982930076943873 +4987416035083733525973957716595507767535834894741996778389788584154257034416 0 58161583950008444336434563594990547246 80772490866812495031810536695110762845 +20041384296047972875346831329539887308491528526446777378863098893248438949239 0 108036697505586643011940513740740196464 141999208084529154882246427086373279977 +19241954056925444996605700710684949914644064010022280637171980481344664443956 0 28679014702593097081643014815016859165 59842582308336098323879543770601350872 +3693449195901450859486790237472275265423975061288742411315057482709631177897 0 128879784200508226117283652205987854146 72141427796148942373009359320914449086 +5523647801399131796885074465819857389113605069630206898146344279605556693275 0 133430419652081349416700260249949601608 52688819177145150129884829814261782286 +11860048821021413630903334236472190470118559242578296350525585665737891698422 0 18372776443544755757839487353056065253 58983618763111795855275435611954136966 +4458215882598524622761830986582907904672949928186096928880304464442404241077 0 124486025957927352545944962771449806339 58720693470876098137925680877386373758 +15707426943105980097492210212777162841207802973201624325331462857297089394630 0 109273682785081206606002417848549783486 61238798097410210101993473148626112202 +13957851964124194755690938676037072049558452391974860206609905018241392643747 0 87753179948828174599719289772165344348 17069900571436952081297233514796123644 +5813486993747423614394153320955648383543901584412830993170206732193382799558 0 124543902665097614409816329816951750224 95266304223994206719193108126945072953 +14667839736266793286900177522585930798502389383978263094177378124157517561013 0 22645584717372056505011726085885935810 31977966039031977486813488589651199957 +7299694358589669371118349361230078440422261077495769750627103917723401063490 0 140405471997020111481626318562321667162 21444207978694923637491851964951348740 +13735435710689895797516246040294495817374259521680177034884075405426262052159 0 126104139559134898322731903029172789033 84702497182703826279021786510770821248 +20330500064371003684661323919129198962642767474306768149399583024058842061869 0 25473993563671473026937366159810836413 114330395435258944630427556016112115860 +17015908399265549404656365345607219190932834839376960619033619970215153846849 0 43767975269240953316527283919990013112 79292311474709140797914868403628366943 +4092536196216481095030298756368321930949021762573057050131107389390283289631 0 11967797252826989512111967226935107236 123037980880993989684063189712855105760 +16090894029286113453394458924793480116432566286566308319807385937569787078469 0 86445320173170360873372134640394885328 137223934835975353464410976859540901526 +14692588553870114413345899700111380807689095706182681253752784657567339032818 0 96219795066865121122580202373117882276 28480080047369264994201833470110732344 +3266654549083320904024774360189411725174597025122482064722467816957297966915 0 64307120794502627224589796579960455564 51653543672031254554470749839150054806 +13813539504722398169735805059347024588492281926429957493336154456241867102894 0 38523840865897820049341168222949782084 30154952157735089974384787806893593046 +17197158847256049049099138292940497856844845388464043169234610938249328503800 0 106213501787135136389225757679142728635 127029723847876910143301065654212539822 +20712037939497496192804010297717599799771924874999536271578735985180060397476 0 141431171123517904755722699291101099578 109834571731645062466220859241764672561 +6334819740892196091058468656624864135169279297771620863137144254522056077841 0 91459263913627969844256893607492804859 69831317422038024785968802896104215533 +18195767075198493095524567434474206758199983617898640333787691576855281974152 0 134668538207583972062619221046921045846 22213521196378834960541043380369589358 +14023866312938937287572015944477941125258551879702704304668041187121220018958 0 144180105578645203742312630980175479054 152289402171271546689320551384238700104 +5714887580760655414842433943854004219882719493024490159308549863774164501348 0 11419722054954809820828058874936469544 46031431774635803026053930421763510504 +16144457686869815017789991745223167988999122602630486881761027837602491628958 0 42430337983890955580948429745144836486 68115576137576981092415163560179076596 +16825309791670049082171545388426415737914399809545405740691458779807712510549 0 85944248541460814157803662886336237888 89362478863881229143763295790554057725 +4812454497092632573254351195545773789397545104158659975562480716476784627149 0 43357056741105004192188006408613887153 141765254344781250263841542674934796922 +4779205253461637837145123390355232515862862375382265003024122063588169806108 0 126679744385146266213285328195257815678 115213451143031782832844965835238625913 +17379467621772802951201838921302415097366066314792091014212153957380047896988 0 125993242563357622272510948689380402071 19879603543982832347038177585083576367 +2957260999853016148406814778642953798774122409700294742726883539396233889303 0 56944827010003676801928325144372747019 91896424910557216080107415147336597044 +121099358196517765613108240067037867549743530282548309558029574283286085243 0 144081437662222861328628111671162045431 39790939973329068297628605789469118479 +13196679823361890887496117623114618761559523832716513714828933126126741740433 0 34861522883074807869082187587196497425 55764040077561094764580188633426451852 +1448357833651497318747388460600907913420436444631503189366532147650489741417 0 47226107588379060474597120957754122900 18458999326820831909262328456929588360 +21488205615832860961753897114454582218353818723470382648742770982295072364877 0 60306727320578539750494076849052700207 16248562841347185321330756615248804026 +20764530780460141553865787564702490525931293454205906854616710159977934541675 0 29121633939851249475554049669551446899 129230213121506450996926051679526688697 +4127053254429472360280699532447227217295861182117124363860245755389297263257 0 129873743638747764402930582652665882643 140525708621500920221907725590190091688 +9510972476806707967889352377926344472315129901608320327517303878756644169756 0 84382763582881066249106357579692343940 20424337009649733011937863447292683745 +4100004877474630614093026835654356036234627660324480494140184222012025554119 0 111454821053240267712883864182243022273 14840966250130358674378361232580790245 +21564061500868455552374635271424443110873290171366432392055890015666597480157 0 33096872199416639199545131822177542902 57268355572649114741826224633134538541 +14232496969188933801087860681054441058404945739642889345734562492871863391698 0 145072166733266658485873988554955236877 85719183938448564572311910768712218478 +21673568707014063106380673176697966489910346752494838637381805803361047048740 0 89861915557762748112453261994519028015 25398881297641465424698399702405810854 +19168746023983977480112889499970136154265134164808978926192461712658317302901 0 105911967805835513394347132352858525308 49082603819998306002052187324385233769 +2572709504867550769898886304796216087110186436217112652266018491246056939176 0 50922002236889899277149851541414367532 140916327962257966472004689483262508935 +19073069870951676045247792642385742988181826336952054342010816735543313122400 0 125340093675700223006637016065303420913 96492582576135623371419973514691483139 +6104826072322709110364442955464355287444377961537639133748195283040247959698 0 23448011258442716611733045561022662897 100886572504952511303483198873015166356 +18879921967245395536634782041551751835939470730016103798143353048517844393779 0 143670902480939282208169605885383751293 137612836978759254145467221787573160061 +11001256171372713953092811055803191303479361221598041901910401752562652709913 0 81892232478475881861568841326876361221 74983396578841120249147009436371730763 +4763602570479238126579674408884414538899131317230326492899320004517075364013 0 56070147706916513860089238156423245190 15341008547809294500462937127145714123 +9044073409870579032593195601690236928359931230994805794767893890013916138056 0 19159221690594870674846151396445546860 19569956543829648513365540249257570214 +20159051941808550906233078607072941139301117858816537542850349914459095409157 0 21503967429628408317788639768305617126 95201071311973169246550313304907978386 +349347162387925312496280483889599498886725126692686809694478916814845460761 0 95733666682111105660417919217174039136 54590700735115571434759934564246582402 +8822727110522930989432650454117390686480461099018106129292611297714148971323 0 88160349584862270788229949644409466485 22010442686860639929775689468716815409 +445292251885435713943393277431127909477065515922726078647837086874201779685 0 99640690143503662786894353526946199340 37809949770249041185577281940624716858 +172982207375200048322335789841558446114946223763374896131173546046710520450 0 106481912390520025491389827640080821244 53942751716278175673727766827619153350 +20079648208857972763680274699461731151252562139792115786479922261831956565953 0 118877154857576896624765877286806423460 114599821541935330202364009542661355028 +13045805666429578353940257409565309408951129651719009124871619808398784502711 0 21549493067781799631137621798789760853 35950784074724483543563985560818407432 +2264612743459628441270670900116988437870044162226513642319964395885562947233 0 62309792815164251392025032598725111040 116958972470712457941482058922332131881 +2510428856188763713282909552872091509355599318044152166361638072610727584546 0 4404597221460081863383183442546679423 18219966988269011455155136802527608448 +3275400947584317652564517605744696612537685105676157045728823888077189581426 0 58827481906218390244768644942405052157 93911170606739652412757446518027159834 +2627894197330919171401315575543425955801426277016485171470254503338604572813 0 134008473745007824088221853738022392588 37219172987065227175955434190711152777 +16159945131104667375628977340925839724894894509232782011741375136732552257379 0 35960809624192126674187750826252049144 77068101371836662058860113060337571900 +9263229172891852689400612936947309355854215175764386372534637695162437187667 0 98725066922440112206854187222271434643 91045081652234005837391854463343110403 +3095207311267567420035976701985542344184458193027353264187885369192036217621 0 14724148513950637236678875892363993476 125069146490157419260177720474326390303 +1081799652148060013572156813107628557102694222787357640885856169108893906121 0 111066009518002536795600480172363826265 56650189812766186599064637533523888448 +19259739007177671727215696798641406905312168308616023272582714736839528376698 0 29087382211900932754359529827194112477 94031063110146648810532870074311199586 +8533516955016732040060213410625639091039592322748366054295623461647822233911 0 128965159945643323854498991985642676355 15192075467396817028733531337024243949 +13615037218973649702520892117818190320363774956298859523588370308369469751061 0 145794439649810691119426123382912522827 99497055150512912195955341020134338518 +6225122470092248426328254886772058831835496134345818003064226006995234768219 0 98166826115440895135943089025296791396 132656264405547232165970673948564185478 +16895878356118560023810139573042805701723380777481597995556179138738619188061 0 151967061506231599640182318184496548946 87254450603971583985055596693284930073 +21321791861226926998943789267386742957808121775188011361103113472550144410334 0 98871635074236707105865241410559484730 80212968518983989459473923320885678320 +13564336017723340724993404425788184058216255780902817953280054189252140872223 0 31543044381097243036574924004843293119 144906489816999860493983460467842075562 +5918417570340862134608896228662352551431838722672955883089256685694518275179 0 11556084129406557970228339623242470582 10106220126315131058489799027549660718 +14168991345074243347965522052935186827090806731158576943755562280664846496382 0 123906971478251910746827613203654564532 75181217032045468229758958879279562370 +10536964977822463281828810951980450446367329931516724601953097763117877308528 0 36482795809958517908452393560184287831 29819394541213580731679044648011557268 +4349752837260934538721628686930665164337058343891367361653601221484722271005 0 94513159595441204676779906629308103212 106263430132263711127237255472644652505 +7665257672333447498088033611049084835973241648135240946910402744444044675553 0 92060092145429435416416644597506800147 148461742939332570460142263472599995457 +12931657764161080289032576559318512753642765011161637187580781836570780498812 0 86655563468340292335517457363961437188 104219194493943518652937310994248976905 +2121397955825782481248686651908658822706587806186487939731673207583342778841 0 107071263278310336285108208844013111841 99565649236747160327675914731640550789 +2472062537124481933543180905686286453593114386582310541773995573590525213679 0 128502210979137483540667528197502099909 116347847190402325515778621506714338115 +18560428496995087629357318901751994450970174794392341767836083842606163766246 0 43097648003552176971522407568515649455 99587274380997894507501395444594343482 +18385340693544763869962061607346913376106758340276366357665906991426246723121 0 155992202743866314893922744098520977329 54040193442403274073106129584452489292 +15340953094390162865381763419830743022134913067683208179409921929037009663764 0 103783439671483222592346482960743157700 13933070412432958963240991080503089299 +19499339962983997386760051103393003270923677990476477710143191028590201507526 0 140069058016249379574008059416335098801 114035003418721194993902078964961539803 +12837259454978821434911310862813590386947120051864159601941253624023309567262 0 11338845634303663465326588512322727704 12324707533171646486421474839479037549 +19331471727916467313059457191938055544657719941043148932123955387441935419425 0 61153462707268254666030720496579533875 21690999347139257754279413721977622855 +19281152190244651572034145071373322383122615735890087284497409245552962608020 0 100321096586904961102241553245934503699 72464675519943670576111327225787757259 +13567120525484238022727128853832094142989508774348273559670592640058334680242 0 84088851800398953561012253528463347227 109105159933106007278421148025322574567 +6964134122309627932292494272939228743748738471525670764523359342216840144849 0 137868692492510155585645988469494560670 99953680603757517721729934028044188600 +12267849772054866418097849837950823012075364002746751186719291914776422523350 0 74093492093803947256523033251482280821 98264972286000122231967773187661793013 +2193459662183386321244192716795391581761696110239124019823916682539855582950 0 46855419267532877524388568895697137423 89811107533878640771255239784975811541 +16495123023106115130921891546358331749496101119975808218930811120660047616644 0 82674521500243954025960819398365239652 9963640846978075794750984683888733270 +18556972810710005134515381667524631364599589553102507617724471419877123544601 0 17444802928506321774205246822247841223 134642098444804295216949179247017943071 +6915010365067176763602882668248828893391499454641347223745028146569557959189 0 6535410018101867951012270143606141087 48095628054058484640055801089936692186 +9443910130531227061840486787177707580875844008877413257262437219740169149516 0 104107356112763339644050724081479055081 57362523783719059228171278247010015567 +16104481728505822612831962401627543991311739739094131128443431918915905607025 0 38064818867372947342010574102364757191 12797614605016434968069069700368650034 +6060952421827203288391535782394724618157285834692279277884270373285760195252 0 66227749925225633119984583277935502594 41213494835693698919498084500738550410 +19650416219130254402331806602715983872175810960597185973141763670497301769351 0 66136134151349174595209993639049102972 55430030791729871930418285623035111605 +4237906523642416629755440383097307089966394692985515441454516601732339561426 0 113628514808213413280390186544604309751 82903799502031318480180398365443142562 +13094351465393802599327926760620294048237620432395181104123579515348367408562 0 134735828361132617290094462167768868366 61664880068950656134714520071365900249 +8940945992192418686347707878882369270895783920547977079265136598158023530829 0 21162127876359676582808122845202134607 76678267763887970602035025774360022593 +18182740139691596096018450692211662081236236228437982543060764252669261514498 0 48721929536945834600203183933622467548 112594946662620836003386560184731184271 +10874339135131065986663497992015686371202833085442521789871766910367830775914 0 132967339262448200706380349023292092489 69537917753026449200052884251613746613 +5940698840414420187780684026864372675271530071288184115214250588317745545018 0 94014114635496161483355636399418306416 145435280883614002835289307860673977976 +20872087261437330783919792457702505383262839253709332964846893970409491903538 0 23190254389115446781001984824962662576 25440100555239409775294101646253799666 +18297879412360880677133675028573962299397524346719374570008101802100355036052 0 87094275353627073357573759896576163142 37620288857581483076658491234020305811 +20387156668578719167753781879347928579362216587357803652206906001110762445816 0 124899052213690635531136773823778803240 22004745637406270586025146667062606845 +19907350827823897135699826258332838237231383298994819898883746696800689811779 0 49772502066886518231755896745204232941 13299895749181875733699898106681450019 +13985780945470195425397131129882814952436736574989903273977762104145489774638 0 59983274371904506562318089589914764370 65839553912965946809125072805563199628 +13858392850428840329722241070015025975529421575954576548885090521450397698213 0 63343225700670303702155696542270295911 58526163270103256578683305882711067352 +15243348516152970149155910000646037517429011751885055566601357475082872003201 0 45245915435765696165906044406412901697 107307836920139843883687584721294860496 +9690006689576824682164257993969914105316987166475128329832836357770717191971 0 140836086027101193083385538552638747509 74477850755017069475667413881463604134 +13882028114418638059368301303375480913773753181913479891732418442060547876143 0 38197379279729644744372980370551777911 21073422773175342521767081250181228062 +2959152406326762487976648963471028648880271035038371815734334187210618671508 0 89727737598220911981735087859888798843 9604325061101201268929641953241129603 +20137754143026532484708082715379335650282903195913858217995888631111463320777 0 51009350396322832311786364095278390529 50599353015432286376057497973609587467 +20444574591373934895201480458151426046396924468857174695912093624703636026239 0 49080720059465345758760342522312455987 41323211761229039213684187578644092212 +9361890782903368642257504171231427046129221479083238653805021054678659870204 0 119358451181121251480457646599025261943 53269492467999596733926245034248224253 +2049562809685422103379250135949276805876672466004808454873059285468322693947 0 100936241046097565363375500145997612373 47985804805579080644618535141995330167 +1320032313256333659804010302499630775361478891078683706100198036321215722698 0 106437326750191567231132831167008706683 74738298053105325871343983553257150821 +15117611426633504519773636628972722693490254510210914605859641156636995794928 0 37088749427215677590377222475430470613 22748186620706117665179506789672177514 +17490343702930070189152723883051227710557640861961148018243813621711245746600 0 72393808523595944920892866905106496898 114188830140979166680799954276855571600 +6796064430388201779232325916427499048952672176224774141471516121464715077546 0 86334773575710482056430740275507709460 69065103388162682613034305138105109823 +11823838814045258154927694235261694012703533277060451335285537832472022513406 0 74614715572608379877574905632403919344 119500546992045339058312923872174428948 +12123161115817914571598395010675598415076576506061976826960515159514743096333 0 111821020320835571899500395479636081641 16963259601306043826668174466774791993 +13426302615388648394347954001960264629679869081869837299541401518383371131117 0 44821427450453820022229964790725766568 40886539870392879402419960436238424475 +4055331283619994657215376526773102493212142457830385467880929718005475263512 0 130722870612500646043972798483643400320 75435721565464353403693515805882904587 +18218755926009446427389104798038187893868770644384278786839647830547362276147 0 146013220587124187643844868103442928349 76489599363524033384583282139142447614 +3723813779110272828459631331893016106684670316794348282024527528067822309106 0 94057027179367834190429802590050934418 34597037978868396611413208385236414601 +17558204449618575917426861524353677829303469556172216629599661634675568934126 0 55661718558653150374619155897398011109 56127364874640241605346925148568406889 +14906000560605706290277708994666322924464492553328319946773040575977798237096 0 30655586764583465373533807687905848817 41987142730755214155804740123079349057 +12824988348354474011712382516848580859176550802860413112356145347186367310411 0 107012140032391673981459242914828833344 112323960843649957389481030379596393827 +9935847954449685981170526176140880704206535436601077173439190017455792782071 0 37442996668633717869873012240629035697 110490313728990491985598976801663857248 +12785749241202494501738050984326324608001023381083094077031481998023206568058 0 75549854930586988435711181294255219634 109220313132435895604828882043961708894 +20666433984200664062353472247386508058350438509666916379899695711462212492239 0 45553074725802470522548968883186102436 46321043830969058189411670939627139763 +8793017542054983908823811197652736637107731153854158847244943539325007911855 0 92538660564881186826627778308469151109 125683149066840178788131291152310291252 +19929337249901897942111039236646382156542011479965121721970228898824522887777 0 108250717044074524768613401354319287306 137072142818278608840473199617123720648 +1580031562098816032868558101172003691459703413324110354717864665588545339781 0 64326588158037804080129189270409849979 135509716361170070040603017231576422320 +839574832782191698429461305438355111729637033979277265764803056410407595362 0 128816396779218430381094819347777280443 109042221015343934739610002235196272550 +4087412384429105099491227987041782062231523926453500676106575370434580794862 0 85349686729609657133900857876620150902 35253210900642346043877986643588825306 +9973124371227685600995268032270502540891332762400339153900970878100069595647 0 81671674504894733053233508392405186645 50665774469609556613736692152151558821 +9693610230470685404216736447856083369077424710161815065305764800051604257383 0 11694044653777370614156040085655036467 37099033075172883976103066211099245134 +3886220820707234665903479205774217829874451564644488408820602663820964570971 0 79316680457388711703045175465108378659 85002721846907948078675660508695525621 +14469597391355726214297190546488077426506058003236986695329367748076651310108 0 95969175509735989090061369985231539123 18080359364058982508445993022962596673 +12939679408031052944580498916816350486234927120461693834722926593293871150563 0 57550492671548707348986468130746037000 113213217939411762761746526955506851407 +21241866645395427491346817793464573860977323866634431061802851172385815282238 0 145611768909307043007032391418300447835 70518339182647947399650908284081557818 +6315493099898441703307302098768554140188730274861882089405620783142847648305 0 102360585308250542346571618886122111143 58241108620166913400705703879703849578 +5754514125913896061857742817618353986584724400468903402335247557260682404047 0 125066471272725510876798488418674051477 38110406894569336934900872743233986113 +15330333491308628638661129858546870313460121586227401588858921756039488073622 0 46215960227879175461801574707637258902 93512174737600444447909088223543618656 +14999035400163714002086094607004441129232273605310199306467221119877484568627 0 128100634562503232924203851259776956210 64808694400527334821481849263312602306 +19771943101939502778025912030363854614169534104618846418853368414432527192196 0 140265798432424900024675200168177340125 53937062066127434581545080315600271611 +11885130506019208015297325203193792010076903779115325474110837251957428284014 0 30960191827503812999256770449210483282 132098168983648653721318531408295981239 +3806884399393882143841541269072612737493425538300648482476924853334315307117 0 124701288667317390307045090902834326191 30446923000239427648308095885180201662 +10683145618499511315617922290523660592342574460190275706066531455152533659487 0 27785764435924843971339065637862300510 26443299384671706430272864259101371646 +18530197834523019168618458434939562017808340898489943473782721786372837632635 0 69533927773508205538109545093560700342 106006501458347411965308054556829763207 +18548972506876899476885384884811629078186571465873886276859145613054978845895 0 42092893676513191785591474591185009826 30249223283427739307266555249046295078 +1523851250489688753488483296585707135364404860287618707249002114740720855867 0 55184210338934379198070391384654282082 106226544249863025093805664164330765338 +734757956918193343426505040487478050078986234519484232186278351884027146990 0 102966068617024001560249578429098865517 43688723478085181923975572769575935572 +4454185266098819476585682867141519043505695101095217293821973928639052842070 0 81107525500505611445751576122712610449 117849243635257175156674314011731763880 +6473906206570780048765450356879116529821914288846657105485467487888296552839 0 101172319957028699377773566719395418206 134415178660947758984317986403201085634 +11900906700761749779358350908991580499556496329480709675232632584042873002622 0 92294999153538717430330800915084521056 143483926530396471419119423452553226747 +14329185771167175055522370841099833577711535833356788404700243429379937325394 0 147900396565318049263300226761538788054 63744953255130759565933047301371547140 +2185327977093543132686873748672036071143041445256393417766401814518656861365 0 28222460343684218216670309570791316085 16747733567222889207757005554533171201 +1184859327706710449514913292841609622400596824947929329496684564892638777658 0 66000660255583102833919886034458305161 119179123630003079144698962506241288196 +5727640583527490782979684871903740387773986790341956245506980042300948942167 0 74405143013584651237736538928047252060 26203765927840743617631341401346330095 +3734032349113863673713613763900270070998891883782242015885074567187537943195 0 10591851913415250369028959961503167565 137149931237722302976824210216673419181 +19508336235327094305987183446624154263874782625152003627868440139310250358794 0 154001214060524748835872114712655763383 74209380512658461070471454162428282199 +10440429737927089304767676967991625651702782324736660001013040566028203057735 0 108316060767101946695054028084117912700 61141842218923630960361839275644784540 +4497311579743601977509626352049295866091674952431563547757784586412004878998 0 150458594739652341280648241841798944516 80377640126353157539450796803889140295 +978504059643053414932378581552920383804913395489614295116262736687066053136 0 112106171955124971853306040353656185944 139115104333654181861846975438402121084 +15366560097235544409806297714368147774464656337345168314959987723664917908894 0 148518647555868082991145037202594013900 133747239058819845554426348402511420029 +5522760104919822015614887203539638531585041328321792363158557576082385483589 0 35394460307238081330158867183155348660 79601702513437690426940314377268286736 +1185603028244230964172557100712016578062999865876737947371463890240776573285 0 54135416728149618571698101818509123078 106454984340304716444798020493343984178 +8381941035415033520556520621272135161235785660394326944671288407426289512533 0 100618798441030434109193364064687213696 102272326718348242778405859250969817086 +19494442500475328993823166483648814702609605203300613736524083672943010985280 0 96880795665302704403542247546911027523 51842898544989597790771306134136711083 +962895556018047279803701040081845153625401206028179018932069662876328565433 0 42138497310609919586534196432213059039 55686154919167513372738367452194016031 +1124293334183065065202006974647778467013161899763143909083931392870825173402 0 104385448131197758912922630882117309254 51701116313166605836867471061133376830 +15763646604832407784669110613325647933316281200982721706426353913740050505279 0 20271862110976611540238339938461337222 114678889164968245252938638726519312721 +19466564705355921903332155699278266719638469347385278563161233710963685096860 0 63446584583976575661444535832487338016 132054458428576899238632961459390242478 +973764599270189927998722357878579404834445713526812989713525633517625813575 0 70435311212222507730327987304333637587 1876557465875150608268655538860445478 +13378798809059193675920675192928779427273774200481292944988433526946232046081 0 149072999414756522347603059141196663198 79734171032216011967221887026046457512 +6603695700383247891239844892684741552543613438775346006562725423309119015133 0 98075004360091001147178683862248797876 45976667122595218776381603610875442714 +10260029475524127617368421015120176079207911399040788892241964808525694619549 0 65963287423193636254440192219813359170 26920654211915524224115812587161625828 +6198344802760175773424336343056191026463216566085694990669053970075022194566 0 81782324171456323058347724456233635811 80165726475845643117056298591116923426 +10123967398827019133633605090002820925670717793082073197216781474116035360552 0 74514116978614904006246697953847806368 62547440938061612231272612678235958561 +21688816483112299266245671776062206991280944506329175320835212446404881735402 0 130823715436777748734971719838949456231 13370178989857586512302143166892102007 +10318542867876342289198393542366278141456326056233143615502996375560373049034 0 19672310812961585603324675915385376496 97102295646479889730601925015687220277 +9286363218221986719559598544977274300918935473961893632992922153794558168606 0 130051240765672859985490283991556956016 9914038615879418880085701846316758524 +8976880441970629712473383873144544234659537040834024968579743808603123237039 0 94358847623176552122568259343966974190 95883050674024441118292270451444383994 +2076665297770961302177696826137396219216396213708180943379253971502284568451 0 106516644573268183892136193635892579582 13600472730058948527866472207484517020 +16466137276632558383884596226140188208505619227991550898610489672227763479508 0 160154228334063756692864461554421389631 75881527468537046537799040774233877874 +804929431084375199851593007896131159670376730285964127519389659288014397428 0 38415677580863953343450042084001135851 117925669474664017838194222560679336483 +9567608857402077778692333055551906628983106548907727415613137192286942346339 0 116991461605973932465165811861156702352 33420064478398820646711587676465496602 +10684235404709108826133294215167105868585195188716293353235782515120086858146 0 34635868191125700977579220159202892972 149112252682571163585551999210771802663 +13754265960823992176547172372605990421096157645710615236573101718878441613112 0 95592997283501312367756987761929293306 18770411210576687137975494784951328871 +11186960834186599126359397784040120228008249722740881264010620674482776594883 0 92796951638483263298909033462849290795 39427113829119745216600843694460230755 +4259296026828348697619140131199753230310101762554344721543847007079161511236 0 65674410329521643599445228471431454370 145485030805309114213784317692800466284 +16249237799205700675791786076922637818484378293869203885489721124689473941758 0 154649465929069556819826361098582253576 145547951295909810128157667346290285397 +12790263745339963022462417945758281013093719856119524331175229205841581970539 0 155326691935729700575248796413024743238 34171848230800799011002945924319360030 +3251999079911866740843148973092937819907688039292287615979786716188509513872 0 80280757227673592965532294607022952559 25333521994698239185034757832826901624 +8374143700857587808428487178524838555639594630733700818412287937303544171414 0 145839707557496375866607623250099977383 147606928493080315953087105918782231768 +11145362710226086605167620107990841898939285828945627644197467513402490437673 0 135576050181535791577752743563553693181 70697265041237873585127720752045599863 +11041505129753175142272086765933464695066902330115896175971032135904403634234 0 17141687924353859473468472422345415745 8694212482613241774644350518482491123 +13785632788686724665790183879628056544379639920585480059223851238091655113765 0 30067859800700424321835964274720789326 49834820444017816799880275631334782568 +768931432237687733158758904311676610997274210076664094569075464806675825954 0 23910748349083025453980850552633976060 73335285477975714453490611012505143724 +4427249150496580234035982679369858637636363496855329584684095754332250419916 0 40567595185277434484215868889473876498 44774888730020277002411103087857673728 +8390148250330919834073147131852277869413259823095778938405432232358942798308 0 101620334008794766901689173918794167820 63868766968520434376119079051075588195 +9686553105815436719602968033448863782550513044653723445290957194257198250331 0 24844662439431301338902205210435219764 48786069450699547917624227369907521160 +11382847672423102907779164635687703754059393767099141982795207309944805570807 0 92499415964984009358964996757630661699 144563854453528667082039270555972413426 +3189713683813137281426348407829897152675698450194801824980205783367492868912 0 78630173361414092861134106884921712214 138250445896283261190396186074809712399 +9354848312237063162827194647945740310410168741824771504585604403035919744387 0 26667689514074977611101767539237076689 14410142243496351938711347187837092441 +15477341479928050946367300387757529851577099496176195054167417091871307348618 0 50699702863653635521495775879808175934 151990131554628745961662797180210415037 +15699719364254499729835966883777630936607742989896515994308939376358053607895 0 113308535623432731185429426391078532456 35513867653353557372601983524629083091 +14500881784314689324404103267819446065147810646709277586943611727382056870523 0 92220690561849097018282719732597913502 13941916992099257786964809901408891494 +756145386381031436247269688860847733096588605504824392030348049109023216110 0 86774061318256441590471319007510011521 83546192375118753005684527706505599647 +21335992630262688519720730728557811205069924403825123846012236083382972861788 0 142255103473624538874747036424763882059 66305204780261451000882498935234138041 +1306199862822152320576933516044634210904529112093098930909055591761788578770 0 94010162021898360702687588565675966208 75467476852241662568179909663590363072 +6573108638091598848641141363998966193015499438000096462746054874933468224469 0 32508188230680185966713256852771514453 43157133402516809410300782788138848674 +6416060665244744854393444906808449999050052424373335009625781543264680790745 0 29013564836868014831123958127131937733 53281540647206714267446094083419164156 +13065916190787450825618707512090772936876788988676955876425765631552520862502 0 76802684218087167930706996479390965939 84903883424246790485151945469031147394 +12970911010889089847096837504368051614422054636404672637914725473645978034527 0 15452400555905161101127423665684004372 57406261531728001442409965866376230777 +14322156245967072388856363755878116912029024765055813937322475379075035005958 0 62909673158628372294896757171167137329 99183517464101582470546645672652998820 +17281548203637030389803060489586330847460043968529882068816624383644958856238 0 63363845328588507361348575208540535881 28201238999055993483706717137746540042 +5360005951041386222509443734484710290082557711343588360106289099461935536516 0 30226421184367649813177338940240943172 60306239166432145439346650819846101647 +7534419252324513842435669996378798081230148613176913951446706156377262379823 0 60087176698655296762613047243505486266 36372745445699382142625345204565818052 +9091939145689271950662409831456417065877907319862782389649064940794737752406 0 132195239271082065141728483865839700315 9352158214790314838863068089640263644 +18848680419893364580190219585622511450733518919340357654958600275312167630120 0 158845843171183053601045532380234473746 127530534835351348743359950916804272077 +2550577578550538657744500079930807247270116144927936885849547022879381377999 0 100191751829210904140251758839568852245 98668029195605373960320187525677954532 +17676282570569871070854240235561914293208769411767859459448234157950452154033 0 88139641429663102311178786589175715182 97971900261707132870107097410172345319 +16962173457881862877751550076333396644155597064945291279347941651695559248173 0 27022434400206402194355297708735296424 152138057382565133186153750703015405731 +3786738636074217446108932538357464104135432739303281204286382734249213045638 0 18090637375516417310369447756880837049 100864582724431580349674175154437838980 +11545778582843619355717161446460470409422921574404052966923083262381161142547 0 140039916096510797884357696868616358054 59097048408961812675347977667031230739 +5995693828446839121950122564344787936410700259224934217866494248432376373671 0 81074421639312698544065680909779362680 22556808215154643155371747596291519454 +19115740813986140094929462512255095215820517208941845956963292443630250796195 0 38166142718353662109249308884459017518 22787109372909583089301570776566341655 +12045767599746414168795305509491560105753173712602007447486084774182946700323 0 78946187544424613551285486649576426938 61043568396033944955277822021219250648 +9495423021974025589125349960088889580431911435816050628631425826727630554531 0 113920090810418034909110472935275830101 30198854887139752170750714567351752309 +10328552391271137556031606450121800801025719553492543537711993791610935416497 0 27096018493803556450978678413709617383 70806790169496293177745470779427860100 +10124666067511065731421127599973546827384419139541227084443682938796538270480 0 73684449432401652247828410096981429314 63337343531536382873440608213945993943 +10611049872696695074987073337158640177295145418978567737371843216285814496582 0 46270910643501308555143772046020775263 85669562976045013026278078228032609061 +4668602035929083216252888829168592789922867019178826682812258041071294919925 0 13538054666911197157738518611455326760 89474288711888239746778478177310053509 +3221737241344394413512064822299067770048179671327721253187748700285052495332 0 112465625803930115563284301749545965893 102298992973645248126224843879971822450 +17186181555102620978262091650637055085333765528553456998670790569492937488861 0 22171836269941374960390180206078002522 12465688836755459354196873179244293404 +3877927468111207247698871953223802965866164401026063552505168132658586574490 0 101392901752985040326138641435558971050 38933892379375964992043937816828008344 +7863286921870105923038452997716883267396098560637201758710985449147962157010 0 31851093003662845820759272165989275666 39777180516209540535367859610451653450 +14363293277307214096512000934281636904454475978369715563594294183185819426928 0 143999435904986519975835880631522426508 122194361584229007604591127929905509290 +16615526080907526046645420871816491821028050800678634028694422091219133224107 0 101254614198816255743687849511075485298 125793949885673571056041883544827260958 +16287002627399310789768872532379332355205871821510275714784506394447370744787 0 142092817313951391622838836976782309055 113817562465212438527232980539065749929 +8566459193285785830182394146260455483523441349028242285417151445725461987362 0 113124879711485084778938412989565323701 47846837107729540300656478710799417556 +14179861264707073818935425371616341735293801027166053219363302098798797355277 0 149972785567711222639226054341712489888 145831779904224582638982492054331384490 +7858883975902958675148870716910568849886715364454290356259511524278163189474 0 149645414587952498153666983099744782871 13704220157276187921141904061843023273 +21028434920930019314250990013010224034369858575113715215394759483397477543891 0 17736353177960572257554579838008616919 97357562102692107966178599988673910147 +906193653432036315753245927574540865920954283988963934159546782416863082865 0 90857130170382129381530938871410848855 54411118090952173603782033651807122263 +11322236879761388505467388358682207607475704953879667783720807918080323463775 0 148866480888457585474444745099253379217 79289955142435231856417045254549167273 +2696748392552638972027659097510490035154395794435139614245678412205641006672 0 81264501804863007369094527278031434091 140719590327917280063700592180641234279 +20609826601643680693328801909753213331543029751429303252847992804552787362085 0 152776939441703449604906230918713887819 155965842188535070769260239003503724927 +1982720913783165836160314339574286717353457454306059763998427584714307322955 0 43966181358377856689203475952886912069 124207127986741305529363145004497510009 +5392031147721428807643929573317163467652305519050877877215080894280560223427 0 112091826699220421992050769522568384014 135783703571666375601470712923979727807 +16725833802377681356082528071134022232406813463507276437187553474142036724654 0 104644182690813986114009126237087380633 43857220882369818512872402191282433186 +695739430316799332969061839721282755095970860406764783034590766917187605153 0 144503862203572312918167278071153175710 31051975597075560950473304669720671911 +20467918353159918244572984701002865268623148524754364351442883233722628224953 0 94281966408350095921695877757240886954 118202148746923224761685648778926007030 +15747740767128644440212475140584996848823465011649998888000918277706602830590 0 121761995346494626400416562686587443848 50357221544980018691176398813636627125 +12045186322678183649258724402625577694625759739897684242152924957112380702995 0 12522472922623294279226247692853521125 37758659290974022231359961533183865718 +7181677998379541110113346266580286253588457617905917699674647961359483131364 0 113766275437612007771243735081847503838 22189018265279060870805922591903392422 +8374982908233471399072547701363401140936099527188368838002187419486217354636 0 120934101065077076388974955641402801025 24645077515285808715820546762433565194 +7179966759137693601236078590349807385376343724629033003019517924068712019952 0 98866585605062428711679485828835724590 119958827607585450921421086648212878135 +9788818274029248810408342517051100109321768923462340510533075711627263692192 0 138895130425703616782248978796102644999 49806256650201810776837207507926019321 +17282502516072781325409008427177437782284905675814711657318433600005792397680 0 154311311301550744594359237782769875182 65903771044826236374296376070737756184 +16431785315377719435229789467317778922957202063749860707323840539451074976364 0 84852896814122437751732492001768747641 96562653679275486350144294779022133004 +10752361979161586977978388558566393475941517855901393412039222124144040299649 0 112247377328402898306587532762210779395 142978576629733618406064007572173653566 +3968854480843231230978978714806306078259809079303430406177006316284887654001 0 25992301910377991773544999352362590116 58642665950124639066725555487588516424 +13732457024095116083263193632796717801547562490657124256706090984027869788483 0 126959979203877896228870705538365168805 113915361963739769482880978422765304272 +10767403391416290190499028526999550035443785009767671378242682409785721575558 0 145532819739032463294438521489101496901 40947725338964426964917049103305640209 +2494795378364660205495546552618294681132965267103659357938727273697221190516 0 96663421525810862301626761846924659009 126705491863341646214008541449876843862 +3814202705405505857427122879108693990448790101742191236013037397932063920042 0 34419019637293564880148604726442570730 137378880805246888416516434011193858023 +10569360575458734358882715370170549980084696735957871206994916071301762159586 0 52382885353924441081853914308095387111 107991696241785166280846819988338609368 +14901830838638823359673086111656057680075776739865935437550064619608934378332 0 114260456529837908012359528572781045629 78599034570255601827213789876281400269 +1242640309589090889312737874098431223995413693320032235349830303256576845147 0 92626492824507117674059493543174558161 75713634495179325993126899304205395057 +18452849347797976290393914100655405888198213852514547760513410958495919459764 0 63725718978994335096810834200841512831 65491846800073682476304465293260003844 +16287943820938649272697148321099819570655892785765358723679103606625479478114 0 32543450429425550739213179307400893174 51773297189290466817351366634952925327 +20813845791724828915910420595290626071663687442948848137872121552559451558524 0 55396013949826414039762061876875875207 57748498938584237889058880746879987096 +3857424081266674152713453863414901377160887336865271763683931549970777447660 0 131247174801095970982972545350240091335 89034633873888702840264228580166067498 +3578996480426991332586299957518757559564501022047779374468899967412492724996 0 87376534324479770316342937712740259261 78901540959793320630203370877575527849 +1329792089774948708023134307530335168460697947482455748298880132582199484494 0 2242015800376193019012466434887850937 34565706390331212355711571925734823205 +20050930164905529460116959138227367858015013014531009394238243904841584480475 0 139872586194429828108124485687251456365 132912192152053043595163370600578071350 +14962593156635227156684759143872404451951812421785866179734543304709869847260 0 92013291300560542706943508739028115942 142576588794963832793094408621452675841 +4377112038350810816990070927005376186016222760743943339888013655875338774092 0 118666536106284601382983520270826992748 14511695119818688868944248479773622904 +7540971497295583090260078443111770560003909366355181253590663090387429376789 0 12531367043949731229768469747044257562 139265066278864904819055402301612045210 +932952018908963391032474122721813856538207130564426172438248824064379347636 0 117480402339006753147470024535505144040 51216051996296197850806470151684102974 +14358225542508714254456927000293790790967991515374823357521583571395776841728 0 66592065569083249123147814471753266194 11600003626152216111089080649462682500 +13844261426616648534706667836280335732364421458312863065806558418397686868236 0 156275943555675052122907281789332727398 9903759859488934242394755669776602789 +2602009587115085014427414764857203968238987983962564553699912628939300525137 0 59552437627322534115452327840348465868 102638623397771246433957239969645324300 +19098704706469628952501634490970415978708181159003429987920983536772844306979 0 104018370472111473581189680931867610274 35220775615257671657869105394694087816 +12476623088358514906391245306281053209462246923224098316795105846247377235377 0 129048773803102581738132300710949082596 141270601517063895908453282141491221860 +13755735286099926114351243417465161652424897978921212313271547058160501627115 0 72229448983796233731973622803550458011 51591104694367444746342290927590373461 +175047565604051867205440590848154654567575072919523864545207052228232067843 0 94052369104367893752965073445076549688 136156281327188277385680317907532570890 +946943762589005980222260667008706229381467777663413883527330481440658625377 0 78518981836867665897231578979366732882 67978625992711811096535235662141023070 +14614078777301068041276973865806323418130399089278364415596447161155172323687 0 133750803461841045516997669794261645563 90381355632719424197697095782398253094 +20218055410992406361546491625935848774967833826816131225182291496934001994789 0 66414483671879349424245480692880301521 24548524262045835124756128997105161322 +9129571262922031657880910235631445613224491346140320244566742727637817971709 0 125606114971928701141723694349928436783 151369989468028575032497858220706051600 +6168822752583422035206841188328064020987446675506356935740422789648121284397 0 121273504240892803986964638769826865917 15163167142101889983355493513455353819 +4748208169759658383303296193330831566473463414481649768673096984937351213287 0 47572104554386755664017410861546804942 62458257804680390673141945212488294177 +16034110507154434476276699478721943751365407884786888269619843235877677515936 0 138995712449129025444239148158279804760 55622014097793879671672766914812419838 +12032282239678454094067558924752476921816621736106923932269272755245456206793 0 63411100725502184138316756374913085465 22831412602592238929954825337211092704 +2578237973402496254194030718716902506691297517386123026391366105987867406795 0 36048602786360063143163634358628240729 85626040230573710459306219864694958849 +19313092822646031770036665905703833494827484546969284553376423960785711239131 0 155589758567920785790000683430766662350 105494186980383165988899208550096708779 +10386516165051896925607922227954841295392431682028789063153732813171549626291 0 113394060952065272380353167115004434263 53784352421133244239717699492733547483 +5329433303795331086799840904898400126593906595274248453445814508833355967355 0 27183392047065542613759176509065508737 77352462353106926685242874255443156471 +14537303012501231516515531772082064417192873686045399746495246481173239272194 0 82061017158179199815468131251611276806 37952138891693304387937828544858392030 +16181855215265429345469492604615520249423921644762891065466320068612115579914 0 63563179865773716465643178153031475564 47513775281653302950875409491560419049 +16927390215451690422887146118519550050333170813358336717288310717036814156785 0 160741430713730384992446099992437433868 105127177076222001219688858941100137659 +17431569216547272982209356934259387851827792186612702551786841574440519443758 0 73174636216094773882533745393591293473 91115713480424480955472681036214889271 +94222236574222229896648715862066253932052797289694486694653089364884127728 0 111335682481465707245904059824684533294 104755678002413318765819414988169885045 +17261845842205114164495882803885688652641598441650321057699465347961722542849 0 75926734923618153265116610638456706302 109082305895808098971688224092824499632 +13743717227465698384985262595260289041851388301995051967197745707995514603447 0 110604936613436379418854580622425040980 15935858656913577857757615363562116398 +4283168142282155688731933839887147640396829044935386044266283335093745193499 0 19358432646827806836843918173651861187 59230154299231573133157688015180568714 +13502081227538851782817745575581920991665901739217503226025613718141465255784 0 97676119155329733097340714852042911673 132513546015191570295402305916350587591 +7978651299477657589531097618189374587323178294918525791795788698398212618892 0 109845531896460998739428893108554151992 115735878459626902478757351190656162148 +10432989259020212061920850389288591543722105065886241722140574315336620213063 0 151345330439917421732275578155628743972 130061836495956931463265377631361666974 +15487998508745790315907408213265647172622516311941607149272766394152707715595 0 34747635367614981252679917810061693685 123313594562729293700687455821221766634 +20912123749755922506172243487374546585742112020179580363824451817103128383326 0 39468018031737871999795646593307149781 23510394200633607410375873661667297990 +15737479051623110886184779643793170278837513560612777584077774000724532681987 0 56145552661660772287665659113916050494 113665646557851424640580738392627314683 +7739427771092944137183589620322912296745308672547361830727978973507157040938 0 146052060320276905701734945446834551008 9708535123199042695355981069250450124 +3332155818144923716425006864362238696609802147748628250339125038404088343340 0 127577581126419191696913380371343185206 93659537552882762395505063315413999394 +7262198375885605412435047376447721346956458347096032548506305531446144507892 0 113005355863905731168386423457164867858 65743618782822733276233708845856343589 +15334643508342097750868298655209340849701778853514900042405237926103968975636 0 52502787718594727601679758535992067778 31347578297500227271437998597922541885 +1383634665416430750551898358420782567485250847533999250798065265478733142086 0 76989479324485844153734949426596762091 139076508287217768721218297924622667156 +17190942895117078389913518130609090572877043303431009426558478772585281331872 0 137275840934995308121269050664738539681 9520305870909540817601971921226349077 +18880577833446776349404837636804471622732647019849991759544365715397839288330 0 148823160109453749745922461464970753762 129544873806613333203525285278180865379 +20783740819607263465918963393738802971436840534823156986258351151313176027701 0 121394954508168709025250014096517244158 130728578326093187626098192433437602867 +13566663693299474754918266840914487848910689348639097441710977753956902069385 0 44352123071927231126646602054291124043 16750557507535973029310587882526204225 +10179565406080644010932889753272595447149870075450489933396905696992253283290 0 14386311677194070670946916383226021107 120835393857323413042790852554358373498 +13597007992295093863337837467174995574235901794371174877707830322964008878783 0 78949489494833771898024711000942107124 11795631133643714589445922440300349113 +20494482479637184021932316757564960428767617463916503465631023626293366699170 0 121201782865264535869607994907022858178 145434587845453293159049499132665368323 +8809451198621911935374909246082314038847449162831159757621011453929837256309 0 113345876352078229477501507968801863935 29243552907450337343271357238301615103 +5633114791604512368052266092685765350089870238498956281129978683053500270734 0 93869192166705261595473004800107876020 68122687991723807350184978277677863929 +14609491961619771792391062115697791926452845435844033137369885941967175751922 0 147075061497605219483446120034822923124 17242279455397742721514422285986160623 +3117058665735943896257347140339012327357386317539186069880079862787697306856 0 50607027043671763666851056380644297903 54024232440615119800766116567706456411 +2398898057644818779520204641614692524327589968068698143492222489820716545401 0 122630300383764611621652352849327635396 11629416494398872206891901934761683366 +15999364355906040736243276457645282983389371635055528458960936667712619848740 0 18602715347467971286115044151669172630 95052143904841222357374974588505777762 +19193066433326715712738299606649005834956038798186692726767697845374711923507 0 88037175849297141368890680833418925378 135443324968105616369755399871087130492 +15713111785558355526083338869564066441795359319960058742891063549609862908647 0 18719034353325412205551045645103282466 102956684996012790957769715161899668789 +3675003819329525560938729669837202776555463195547674246662780656686700823768 0 93855923621066353873509210174074556612 9188349634756614463021190743544980075 +4631409188981836709195644960134868871741789051725741925172551305713102932630 0 65305679104824727496452041370148998117 85927773921048153451377458846843305987 +2303093349719006844954724842169658306283569785314711016568715294791578031663 0 126512120387630928946898346944355662150 121374797576943566885955382890631541528 +1921429612741320483355895653053882474081168548779988178436652234791857226897 0 46752128281649872581087570248655867074 24600405075099309423217052599923383210 +19247180283626924708550984115167926715893297541393238824277558810775845011490 0 53969830703886382296098569909796560324 29886719892851469332337321244486738138 +21455844743513250600721206231975637441144657921917683497133066755381906576073 0 141317677799118497655287613723036853781 83892904472171312154498862934639027760 +19616507226215142979355744144670207153355428773450485502810594052641844296650 0 163269758770837213639218042474188949850 141854939823615851649497366902572001801 +14767489279144171355038509774788154681524978069104744675423532513554333414083 0 19884901812154281703152995233433099843 38853214452097313980420077670638279501 +2619737024127451909918400690119536273385745831932370620194854487839812428318 0 120876619724924653795213300420461151053 113969386358009630874926738812066430003 +10773503479432411696222447848140769798857795955352927109713921437690655963067 0 124175951457093499935864470967489786998 116728310890895136541266465040328859777 +1607285938290320804245061649588169082773442372476026458460873946970790189646 0 137010689957477508390438098659182554759 35861974138083674907979799324475497372 +18725064971259585419969592725920568956396216593592407597291735968580943671159 0 81800680202315887031080690401366979361 125742635102822943044399479581316827633 +7287131213074599027805894772673279897607965662807822035690623596250292871793 0 99500372346727821282124099076113844139 137811445163967929231872804261972050156 +18048411279259147201710597640512306635544996420842944921847732752985450702420 0 84464448005984819969423684271215278150 31859679167615194086434434479817405517 +7712684366262318391631468847344389640515395084403914096504052565277817789074 0 93622524305751778321982553192629567725 63240057865612481853259359347677692140 +14924490130519536368002362063340603186648025423011223476284575893127132508618 0 14880968408532448588515117801506277498 111698688771998702618726716349105985131 +17642553491802784770998877532452075463248594889377890094872699756135267171277 0 114829420591204249084593399969335801493 40168685985017927355365635611809692390 +12031616870347314629318338728106937231247436180364129740218144198178460240024 0 157713085830331743652218362839896565646 149008012702872258972969320552286440966 +16233006021562177163577020552847244690298346727842943187891576158258530061777 0 147615375769575555571149456602492887772 149975773983621740612420703613396293949 +14283733750321224442960859371603191333946737012713984236713058209450777218851 0 46294089866792694716200790345594497271 26887478793413603075330387366079252360 +21716218374639038467408320424999103935820120619960082969934608273951686389172 0 21038379018156566145490270041233743795 114608096730007406823525216783230153145 +9232892712846540939785586720559709306726202460078910557707727217915471045387 0 18078484475545187953679535522462044012 117698606931668916242646247284401920945 +14975272857566211999671590328494301348193136040586018598021367585364759778958 0 114025182759445398849849936531230479981 98842150048793827195282495997684938532 +16417465731950394245855013550562866342410000991655612002290516684419542835378 0 127084648177422738799322701861171329346 68979257507170908918754068396306478787 +760136144576822741470379994853567509187728475389740987713668025862770298873 0 135472089334245082999937317345117552119 34073165790252196828156549431788613083 +95047395989638692459029373663621417506904154138811084424057468095785773148 0 58450600358003271409591034427183635777 41040211119481338551308085071220696272 +14012698293380562241213138965291541704345661401294642745102580514749032500865 0 127208040916926676261436884787872387012 139732546699999600215525649749562191581 +2767477570139605282247511905027424160587516943951671887521188726791697476193 0 52599428192146791447287780619256693273 42823945622333519614708058895122340786 +2206183579060822772939485457359326187398643500777101451041546611632003083517 0 64789843434164254943464539817961545453 58120673153282339982644102991956397912 +11246942289785531886936686515514363917003895331527293737217218221988860333165 0 69983477152691420311846145037237553017 56940584546538405417992065057649926375 +15663456545511047595954689108405546334631586477094453939381073731331233655828 0 157408456330176850797776484112009215101 94119307369376889847835484015891326343 +11303545237783463234177576705345532690401171237332152146952779423271083506608 0 111458136258518345516784379018384244785 151957240153667362304961618271762897096 +6379314440133417349888413656543584723693075159127730737860158057614690388366 0 109870490375177536927080156363359739838 71205998442237569743470277527675877018 +14887603261388853852348848926190100186384194373983784177666081616022424407972 0 31704868865913231731696222869862404745 143992263897232358593430316676738158857 +574535712669861253314999954218993783723235095838829451052466089888985503892 0 64016410725328752638263778390559762655 99891644577262283252600715823099800218 +1697833316827616811743211477818214783575384779807600332653122323252224406529 0 123772127955647412331454454845851796141 69220466158949185560068656886953687167 +5874258862613952965406990383279701414253554200561899915931442971454543605710 0 138698379771989265512178641321429146126 113137704112141670613249971564645126507 +15217783094363468305765514142278933312162396941189667223535360854132094731852 0 126673529925586366900768062398221990744 60523484586745046804841607125088822022 +3280719579163787883144484442005653078651483341421475675295298379310174163332 0 131374581015794430755677704864527750452 33640350034250337719810756472862552711 +7557540119713944248357041292668245075918070375480819372957790396352420100764 0 6975281083703600663016866156557716928 136605444772520735814301168764716431703 +519062686134438967281493335150002014662561562168030528863902382257385307397 0 43623228405140323962081012818835996531 112695829708710741718182559959135429467 +21381776856575523277969432429872000197373707331940938442925959530051717652638 0 23914824576700170967816199845204756330 97940820725031359423236280633171237104 +9809488192616862055406736515276574803069239497748060764284687345257608553623 0 81961000652292697849175914756989968374 13567112847083514280395001862575195945 +16334082264222786426266443998940400159561319371444008147287086504766636008299 0 41559352839907521937447864994809959634 86608121681461789756086166426990040116 +2313712300746008372971757520203374612978567917995617352654453644700806350049 0 131099973288589194416838371306115476240 123518088753181479019798354211667596800 +3110920538794684173034372445530530101059789843873289866553580193056278058755 0 135223880958496022452935261544916680240 25515320347893962039295128742334315052 +5472811852746125176811506279917170399025931474599887498036277822970417833446 0 130120984182005832806372156552008570095 68838627797271005069340964764063824062 +20130222783043462392447546923371880940183301424791856871298122207055723203385 0 81609824609063338534014403375917913327 62276676287306500941995060361098786149 +10174700064534775570635757986145067875560261369620388674077832439044470141841 0 89646439449114727124217496120293827797 129132950395382682111662943028937810981 +9456249563933608470241929231174866736527317168393699246070011020784058920361 0 56700088802021021801407042260815411004 74027073445852126561657868320436591241 +5020634526076495357709120036876983137300172837294060269115320493532659523863 0 13650806895201559419807081489721318938 48421342613803388825949686171067555773 +6355670664648695229755588893358649914817234661711380296045988030227099856652 0 9904288781499710289878501071579086379 28733889794115785646316794528905192929 +14813635541433070767571363969951860171316792668277265933243959465158977099105 0 71933449891201766371215023367514658093 143962668026664068344623679300930357424 +9847558760541612681079210369357852429994331824689401207881057604550971995889 0 55510844728808675716120586480989554538 147648687883994664704567359548065531500 +6215381633781677172309053301322469258058850209010108387608025285733807369416 0 79804904674186709479203072293997668595 57720891335655367225829930883143589386 +13910520114109148801041157739728865743926733972177287749919097280222013727448 0 46675549097347845739090747728829790912 42136066823763498421154169456068454220 +15793757844787881222685587097909465097792699967620165627730292425555215645029 0 73170366143012063644573830248183932873 106365247294753297047383030366599824014 +12638724722422037695531299054674825253952005319518976874068552896177905584696 0 122450236419039927056627522305382264043 145911392070154925224975278774841915754 +12053170666768931800841375695665603918454401892485807443845427993689008281082 0 102211302887713314359029646917858586405 138568589794960528776144854435566924612 +9141939185102999400547144603193449076822235610503826592222955785422425238947 0 131784288739314219533864451216860716145 122115850794110967818777553846505789042 +17565468821175577393360314205710495583062328080779794243295195647002073280133 0 140072839207810720795347568719117980256 126958302084974960262100420102305402044 +10485117008719717199986648049344092243464713904679175749803014550725169915266 0 115889044790524021991329353585278451745 122652016571369081695533003887163700716 +13187224576351437816328434812142180103381823614112387881828657675019048293891 0 107603170681509619247463016565838587076 33405879491909532220253585655587805303 +3452918176796836097087345349946007454427860851281699198723878829671734354785 0 42771119057994474106326051495888829013 130603877220822002893249712790964449743 +19630671363083492898191918538506511510314793855448412059687511710544454054052 0 70555995738650455521272199483559081139 42093719805505507775795080334138112436 +6297424179694732782960236239010721107176015919018520174922779153404174083206 0 109936255979801612816362415467967835935 40189592657289756303846420069537629018 +16916688441734111643324260351377633854901228354848306499798542071292637916688 0 85759347352506000977405139213304143718 137747231823657012055512527289649667010 +12491469575415615500328138260422762187297878769073329500612970297121334532486 0 27132685058149624844255862684241183332 139015608745923688856327167545040935670 +17050991179977246384028208040477013691089607378489609856754362829639905970207 0 154674124376680819563582764635462658446 87286671992621291884713055560848625500 +18107728879077347734284347235549137698100229763847809609027734726979032940018 0 117855077522045823244807254782208007651 75600762109112305034013038957179652582 +18281468059820834050165442337023857078056275135388722705405239715510103384167 0 73326381033720330477022693853875178493 125586633655337173463140232056217215964 +9607554058641610243186579164752090747492159075917300358556518591842091196265 0 148820774984666101949686656554742900238 34902928932385890267194108209257280027 +11768494077393822963124239604713140894855400172927471906411582857904174758092 0 62654510722712755292575847539664388957 132329888419531731175960834449016721152 +13114692655366232616769018450046764566670215671260255274189811045230550349520 0 116961045784294012257912750523114190682 64470497795552974232053150154000899845 +1508165641316674030986998746910657444633298507421469057212452576080889085216 0 85476347280104446499512313309337602415 139026143535963820810739584198809405252 +20335539947458225880177683375277578455485585643843099414432043674782068409701 0 103821470575139634963905129476281968001 70016309721023088000319215732780443662 +17234255739004456281886009223908545426058159706463080407150287986139693880873 0 19015386742995532892888273587091178387 36081581253956559550474675191120967623 +1680995620338523372413340225186103672303218245771433283444119769717957555211 0 63618265676099220233586656226261869829 133161437278467348547250161932171926127 +9446024589866666797561784098026043643676851364476586835295419515198289455974 0 71401030347272021866345119374616698162 23981680396676724694840251193454074874 +11560674309106806598671481713036822424158194656284153072266823933182318460821 0 47884671152119885617913212069185352493 121034284615437087224064184729084985802 +18466024259838476694726843200494125107483559202364958861845549018476463684414 0 100406182164437271647984276729239386064 39362226595813884488773156738653173451 +4706557191904468735252244603343564699525999230193185794129304704423420363057 0 85578476035883062920615196936395642998 29685071809921600105792885140850094940 +1377099653243821910978614033340710503812606571003355876022763699505088284206 0 65020972603254490876059059249644889632 33349207792881784743835624985145844553 +10719984690633042357845477478408353398073901319742703636827855197466186736191 0 64151484044475652316073705336039944999 123884019661469336822894018479703831706 +17825817220856468085757517486928102167675350376221289960516112465413667035502 0 73543658370488518830778899425898594017 95634559672210977451169988141491058363 +4713722054924156168802365119994164863046046222406872897506526029657952639248 0 95884610057407992036137511736870200915 100933021398028013951689243121520154743 +1351390495304477727306945366108082546454448534825289884410436716107859958690 0 135168988849714289971916757537595594314 46552365594235303573313824986592560928 +18742019834019757564182555998663715905458092049209100571348613671448221808026 0 71001620068909415622465415267207072556 27374165185639148629987754019595843092 +1711919230023037148965391808020614099576932525729661746333845968160423103076 0 98321154207213869109456246826964231753 63910303053523921927661666272679021923 +11494200469534503240589725552802719903436083377952490182640864666976761018126 0 87602356977203675231238721343794294093 89395000360755254767932640092776502996 +12112041980827429439186874511409842603836019519259698234644383600004772218715 0 45826936807017636783837690811360074054 49129360275820285880392713432887953849 +9608844321516261599276766330028352541561549321205878218946598502913058005333 0 108440904820164932193845410489576253437 106530034826724340813774055358811978756 +6436448019706174884865996994329961956887923025885620906125673717574167826039 0 117996412965904792696070700739011608437 64817308498377149870418421361883498295 +12444673024064890052304529013899405975026498424534226111897310983616869471897 0 70089713249530250448047989655563658531 99385138487882605238750761535782003305 +220266200692322108117823631367234292096148432065782092922754279660824865490 0 42053003665516676722763162528031918327 53211918728436633411432307688735549680 +3022286362640826312307493622552007563414666124817699700404184201094350103340 0 13704663719653236507162180484610363235 92294754022904149570017880275089753689 +6630154772945129712084629972943943907483588063687611117063801371793744560305 0 129929179260478422110640261546990608292 80198918605320552846901781461065178386 +675422973641915333657904478534788399841232556489858805541397237825070599733 0 62557641937105312736842231653987522338 119772938887313402410501334267309940902 +1407977205010318346895561499860425758051686756673350926488716048638803671255 0 133562250023576694488224985246122084645 70604807184037854049173392198081814564 +5889129340030748053869932405212357922118804052425342563295622289697196967090 0 93203196797605551187874916991441614106 62888819470843945346334648104966716484 +7284234128647237550911991460793802403130167358137662209111430090407182200608 0 56252065774192277914349382816533551794 62484597648471265899180636335872114393 +7144308047260501836338644733409117101753634467192217913319400075590310818780 0 39638631491123742532692674100251589270 115949403547952842256889284338354809077 +4807703040255999510204786777958664804559900827966843078413760628523726409097 0 120840525094150253930180572861511540993 43415800015441705472454954609453368686 +20701193029212791647070045251735207258996330947499809705861170279312766645456 0 107036892008449568811856443789399315181 70217335662881308468866742842745849200 +19025762296572972895540877149775769279289564568067354084468884667929662526506 0 160096229211273299744587527005912187806 46153401618976149456059609849194435813 +18444396151134945499022918958828991544665153071533769360756405363501647881417 0 26480600961338078718429444373806749616 103535914048337677460229167499909534036 +5894719217427695375362266633867398614287414933348049883107243319389090325252 0 30508622824196311963090466274893039939 15909154113483941334939088221776409917 +9118863371133207971705756436862883548236114878004771595988372212210282138956 0 30464807557752197231292134587101754874 7905945572735906880438758392014027852 +6300150942279902331305267239194027677093130647125154877781467663977211983334 0 18350261708259782088462003029845793245 13236719843736815233197079309899340077 +14696883030397160965038303556876606596653650337991934116062322481082523246267 0 33489405467060888126884328990677921952 78493363165667997660690811619385454705 +3474542525951462238181103120294905243438703265292704960213407268988430018056 0 42685638737834131893436429207880024609 142384693915701556145894516625141069064 +17979208697961452478584411310236475424596137362101191950632394617475232093890 0 123407692367508278642079923969967650501 142012200902814420167014602128115967220 +14194559589863971922171781590964380489153374887425076588625816455434433494867 0 49390454939674048641217301320555990643 81608012418550644953617220296500984895 +9322343661403402958141455470624565632804316352194030837355246411833622250553 0 141823740222534063090259748551406634073 89388136956672272496050544655923281763 +8007454490797321177383721197776403841804165973644152912236000900010646514383 0 150650034352630219726318796943380404938 39625483121933814010772034954974371287 +18130650818761181265813555857950542845726583355312936402063148855337533764639 0 142402954905707056236329483745475158761 50806830178344610117303101479792697777 +835976905959853126060544202332928346119690165543531807580461126726711503920 0 124846987667904841689310701803244627972 5022744123070697789217111040682998601 +12268863586969810035142227314726974582602747671233007040986643004587151533029 0 51194782214572904817071814360656382342 85393044812990354995579690309925614139 +5397107722884830124513598996862397810196593139438821297834857338865093059932 0 21061707629537500703929822354679463294 67414128427118266862835784843773755943 +17189919703711942195283108866037956137625421882614519281016986768715747114353 0 99217619281340276217391392902605381126 39344543290956699063182690072593321438 +7603030506127310814333010332526140291318062930514011610020891628388496722445 0 109718588180787186854010465298235097969 131554978876841696531603137604631902309 +15446126815546733142629533695127240738926107471109964855141715848160470784135 0 21933940825608434745124748143932967433 136645333736053804674257455909673257398 +16700138544662966039087423601705478620370454674015771516375459632740690629944 0 104413141890923743561842363505723231887 51951972443508072021256618047389579532 +7082673320251295153700074738537291904484077497739235956989199992733944336872 0 57556401886775930374383681912955098877 111823969871038849117443504862296689053 +10080157544731033909173739671631924171283024989362813911015770855246674040199 0 83467168364350564124690024003891524965 37150509520335807786853289124548680674 +14393642747532446047142417168703806296753852689005526533085326189678087097862 0 38582608710803650831223911734200988225 110235081145154811688214510937031962638 +12427385200632149419188420971647807927969205840888020571807510439926679921769 0 75088160114573292574943183688851810163 71141002773683767722386585059255435876 +15949411836050187414591821952597349193457732754943311260318171621509312314464 0 86449897940710649666554257813365078449 57303595208712960428855630737727377121 +2835740943669527254215575234145772175141043157159173737925835347643175830498 0 125835275607144446261102565146776690861 86171058696666885941103343509805043147 +21014586382350475768270864212669663700500660311772852885401341723340967421869 0 153387610107980678400524968563086338073 75961584939036667398572776529727168723 +3046085087244008155860504609584749833692798820509562358196611162921621576980 0 71092160459367907900570537339838040350 94208085295256201445488988439986007175 +14659215107468791425694356239659775610781337432617037909911069779708077160624 0 23207152090342641387320199991110954590 121304816575251785718638192418308918369 +7114328177422545372950171056101012101926755029630478658631059740372646104129 0 51103957643736197160947563521630898607 45624477892336038867203581565667622982 +8131054382393798372050299389611388298474785452945527454441658012493679356275 0 87769828685418239190684633338845337193 19559202259374517781925982128979635867 +8014913052629565670847801343718568673333745095623457866972282338984467097249 0 50594236402965908720439806600265725783 84803724430704448258498904072378711092 +1532233701953291047460583047027728035231969971563063186175353965776686670645 0 39568027270323537987688873057376472509 21261090318828428222243479985908993860 +16613246479056439200240767711900930418667419165646080632029998980974592095500 0 148414357274394632628708675039392292130 82310027191618895066897682272611137428 +19731118012079267420355925574630272396275190742265922611825136284774763355531 0 140397361111251799367087183370487547304 101780718947322683542608685447859793611 +7480419524059407122821825652591420950423091990780147665476220935567284739070 0 62198750972754182526570963452509717305 110026898954416225213915490573792057061 +17132595578044522502875197873590327996588780148758753584611033834336014399743 0 22220605900661247016232099306213180940 142078586923732370788104939703675868171 +20892083205614989523933848215778366999481384478204665482604419677899906803448 0 35795700456693681186999432012289679089 76630892482435460962803905552689323896 +9259794233046854120007746399553121255001312349141018379932187091544609969679 0 82639801301736637594025448359421318738 70527343681341766759184192344899384785 +3849014457017998940904221620756625477982905520421576450031869064392338390349 0 10546371464577627013258958249169379656 5463577309338164553659877397657789447 +20630046893838811998228830024974589473075121048003492687888620891038465443132 0 124700146523212302465034169181536710446 46868955249138160650163974618277385000 +10846220232795489927422083447846816544690973345585374920481680895568708219304 0 17848930127738012458934756071047532744 45677358949268882924428775376677021178 +12918715329330270630307614475278080889485300121121111700320332678511264951785 0 123946140105155763921926795946168348595 132976370314489418408190577146513062061 +8038170953568702466517610056903368177593505764542877507640609656043046837550 0 39477174990461422169369461839016361940 41881274948261351134785743410835735731 +10162202479916907613432795247933983608624492065094761957036100121262459931836 0 98649359020695808542016096648552337072 130478147918315265134397386504477928231 +6676942927666365225785774472698205489916427788785139701920975553415416198786 0 84898791394754352652615069349505666159 111700163916758638627537021624591746550 +16025763464871994358348152524446578035725859571787285346193048578187361780554 0 81804652376350937457609383366937857994 20259867444355778587487147794333255379 +5779921734936210496289244605054038340015059592258350563042139557427523001292 0 47717397796833494966929189539419624595 111154120777448085257387409222521570811 +21671499397079085744453212136569955933730751595443009180479606310189065128605 0 115597876916700963685350361655657550962 74304628189372000319833736512891965767 +14621993402515164647827247521242310162619471446251855253344921335708662576409 0 70899565327853160835406084497471567622 124019784769874794878226284593248844991 +14212457990591867062512025840731524051593699677372043979367592419330158508851 0 23139407453152133340431188139689448082 113094484448661224145726651877290671686 +1928725727055270127843452073201653499838343772609063545339271712058346720790 0 96452730911079101118282713407474789731 78714913733359526381757272637733001725 +9775470669634334221628734160176590848311711320976245320055968243440367985367 0 89337104573057107301035613698055736246 14118465341542862199805444557972934313 +3592285698188909954678073895788831050391036270757299838606563030220376609032 0 94533234910338449644287746070468945629 46778318548491466395277024428902779410 +16453722887546103295331775033485560968162743938198749021570792844012482181002 0 113480990516291762445028582440343260141 19034744884027386817771044773400416672 +18376699678848851246641731212078742829766290945884220850123603091217280287748 0 46725243124549180275875218813808407600 77289252626263277857338390220108835688 +20596671779757094963256433093897525924370903616770757609793365575713421618358 0 76515504893233915912323175802362637041 158451397512634796629461346338449495410 +17304266831834744954943487697875327656793049703007111927951072437641403347646 0 127656370671921569657032039395195188256 94577708557295345556048323425481643177 +13175886236495284699133969382828850951475591084680239899948884674234935739672 0 19718348632729300124428719225934261378 34591360793544838899277613376057980051 +12797909950765129320184945362836090137355941973700661323360437653051512394390 0 153420043631698302906309658600459318877 47179818498395290012029349023487734310 +19027824686229374811681293312092863039880308413530199306196094224189307524081 0 74697553459032053406972502231969409446 130278818275193973255768884784232068870 +12617616236658346098390790966390388001769101364522234897554024800491459107968 0 135331884759860327266759290333649835655 137782054289640448100832272962757168368 +258706760639005569927988164668966418110305635495997289542675336757546649569 0 41677076318085292352985836978447392381 60059100249355965079296732790355453282 +17093823353373158381138659984010512794675261419572540875378036196917282672094 0 69087434841844857270469906352910864882 16263057546578689888364150674120629247 +13709196197159228531870438468134889204564675450795457317989994134038451149060 0 83057298336494936239503587225587648614 132053380491293947358765035244661107416 +15940282031616826808117640180436407626476057990972932493000944703887192086519 0 35261003720711229107008864639055351548 40575401748028429568990267836137685409 +5346879859459768425911494636474838654258196846008776528433696344293281539331 0 78869414305833040249144434298618048687 26566938023567895084226851988398482448 +13548073043884110465725367291916289359736755311669911700584362427483679947539 0 156244599340467128781259119517038781819 152500682626794171221697417040437849908 +20680775381985770934768786980619546003887976417494777506933307308576302395895 0 54936126807958012697487789020607360845 113598700303788647536390686625376740336 +2599168808719260672646526832663686806019286493935561475659968268858960902304 0 112944904302205314273326793099323193000 81408913735023820180232689147778638739 +12271664880684840329172723776541130288933805075558338016820106049148895698100 0 50230633531507771706557317373984392577 34791238626993588141994530498828286553 +295948218187060281127736819109082492039955280108704058919712607539198081717 0 112342405098078761337236026624901017273 111243956791005467154831334953335069399 +634069668304273974023223063675226392518109714547219262898602761641797979521 0 36309771136444623803207547318160323961 1737316873290664876757653202908135566 +147596843564016447746013438644535851908019770244356027247841082998384131729 0 44012602339403293302042109858686666019 96552737622575502143690555095486606839 +21309213377931755198056710757455131902310095812839483396776686318382056268503 0 142898671114470931764547387164767628646 79299459214241818444442569070560838530 +20853315180269971144442150203759035166803985781617340573420956068695922770157 0 17976083035053011924329901239018301863 157082085946608088485870372359360666491 +4424333419834519218868674868191015285766560259164690660125544525826711451600 0 105786304656770749847945634322136717733 107507600824860893323732496148222415781 +8779196800748484000354814074798263955619801408202816114206260184814496596961 0 124521704504077099484240631988971617578 40072210926411412083689487766975445312 +21075284575688276184227301818122502414432953969075830105489866813734889833868 0 120284211711410019819903754864711169054 78985415312584812509724794371405796171 +9531739190558681867221877392547295734903751870014671194511437892468378726689 0 62357457353425538341805428239294293711 31371059906253257131625263209128505290 +21598713505150209851470711524207097830377089865202016871112590306759206828142 0 60406111817269036239288191258984307762 23115675620052992411530399403597696908 +20305625909368861239343532037774309151312353463806575117150780825287538754167 0 25248011187717839749558518234754995201 32286924639476452091482225186647752113 +2799773693334800735614111979666744302301032075576672690244748407205285700737 0 97373888578365405678477201304689485457 87352864454418005421581125615678826789 +16036094052118321388638463969302792242973756536880122274841991207823773499820 0 81795854561161248407399400170640927738 52854472032778488234495911623657814335 +5980961237254902151080629214697544602963775820268170190953179767638481226034 0 93258397306715150145582516419844171742 8771402525799169914560742087047660715 +18671361586951473084771934079655568954328312548710261760692976496048627684710 0 119519293610238455738193507439318993407 19924972346006504605892438098997630559 +1114168463884004060750819519300098385606376882090473823311466136113150545834 0 62452474146148448434054418782688616290 37867332317586374169682050635525381268 +10106820198315400342445047139925470276832852876851615959005960981887810296262 0 27841131290037767159619602465512320205 150972954761757276582780738574777734335 +4031202387235476662277801988203446006789684902795119688697402619268118041703 0 121561464787027065855503554526092689528 81888357570195005752501485815760880849 +6003942783194355963283121458793820934409702542415166301524541716847043493603 0 61460793551466614063072278457042881137 57256886687430909639082705935692341178 +7713957900172956332176187710363823595160229178132605941191239526574165996193 0 76841291760096725983824196928661658792 128174364368084035095113456581073760338 +21524163590026792167707029120670355073886492060566080939157049743015463215145 0 109852625104500085805530685676003453254 27999482486039972358386033980298818101 +345857085946515896179564631986380141645848589163789963748176600626162485332 0 114252132150461901476805978371173276259 81430193072532772336418044082854883938 +11857625190806555645621858048661223745040806734230627409973302635457145105594 0 69276778055107518475756930748891009615 23946348625942809083975322767489513461 +3008871644317279159620319682565524988559458060956845081130582603810958772456 0 100250165526714859913010885715168811070 38582494267930764840635828438819399859 +3803412693348537265237788712493638219822082379892214663774191342678345854891 0 85656824888014913002631636740674914221 101491310295974032481314306380628400423 +1781836509900469931528186338366918395573840203249175715365679181384367798582 0 33206328260784071811155884999116227645 71547085825883854751646189940851203914 +10704098103625743439708063922759962001254858762278458332504759930223909797350 0 95526271008241115675357887039412439598 126185945568693621663041025033586018651 +5200557787747556003611233354660219539386551333899056684702787359983649515809 0 7101692807653458238150068699170096035 29115632543664211848242312704855854999 +6931768937413903064579254887417001750604128138200456212179002220298928673145 0 67439271503943370751385954446687708004 146716177759194240250163521588796625318 +4216331658952478451775832376017850055994421699152904212430627945111883072766 0 125426489397679591051408186459466992606 49786687905111633568315803738451433654 +8608632406353026658001319514256650745148253402659443513287347238100891695029 0 109167335095764476546026066481950751799 57472249480003378344476665782097387955 +1617647658980019309521230075511533091985727735504963376712064903587047669674 0 6674451037182124862460807800180216645 102010033064025231487296933923643693231 +16765827497215823029988451463295755243276021605450235168855380055822659927692 0 87990033034289610989137588815653976775 117782659508886154496014447106627223232 +6532988517610508108866463848571269572605646962130005208751367451697067224118 0 41021343193908907370824554250979224938 124765510060055392201125999545993233585 +17462756529974897064287333487700926760537058883146158710519913124277322360954 0 39980178905823617258373424093078975417 53012838761846504196476156941787463821 +17719495534002861884418866229455436834527442208339899240124177907723768882899 0 132267372642237117102682683194174483879 45383727299555445368004879015342072490 +14240423849856966253768155333153182762770087297366044363328260204421712946266 0 23708301375116868732996694096839637267 108634813063182282733225048095412904378 +2132570958248122396061738332855991563524378683934704617064753708179947104517 0 28439397841525752338968800277017513885 109898753443952120653479891279485082415 +12951033643596383054832037862029802858203289865461352594666724863595520040060 0 123589325972002486781593622004150176729 9018816504503540973185771305950843295 +4594600987298933763878241965476078442317747960384081993565465893800110560313 0 97130189868373557143129679997631727142 72607712133403386413805016085256431457 +17470655166276153345248774331870913244945367141976738112861832223187568052630 0 109168089129878057607808238973310297040 30520485036632511427974694296975287196 +18337513496160522475942392486138065828928082616973038606903755778159386350974 0 105312487278682078691601878822658927644 20042112451720148536126405327686653358 +16136364663261153836512906569448959507919137908420176066328985679274959630288 0 22679241977437229461624129661641131413 137784173636077969287225905874481423127 +8741670427217687028800705519952484108149992084954391035438326756423392064852 0 98202417575325805443801103963299793245 119166011295673723813804047682313545523 +15071746023244268559039400621930742406449043062528698752373204967299405233919 0 40619000107008352320404593513025891114 11215645168680265064856772553124109565 +5069146700951594526745506124023229988512127387611471454074338119464402311011 0 110464362669231731144044490202039242975 96081276304692915160732135822623746084 +13294034946050463509966536153942653006069504690725988626890961679520979227373 0 136785909230744250870144435189012235866 78276883239018945918625337857429976544 +5566635941245172803405474484776835182694211146335135150257637343191133122579 0 19054600890903436372516268215134897964 81759027125083019491524684411130229536 +15693772801264440460767710816057193795793128850555704976972229991109657401953 0 87777292835589168999061553289079044410 44980968876527331248349272696659351425 +21063661589192226801202951394111315413362170610990736694951877158350724907353 0 133126676753427570765234717823728500739 105800792770228489327204477180931698521 +2233990017712947407152417047583587663476119194372413829939753931097817781385 0 42004698062614968619580051757442715944 55140805501225482885623069339042074531 +14212464712233633711334019901246826701578253404641385446127856573436553880786 0 55201808631173966921703680130135570386 37912966742012418060446472384283765132 +15390541273061290766553076251178263128335331411902827962897029523705676699507 0 32922008580238212568153343419253004418 61467307641350922208661574256310001620 +17837816550645567382447074199323013145620765942753182908383002956990473978319 0 121794411796167952651890904166401033034 89944069851161579236877952047717405615 +4083363149048348832016006470798816073043903871240660041245882110788840013763 0 53190751021370383600316144970061332266 2808819327788086474534143484139808790 +16665816316012041140571882364343877323034079614878061643476944263038070203434 0 21181495252977961841496234426779159460 150945207764257922500314717325813585842 +10083869350481777218687182394325560364938908542399659049303580038296177595427 0 18639968650523909253109395694484555357 130924088028513566014453062678968929432 +13916174145116485920456663721617636398515384835012715328703014814810609851345 0 120913231717300350818439022237103575019 108808708036641120060535641682927361422 +14942871389326112760348428263055676638107111264264402105499238442546681766569 0 145206861006963153652046221870765867244 151248662752759399460410979873337488943 +380113478232642041937470742991042039646083981780051316685418902351333854383 0 79629344363118485804644268501472194608 108109912491979197152023896167324962580 +5780669412886752464734937451428003004999637162856849524987296596586986885408 0 13977136616561082578081206192929149477 49047571552756410220756306333439974738 +21711717020163685915133071208415085499213254920947916054988726310347098262662 0 72976774125200939542568129155096042577 146070365792838152252887917284823072367 +2940630050184667332455014628251036973117457544674960098697196324220364028836 0 122553395863248473268546471611674496257 142108233631576882478353539382496776172 +4521615306840563706089158061545680724954897802419985509927123296804915474769 0 142347595918088977303219768845246568138 10290819705192903775472470469665341532 +20295441997658248244684751171702558602415535947690165689600124600376578554611 0 109784357927781767651861235848790270675 109079926375509912639373679366313443460 +1760549753968384198613410213794075644991929435525702222519319451663936225432 0 2594531942664149030226699132887176727 95170055098953593227841950010278827988 +9128671310626545057287829076703099550849081432876267835180217379782679086921 0 151871093447508185713087912422669840748 14304387241835522591623143837524017195 +21256143045461260785197439292067063953815273368724479573229796187142434659796 0 55644460289208343132026422932395199337 43957432948637467809974772801061576520 +9859307271449737255254842884646555094217119153333256424192556730146914205033 0 106222401563837776383883396018278972576 146650255050051432717818090881283932795 +20361075686290111282247298588076248120206568694799566554337420243727392272786 0 48477817121022478722439109394217284600 127572818919276996897453822860382574149 +20197863205072351957667940718913166012532125666005871325286157711093925224085 0 40407000671871238079206075897002219365 66137836735134378048226673080297085761 +6074046674236633041802727129892204363626509705236938283876075775247677969795 0 5249538226959925526067512787588198122 63202663060581646885832696587310857314 +3311184049411824383134318078041704899425649200423096524201475721981824547949 0 38869540981087429193510804305123172911 76386596681559311549836375275813094648 +1030365272598726380845908575841440773645447526793659746668567980496213507182 0 29960915262529593435704390121124070398 50632049706337632499067769112477557946 +12823162434502753575741074761888240204461169036592372806099999577508956788191 0 119246472734123923545725111460968100061 36653323247766599877102754304620637529 +20046785754167043301098552126254643315149784664640130857469609484767216990764 0 34910653778453858417243277430036127070 20630840970905656394917552633664734473 +15326334123263228775527061258849439685882148106869565386953741774363668519172 0 66500744683300223225058963352885370919 33299359737804689260914556379517239431 +1536827038537102107671164279058250012501899245386546846022428251052243206077 0 91756641454575594718891265740792717604 133196436762387716778931184696517927957 +16631334948691040652655818585524246027973595574537760421931809390805557244250 0 35216793509296283767689105162453876442 51009986355351053331777331899705480831 +310008648629248263675757318269125716471073330347610844044814550643017767997 0 39935643909275605528168247499125826774 93193526020506757036978928715392292615 +2463252262790349257817246462420605689911697219274737975617708156844411580331 0 7157684139797651986643730008993503088 92678754929599000845663986467269459409 +17284406485920881236655133813706512402405976303663558503907854834683600060559 0 45347181723516646264514673965579904277 29979650000616804480788961763876995575 +6482474774432583328585607964771002032035561524185902753733613893206656606362 0 124345662492960828276488359810382182234 97264092822363848903147684642422191363 +21458172575601141568486198665885412717068832372859840866426604752759769998455 0 58800687073352167330659817216034217442 128320042212428601671194335541574262643 +1521412204093457879652095506594144989973431689136036493610215463583683317373 0 143863986568293242598558968031162401866 35193914345694584091796830833653164349 +18967396981099780491841418098664150447532415040261171175112182812620086081431 0 37950782616622189825924586612594824819 69576422446910417539431560512322407239 +12840801243241299539488175630640763675460750870167649360024948812502809060825 0 30069671734494720760804598149124190571 11578020288115991475780922289354748688 +13659311080456894291425385716366270336355100941519581404701738051670827157223 0 74181101614327231275851839396072929764 22373600402897923786618819219476957976 +14971865212535361902888864093738228522006036571937630812220970526572199114699 0 95113838194660450512944642611037371445 40087370226962176680423380879865136753 +17790746179792014954590223167397057998401475418574775448183347791221054701339 0 49224595544494149994701708244339519669 78029117541692663372863262645773610876 +6480541693111832089385132079811951042499749233222751750463894084149741918232 0 57971068577858400240955712096757316023 56375950861705525747656236554406381361 +10747419077457024411696197334657216139653386678990157031656529966251588462253 0 125082033448113404659171851951586279691 59462820978389273262018668201903192855 +9225354699962684185786022969335637997955229241598294665780472613568911989907 0 56347651391149388979476026877858300094 99847101750731977344796558203524167500 +20246898143574537843924603985078325220790411996558030262942947066192243894739 0 129303663503150359925148577207906552669 59728009601109175837976073252999868469 +14222725876183076213457387639656051418262244503289138536699056251877771005068 0 61627140289289636820813640954435167228 69847064697947304368992826552879616415 +16679901590153828885589444152296620580959088044277202147542256155978414501773 0 157762232375646373969579752863657700285 109654336393119268420585438705618660157 +3509558862909900014852847067977352506357030153882981148225364883971050253321 0 64410236925349009561474480890500098664 134719101461404667507725393094582564421 +1131479289025186351795105454265515678999649505267714507137701579622021042268 0 45971283455978390077017097703284740512 66990679327148887408321045060222546223 +13202237181916456215666522491574223369734337299511724702936244256967423023862 0 51787444648189891473304268920960818086 40781390063993877191089394098345275276 +4245373468111017965528521507532593409579731231279701501910232227427151602673 0 39924974021321592156985676604191663589 107602765226706656908067184141549783126 +9200633800167229457777327418788380742780239296629341384552957200874038377624 0 52021714168486949334975240171395400421 76729576285726912636815737264300599626 +4302958212305184184830012307674316737981497271088497726454206081696982545160 0 67133779878311962676750630243491980083 26368993052867615050050016565996008423 +18050650407601925802026431703704272661975010158190154020084914195820191987768 0 152396809521487505366856130349788636929 131724848198859550137175024510657521444 +5135953144290446400929271652604890076355155559442600081001011997077123638561 0 135044675882137410808872665039944543542 77551750007877511977672196963714685425 +1763830533760750783788832873176042423124216623286053514126086083801333242340 0 89271640405993375615750075951757691486 68103656839795995771162377054660596709 +421180974699715662699416313445513743163793539337461359011386490110464560906 0 30413306303721588320563263618348846935 51889285652295541162333134746169613831 +7487685055170244878668616953386915765781923036068896828342096022949318930741 0 32164385373484529671798588737673652560 6611662774270890384072712656338729570 +16920301857509388853964446451965429872358923187109229791730421705080125869019 0 34826822846885276558182303559247353038 69047161675563627734721302969521827228 +6427436323036253758839695308953598476565850341146103700892275775235148590840 0 92562902702077562845891218974200492215 114775888383193019588283747170682767787 +1395212119784206359607418777896584490325049767852891621825610285236194798608 0 134407435233999512527007177967503303373 9914405457481759911629277749959523027 +17474709298616749316471118148852000778169167461188542679052077095740772165522 0 81208599894256255282867615538374387737 36349224568018695238669710673873091229 +800163361278674447545357429339738428377836510283644396957359106603929217801 0 17296837584867811143781436009463247109 76577581327019310692404063884997951997 +8985234309672711659380246905809997696976078301006825155815457594972719729284 0 95437817329605740945481459486361323337 21175209367267417514746180180573053278 +18192827899628612969230263654619743723699111837747195867422408156794656123398 0 130422124428034769092476946136385071789 122088908575943855425234566666606368903 +12749074014932563232721188048428322015774507685803197645552303984383016293733 0 80994323952507528292836596883308213906 51874830477945374678739528510766840069 +19768283869212643843183643647043464164298043783809098922063989418930027885508 0 111452175555404533416009469176934929917 108652854961764793722107935873383663481 +7088166988471681391782377492901703896546285411021596591471237160631783990027 0 44472179831482472026646021758298061014 90132458483810985666685828802729476949 +4196920307434220573974426993630611626685788688571989770906071303463312645826 0 4905009762651119850607151363875528110 4231941445886877474580442315873862930 +5346150384279442190283947786658669475391741362708834390474022348299397541418 0 43040562036515499816352287614224706105 71230681708880501053646036542713051223 +20173679369077087362218946548548908068186286690372796169924990476990342246921 0 54118581707186924725722490059794851609 31724255479438649635268310352852681739 +9798156970502398376823877078049114223883933421793734495365084964149142678231 0 24882434957285251302848630571601041132 16508273467112104165946611068502590575 +7009289112915391260083892637920964761614574168440917704453047585218779398951 0 32306505064857777433046339051001804187 6812183026186677462980247197019529882 +14314745470728325268146986827777882895565399216606059161171870963597610110336 0 104898486430138053004814980242657453305 128138851326309218149922263082758157860 +4412562917343731512061227605323605810190643301349269315370547879142645683100 0 129913289395864711085642678093188854875 106386319732467349749360664253398895588 +12028767715118155962041579459517900153222589754063959209509504013994984268306 0 53241048630474759865015670873222249148 67207748496494885452736215383524113231 +12246750884028308209980448870106655136151752126133142208823329821869768374234 0 152421560332217837756343305589199403764 77344352340241433967009021308896546506 +18228664880783270445467084944182335111523030955626500381371508282438778557439 0 24236230682598972959760976150727921416 99143808039139367211254118838596071126 +4798911225939489447616838669631268050471559751162292893234575150636029987582 0 35600257043616798806636253088878555290 53935491101941428356012369892438768071 +1502452278394666107954752334778079092619084894906925127819666024679712294939 0 71743486195590463179734105421531162651 95399299040829355746559869479652504380 +4968803723853208654429673030676019334739905255981972894037649056744248789725 0 74472721206729155362930571253430852096 53568072880700614395488873844162976119 +12425024562599435002527744822638580794556422148981596748190381010493974915322 0 45443196107602058717986561344032718838 82432441190989704974691168198958699992 +15855635702064282034494825427524547106497263640446962880553941169787887682855 0 56061863816410908791758891816249443922 123447981149774642679053259103524760649 +6868911023172745915234634494620977763151663744660916262990790663438319429706 0 140119805264467227774924290223609448946 83975919280473531705927202193546556409 +8734213151925587275290974719631833863053798983832471364143828835809738148008 0 20970997326406030421250479218493822816 107305716729696026350131194821945359013 +11051143999915734757008281891674328961821936043454015313577218621259792920407 0 142947696093002467937419662331608202578 148236353675604418633690345056734156433 +7344725227296977232496898520547284046759603566412051697523823454686399087683 0 92699831502338285567509706153007419257 61348395677727055077286953342671504041 +15415222883433667396426261505856059995127556846281608779716499670506811908352 0 116837870913398705897280383510833303917 107830456778479514368314276407932197519 +1664154309824540180671177677187161613831766555241101441396263373051464893033 0 63188035904535269573504333963066060650 117586538793932928247531072226923627740 +12929062719903081805005135246817794386554038776361871359846245872639287567216 0 43829383497869529422615652421209180925 144035920988280805463992517012916349691 +8005672939216896426256277788619773159448375007532249139648509779109302286878 0 75715204690327561412284025054919468075 89620187970709958545669121684626375227 +9823860471152812665184246524592480774012862008613798713737767800820830068191 0 68576994052458527470959970437791295132 82096663446595030400026868421118995463 +15687619327887954494357679272464699403263950219266943313750566287276067353526 0 141537622296208364007273901553735687862 139581950098770744884180416235436309511 +13973390760364460869191287025967518936342679359376387196267718747682062054071 0 28382786338576212732305345669516494507 62658186745238145346539845786398234783 +16315879523933246355646912685087844333971760056014452101693642000732457361867 0 99951170051825431134038721097964319045 135218046385634266609146731590482298650 +8577421190893439028114875164247658441981757895996482477629561897942300153680 0 29513504906851498095481639547777924068 89782284556038349930635280843672171259 +6414083401679237555575565160870944176872975106323259027730898503375901444746 0 16866120458289363860093936364182633924 143233267196534356298451563794973872230 +17712603180560147503853792747875311504119345587202275131845536458796955651351 0 37038829938232524087710316701001135115 157151558482068384404863372619156204448 +6765909976465850328496608927089166108057458764793534137710423164655395579907 0 77498274465783970505423886572624627735 121339219156308847147597172811870969245 +14808883726622039734239898006769367524178452850142905245262886603553992301315 0 20237477742775640336957602613979747010 26486594546002555312846547753522513558 +2261068838672740511699860047467241464553597192512582291224577461474454573604 0 136966374827241481601349155189643008099 56050358435274042026562765979436567686 +7979264793125071671225757057347205400488260501091091045130882828366676825084 0 28870529621607864393199046660317197385 80070203491404289997827590164118981810 +2024204262897493767009467541070903804161349851844341416019202729580406670107 0 129306725402031049277631487487917506958 10723069359599266790576338815615511178 +11138470166623618787183129094271386390031626909516857863440692724554405970138 0 20090196687653863486802911971649701845 142425422226546893771500534261749040455 +3524441642461055274391048557771446242024462464203847668137345828521729243945 0 108478210141233850952353580145888570271 133630242646036566847957511634116364627 +16532733059602060690785929847024178341024392103823012778302586570098985388309 0 15750048349203448281711708648364099878 63549389674837878369173696179794058985 +14158789744837569706644992051751601006119334013439239652793718889183339591976 0 130342443209035317816553714170307980375 93338294681056614066154597465555639126 +9912728642315349415539814823546471799303505959511864329589864531153202240661 0 67297894365416007473600081008628427739 131162774731607570334537839843098641015 +18177139082189709695784686625126067716663954837943118145698033777411587654869 0 98236801985913779406218601897206431831 107318565689495187163973730990118414464 +5928319863448351399810739990750522671822151170323139505119505260528471445611 0 15094988335250339147652689319869421122 12281455832326887616997888014003426510 +1738331155022398863568451228562202454351598930403045685991598530559295824320 0 74753932722781472966437210695128864282 33167285109193199897287175844959981854 +5283570977610375369893179584749466019020114453298559128862696321076215886714 0 133525918484362777444700444742990007868 31456084795809114101646208999941871217 +13648021693592051955306499546417615812504358498862863320994439161049140108698 0 150288359053306718239961775392555354954 30524061543281263825304788894162969436 +19582918942383467190667535082137019395139479697998764502366844553173910298264 0 80600518426161174954260236303126723483 149019431346771383851494768971946665089 +434887460715757796763313964314309437643918227064959898587147874550866884562 0 42599271799349352611654079149964726948 95304910036111503330332268458656765472 +9463027529600417631061912432962180565778206574483632161402527365417748736443 0 94782455850607379158918606296709397760 143151026050916019276526303561834029923 +8986759055147288938429642989612379785012771395642261894767280466194781357947 0 32599919852679748837855542837259557737 136155915424744966874516239919443515495 +6882070352328610634895543301764417399807266920780443605639412445541016522304 0 25990973354640593797095180015011933467 71151278237629170455232850302493084283 +20528337398443536064159882539161208997478513789548339699324206644173680034881 0 134326118180112955565082737980006479818 74810510180766695714826655845465048757 +16457636472821006302397441427707964655436622384413834270598565395202133345651 0 57732597487850435078927475892788353776 154769642346344802870976975969222927164 +5610449454817548583074849578741727534370362445301174270947002537633816119974 0 87383178825411049845085082489966242506 102999815378266494405817603985003522527 +21660987615710643886915782486248368327523383824721497747726705107965372723897 0 96252059234912160199857665956195263696 125552514027146782033738160124378338988 +6536400132881142640538729558093230291280483408833968513953114638079365083920 0 140401570093354583676435035254620962126 132974278118569632717754070890193729866 +12278473778626664065405952502391504202543447364209306055434707550643296169863 0 92893759598378889117788981428366793690 30964303441223410759870739796379713522 +16726307773016602570572099051964938490975311395117657959777088321125265212666 0 76587903114551696062069563108606553940 108453305992217254652101320890246347990 +5415144548086459716293252415766813361666559270834273109661474447000272359579 0 97313761947330195025960803350105307084 55432710146199915359232778109783327729 +9226442034538437864523440956911232457106716669061498685140975883260424095393 0 149509008282539693986786060430682491823 116011175760218106891385556028811487572 +7810305135478854636976079590658683428098355241030636106224215947187644714478 0 27379835307682529868588520113401666717 80441540557070700586364362779862170811 +8109137731243254138203952170446452172384047521039545864832824347225681427131 0 90414083338028514115616960705345523576 53646231993016203491267858960067881565 +14030106614594882326603669102197328663543261492731389200756046546633370385424 0 51979271299669989619485789080559379515 101559166342974232415616684607221417414 +1427459672226339976859186341347575628864693739325345196770280809612715239209 0 78811623411536898649283896443892342800 70036252377429626292498520016689936588 \ No newline at end of file diff --git a/evm/src/cpu/kernel/tests/ecc/curve_ops.rs b/evm/src/cpu/kernel/tests/ecc/curve_ops.rs index 3198cef3..f107d8be 100644 --- a/evm/src/cpu/kernel/tests/ecc/curve_ops.rs +++ b/evm/src/cpu/kernel/tests/ecc/curve_ops.rs @@ -4,15 +4,16 @@ mod bn { use ethereum_types::U256; use crate::cpu::kernel::aggregator::KERNEL; - use crate::cpu::kernel::interpreter::run_interpreter; + use crate::cpu::kernel::interpreter::{run_interpreter, Interpreter}; use crate::cpu::kernel::tests::u256ify; + use crate::memory::segments::Segment; #[test] fn test_ec_ops() -> Result<()> { // Make sure we can parse and assemble the entire kernel. - let ec_add = KERNEL.global_labels["ec_add"]; - let ec_double = KERNEL.global_labels["ec_double"]; - let ec_mul = KERNEL.global_labels["ec_mul"]; + let ec_add = KERNEL.global_labels["bn_add"]; + let ec_double = KERNEL.global_labels["bn_double"]; + let ec_mul = KERNEL.global_labels["bn_mul"]; let identity = ("0x0", "0x0"); let invalid = ("0x0", "0x3"); // Not on curve let point0 = ( @@ -130,11 +131,100 @@ mod bn { Ok(()) } + + #[test] + fn test_glv_verify_data() -> Result<()> { + let glv = KERNEL.global_labels["bn_glv_decompose"]; + + let f = include_str!("bn_glv_test_data"); + for line in f.lines().filter(|s| !s.starts_with("//")) { + let mut line = line + .split_whitespace() + .map(|s| U256::from_str_radix(s, 10).unwrap()) + .collect::>(); + let k = line.remove(0); + line.reverse(); + + let mut initial_stack = u256ify(["0xdeadbeef"])?; + initial_stack.push(k); + let mut int = Interpreter::new(&KERNEL.code, glv, initial_stack, &KERNEL.prover_inputs); + int.run()?; + + assert_eq!(line, int.stack()); + } + + Ok(()) + } + + #[test] + fn test_precomputation() -> Result<()> { + let precompute = KERNEL.global_labels["bn_precompute_table"]; + + let initial_stack = u256ify([ + "0xdeadbeef", + "0x10d7cf0621b6e42c1dbb421f5ef5e1936ca6a87b38198d1935be31e28821d171", + "0x11b7d55f16aaac07de9a0ed8ac2e8023570dbaa78571fc95e553c4b3ba627689", + ])?; + let mut int = Interpreter::new( + &KERNEL.code, + precompute, + initial_stack, + &KERNEL.prover_inputs, + ); + int.run()?; + + let mut computed_table = Vec::new(); + for i in 0..32 { + computed_table.push( + int.generation_state + .memory + .mload_general(0, Segment::BnTableQ, i), + ); + } + + let table = u256ify([ + "0x11b7d55f16aaac07de9a0ed8ac2e8023570dbaa78571fc95e553c4b3ba627689", + "0x10d7cf0621b6e42c1dbb421f5ef5e1936ca6a87b38198d1935be31e28821d171", + "0x1565e5587d8566239c23219bc0e1d1d267d19100c3869d0c55b1e3ea4532304e", + "0x19fd9b572558479df062632562113e4d9a3eb655698ee3be9a5350ed23e690ee", + "0x19469e55e27021c0af1310ad266cdf1d9eef6942c80afe9c7b517acf16a2a3e1", + "0x226ec29db9339d7ffb1bc3260f1ca008b804f78553d316c37203118466bb5f5a", + "0x10a16b4786bd1717a031a1948010593173d36ab35535641c9fe41802d639b435", + "0x294fe34d7ec9024c96cfde58311b9ee394ff9f8735d882005fcf0d28709b459d", + "0x300f58e61d4ab1872f6b5fad517c6df1b23468fcfa81154786ec230cb0df6d20", + "0x12ff1d200127d2ba7a0171cadbe0f729fc5acbe95565cc57f07c9fa42c001390", + "0x1045a28c9a35a17b63da593c0137ac08a1fda78430b71755941d3dc501b35272", + "0x2a3f4d91b58179451ec177f599d7eaf79e2555f169fd3e5d2af314600fad299", + "0x21de5680f03b262f53d3252d5ca71bbc5f2c9ff5483fb63abaea1ee7e9cede1d", + "0x144249d3fc4c82327845a38ea51181acb374ab30a1e7ea0f13bc8a8b04d96411", + "0x2ba4ce4289de377397878c1195e21a1d573b02d9463f5c454ec50bdf11aee512", + "0x259a447b42bab48e07388baece550607bc0a8a88e1ea224eba94c6bed08e470e", + "0x2ba4ce4289de377397878c1195e21a1d573b02d9463f5c454ec50bdf11aee512", + "0xaca09f79e76eb9bb117ba07b32c5255db76e0088687a83e818bc55807eeb639", + "0x21de5680f03b262f53d3252d5ca71bbc5f2c9ff5483fb63abaea1ee7e9cede1d", + "0x1c22049ee4e51df7400aa227dc6fd6b0e40cbf60c689e07e2864018bd3a39936", + "0x1045a28c9a35a17b63da593c0137ac08a1fda78430b71755941d3dc501b35272", + "0x2dc05999c5d9889566642e3727e3d9ae1d9f153251d1f6a769715ad0d7822aae", + "0x300f58e61d4ab1872f6b5fad517c6df1b23468fcfa81154786ec230cb0df6d20", + "0x1d653152e009cd6f3e4ed3eba5a061339b269ea8130bfe354ba3ec72ac7ce9b7", + "0x10a16b4786bd1717a031a1948010593173d36ab35535641c9fe41802d639b435", + "0x7146b2562689ddd2180675e5065b97a0281cb0a3299488cdc517eee67e1b7aa", + "0x19469e55e27021c0af1310ad266cdf1d9eef6942c80afe9c7b517acf16a2a3e1", + "0xdf58bd527fe02a9bd3482907264b854df7c730c149eb3c9ca1d7a9271c19ded", + "0x1565e5587d8566239c23219bc0e1d1d267d19100c3869d0c55b1e3ea4532304e", + "0x1666b31bbbd9588bc7ede2911f701a0ffd42b43bfee2e6cea1cd3b29b4966c59", + "0x11b7d55f16aaac07de9a0ed8ac2e8023570dbaa78571fc95e553c4b3ba627689", + "0x1f8c7f6cbf7abbfd9a950397228b76ca2adac21630583d7406625a34505b2bd6", + ])?; + + assert_eq!(computed_table, table); + + Ok(()) + } } #[cfg(test)] mod secp { - use anyhow::Result; use ethereum_types::U256; @@ -146,8 +236,8 @@ mod secp { fn test_ec_ops() -> Result<()> { // Make sure we can parse and assemble the entire kernel. let kernel = combined_kernel(); - let ec_add = kernel.global_labels["ec_add_valid_points_secp"]; - let ec_double = kernel.global_labels["ec_double_secp"]; + let ec_add = kernel.global_labels["secp_add_valid_points"]; + let ec_double = kernel.global_labels["secp_double"]; let identity = ("0x0", "0x0"); let point0 = ( "0xc82ccceebd739e646631b7270ed8c33e96c4940b19db91eaf67da6ec92d109b", @@ -207,9 +297,9 @@ mod secp { #[test] fn test_glv_verify_data() -> Result<()> { - let glv = KERNEL.global_labels["glv_decompose"]; + let glv = KERNEL.global_labels["secp_glv_decompose"]; - let f = include_str!("glv_test_data"); + let f = include_str!("secp_glv_test_data"); for line in f.lines().filter(|s| !s.starts_with("//")) { let mut line = line .split_whitespace() diff --git a/evm/src/cpu/kernel/tests/ecc/glv_test_data b/evm/src/cpu/kernel/tests/ecc/secp_glv_test_data similarity index 100% rename from evm/src/cpu/kernel/tests/ecc/glv_test_data rename to evm/src/cpu/kernel/tests/ecc/secp_glv_test_data diff --git a/evm/src/memory/segments.rs b/evm/src/memory/segments.rs index a632e40f..4ae1afa4 100644 --- a/evm/src/memory/segments.rs +++ b/evm/src/memory/segments.rs @@ -40,10 +40,13 @@ pub(crate) enum Segment { ShiftTable = 16, JumpdestBits = 17, EcdsaTable = 18, + BnWnafA = 19, + BnWnafB = 20, + BnTableQ = 21, } impl Segment { - pub(crate) const COUNT: usize = 19; + pub(crate) const COUNT: usize = 22; pub(crate) fn all() -> [Self; Self::COUNT] { [ @@ -66,6 +69,9 @@ impl Segment { Self::ShiftTable, Self::JumpdestBits, Self::EcdsaTable, + Self::BnWnafA, + Self::BnWnafB, + Self::BnTableQ, ] } @@ -91,6 +97,9 @@ impl Segment { Segment::ShiftTable => "SEGMENT_SHIFT_TABLE", Segment::JumpdestBits => "SEGMENT_JUMPDEST_BITS", Segment::EcdsaTable => "SEGMENT_KERNEL_ECDSA_TABLE", + Segment::BnWnafA => "SEGMENT_KERNEL_BN_WNAF_A", + Segment::BnWnafB => "SEGMENT_KERNEL_BN_WNAF_B", + Segment::BnTableQ => "SEGMENT_KERNEL_BN_TABLE_Q", } } @@ -116,6 +125,9 @@ impl Segment { Segment::ShiftTable => 256, Segment::JumpdestBits => 1, Segment::EcdsaTable => 256, + Segment::BnWnafA => 8, + Segment::BnWnafB => 8, + Segment::BnTableQ => 256, } } }