From 6c4ef29fecc01a18699e4b79c800b36ae389f728 Mon Sep 17 00:00:00 2001 From: Hamish Ivey-Law <426294+unzvfu@users.noreply.github.com> Date: Tue, 31 Jan 2023 02:23:24 +1100 Subject: [PATCH 1/4] Add range checks to the arithmetic Stark (#866) * Simplify loop and remove clippy. * Offset auxiliary coefficients so they're always positive. * Split mul aux input into lo/hi parts. * Rename register. * Combine `QUO_INPUT_{LO,HI}`; rearrange some columns. * Split `MODULAR_AUX_INPUT` into high and low pieces. * Remove range_check_error debug output. * First draft of generating the range checks. * Remove opcodes for operations that were defined elsewhere. * Clean up interface to build arithmetic trace. * Fix "degree too high" bug in DIV by zero. * Fix constraint_transition usage in recursive compare. * Fix variable name; use named constant. * Fix comment values. * Fix bug in recursive MUL circuit. * Superficial improvements; remove unnecessary genericity. * Fix bug in recursive MULMOD circuit. * Remove debugging noise; expand test. * Minor comment. * Enforce assumption in assert. * Make DIV its own operation. * Make MOD it's own operation; rename structs; refactor. * Expand basic test. * Remove comment. * Put Stark operations in their own file. * Test long traces. * Minor comment. * Address William's comments. * Use `const_assert!` instead of `debug_assert!` because Clippy. --- evm/src/arithmetic/add.rs | 7 +- evm/src/arithmetic/arithmetic_stark.rs | 308 +++++++++++++++++++++---- evm/src/arithmetic/columns.rs | 74 +++--- evm/src/arithmetic/compare.rs | 15 +- evm/src/arithmetic/mod.rs | 2 + evm/src/arithmetic/modular.rs | 101 ++++---- evm/src/arithmetic/mul.rs | 48 +++- evm/src/arithmetic/operations.rs | 170 ++++++++++++++ evm/src/arithmetic/sub.rs | 7 +- evm/src/arithmetic/utils.rs | 88 ++----- evm/src/lookup.rs | 10 +- 11 files changed, 596 insertions(+), 234 deletions(-) create mode 100644 evm/src/arithmetic/operations.rs diff --git a/evm/src/arithmetic/add.rs b/evm/src/arithmetic/add.rs index 4e9de4b3..f97b709d 100644 --- a/evm/src/arithmetic/add.rs +++ b/evm/src/arithmetic/add.rs @@ -8,7 +8,6 @@ use plonky2::iop::ext_target::ExtensionTarget; use crate::arithmetic::columns::*; use crate::arithmetic::utils::read_value_u64_limbs; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::range_check_error; pub(crate) fn u256_add_cc(input0: [u64; N_LIMBS], input1: [u64; N_LIMBS]) -> ([u64; N_LIMBS], u64) { // Input and output have 16-bit limbs @@ -104,7 +103,7 @@ where cy } -pub fn generate(lv: &mut [F; NUM_ARITH_COLUMNS]) { +pub fn generate(lv: &mut [F]) { let input0 = read_value_u64_limbs(lv, ADD_INPUT_0); let input1 = read_value_u64_limbs(lv, ADD_INPUT_1); @@ -117,10 +116,6 @@ pub fn eval_packed_generic( lv: &[P; NUM_ARITH_COLUMNS], yield_constr: &mut ConstraintConsumer

, ) { - range_check_error!(ADD_INPUT_0, 16); - range_check_error!(ADD_INPUT_1, 16); - range_check_error!(ADD_OUTPUT, 16); - let is_add = lv[IS_ADD]; let input0_limbs = &lv[ADD_INPUT_0]; let input1_limbs = &lv[ADD_INPUT_1]; diff --git a/evm/src/arithmetic/arithmetic_stark.rs b/evm/src/arithmetic/arithmetic_stark.rs index 5790ae66..80742cd7 100644 --- a/evm/src/arithmetic/arithmetic_stark.rs +++ b/evm/src/arithmetic/arithmetic_stark.rs @@ -1,13 +1,17 @@ use std::marker::PhantomData; -use std::ops::Add; use itertools::Itertools; use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::field::packed::PackedField; +use plonky2::field::polynomial::PolynomialValues; use plonky2::hash::hash_types::RichField; +use plonky2::util::transpose; +use crate::arithmetic::operations::Operation; use crate::arithmetic::{add, columns, compare, modular, mul, sub}; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::lookup::{eval_lookups, eval_lookups_circuit, permuted_cols}; +use crate::permutation::PermutationPair; use crate::stark::Stark; use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; @@ -16,53 +20,60 @@ pub struct ArithmeticStark { pub f: PhantomData, } -impl ArithmeticStark { - pub fn generate( - &self, - local_values: &mut [F; columns::NUM_ARITH_COLUMNS], - next_values: &mut [F; columns::NUM_ARITH_COLUMNS], - ) { - // Check that at most one operation column is "one" and that the - // rest are "zero". - assert_eq!( - columns::ALL_OPERATIONS - .iter() - .map(|&c| { - if local_values[c] == F::ONE { - Ok(1u64) - } else if local_values[c] == F::ZERO { - Ok(0u64) - } else { - Err("column was not 0 nor 1") - } - }) - .fold_ok(0u64, Add::add), - Ok(1) - ); +const RANGE_MAX: usize = 1usize << 16; // Range check strict upper bound - if local_values[columns::IS_ADD].is_one() { - add::generate(local_values); - } else if local_values[columns::IS_SUB].is_one() { - sub::generate(local_values); - } else if local_values[columns::IS_MUL].is_one() { - mul::generate(local_values); - } else if local_values[columns::IS_LT].is_one() { - compare::generate(local_values, columns::IS_LT); - } else if local_values[columns::IS_GT].is_one() { - compare::generate(local_values, columns::IS_GT); - } else if local_values[columns::IS_ADDMOD].is_one() { - modular::generate(local_values, next_values, columns::IS_ADDMOD); - } else if local_values[columns::IS_SUBMOD].is_one() { - modular::generate(local_values, next_values, columns::IS_SUBMOD); - } else if local_values[columns::IS_MULMOD].is_one() { - modular::generate(local_values, next_values, columns::IS_MULMOD); - } else if local_values[columns::IS_MOD].is_one() { - modular::generate(local_values, next_values, columns::IS_MOD); - } else if local_values[columns::IS_DIV].is_one() { - modular::generate(local_values, next_values, columns::IS_DIV); - } else { - panic!("the requested operation should not be handled by the arithmetic table"); +impl ArithmeticStark { + /// Expects input in *column*-major layout + fn generate_range_checks(&self, cols: &mut Vec>) { + debug_assert!(cols.len() == columns::NUM_ARITH_COLUMNS); + + let n_rows = cols[0].len(); + debug_assert!(cols.iter().all(|col| col.len() == n_rows)); + + for i in 0..RANGE_MAX { + cols[columns::RANGE_COUNTER][i] = F::from_canonical_usize(i); } + + // For each column c in cols, generate the range-check + // permutations and put them in the corresponding range-check + // columns rc_c and rc_c+1. + for (c, rc_c) in columns::SHARED_COLS.zip(columns::RC_COLS.step_by(2)) { + let (col_perm, table_perm) = permuted_cols(&cols[c], &cols[columns::RANGE_COUNTER]); + cols[rc_c].copy_from_slice(&col_perm); + cols[rc_c + 1].copy_from_slice(&table_perm); + } + } + + pub fn generate(&self, operations: Vec<&dyn Operation>) -> 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 + // way to reach capacity is when every op is modular or DIV + // (which is obviously unlikely in normal + // circumstances). (Also need at least RANGE_MAX rows to + // accommodate range checks.) + let max_rows = std::cmp::max(2 * operations.len(), RANGE_MAX); + let mut trace_rows = Vec::with_capacity(max_rows); + + for op in operations { + let (row1, maybe_row2) = op.to_rows(); + trace_rows.push(row1); + + if let Some(row2) = maybe_row2 { + trace_rows.push(row2); + } + } + + // Pad the trace with zero rows if it doesn't have enough rows + // to accommodate the range check columns. + for _ in trace_rows.len()..RANGE_MAX { + trace_rows.push(vec![F::ZERO; columns::NUM_ARITH_COLUMNS]); + } + + let mut trace_cols = transpose(&trace_rows); + self.generate_range_checks(&mut trace_cols); + + trace_cols.into_iter().map(PolynomialValues::new).collect() } } @@ -77,8 +88,14 @@ impl, const D: usize> Stark for ArithmeticSta FE: FieldExtension, P: PackedField, { + // Range check all the columns + for col in columns::RC_COLS.step_by(2) { + eval_lookups(vars, yield_constr, col, col + 1); + } + let lv = vars.local_values; let nv = vars.next_values; + add::eval_packed_generic(lv, yield_constr); sub::eval_packed_generic(lv, yield_constr); mul::eval_packed_generic(lv, yield_constr); @@ -92,6 +109,11 @@ impl, const D: usize> Stark for ArithmeticSta vars: StarkEvaluationTargets, yield_constr: &mut RecursiveConstraintConsumer, ) { + // Range check all the columns + for col in columns::RC_COLS.step_by(2) { + eval_lookups_circuit(builder, vars, yield_constr, col, col + 1); + } + let lv = vars.local_values; let nv = vars.next_values; add::eval_ext_circuit(builder, lv, yield_constr); @@ -104,4 +126,198 @@ impl, const D: usize> Stark for ArithmeticSta fn constraint_degree(&self) -> usize { 3 } + + fn permutation_pairs(&self) -> Vec { + const START: usize = columns::START_SHARED_COLS; + const END: usize = START + columns::NUM_SHARED_COLS; + let mut pairs = Vec::with_capacity(2 * columns::NUM_SHARED_COLS); + for (c, c_perm) in (START..END).zip_eq(columns::RC_COLS.step_by(2)) { + pairs.push(PermutationPair::singletons(c, c_perm)); + pairs.push(PermutationPair::singletons( + c_perm + 1, + columns::RANGE_COUNTER, + )); + } + pairs + } +} + +#[cfg(test)] +mod tests { + use anyhow::Result; + use ethereum_types::U256; + use plonky2::field::types::{Field, PrimeField64}; + use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + use rand::{Rng, SeedableRng}; + use rand_chacha::ChaCha8Rng; + + use super::{columns, ArithmeticStark}; + use crate::arithmetic::operations::*; + use crate::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; + + #[test] + fn degree() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = ArithmeticStark; + + let stark = S { + f: Default::default(), + }; + test_stark_low_degree(stark) + } + + #[test] + fn circuit() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = ArithmeticStark; + + let stark = S { + f: Default::default(), + }; + test_stark_circuit_constraints::(stark) + } + + #[test] + fn basic_trace() { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = ArithmeticStark; + + let stark = S { + f: Default::default(), + }; + + // 123 + 456 == 579 + let add = SimpleBinaryOp::new(columns::IS_ADD, U256::from(123), U256::from(456)); + // (123 * 456) % 1007 == 703 + let mulmod = ModularBinaryOp::new( + columns::IS_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), + 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), + }; + // 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 pols = stark.generate(ops); + + // Trace should always have NUM_ARITH_COLUMNS columns and + // min(RANGE_MAX, operations.len()) rows. In this case there + // are only 6 rows, so we should have RANGE_MAX rows. + assert!( + pols.len() == columns::NUM_ARITH_COLUMNS + && pols.iter().all(|v| v.len() == super::RANGE_MAX) + ); + + // Each operation has a single word answer that we can check + let expected_output = [ + // Row (some ops take two rows), col, expected + (0, columns::ADD_OUTPUT, 579), + (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), + ]; + + for (row, col, expected) in expected_output { + // First register should match expected value... + let first = col.start; + let out = pols[first].values[row].to_canonical_u64(); + assert_eq!( + out, expected, + "expected column {} on row {} to be {} but it was {}", + first, row, expected, out, + ); + // ...other registers should be zero + let rest = col.start + 1..col.end; + assert!(pols[rest].iter().all(|v| v.values[row] == F::ZERO)); + } + } + + #[test] + fn big_traces() { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = ArithmeticStark; + + let stark = S { + f: Default::default(), + }; + + let mut rng = ChaCha8Rng::seed_from_u64(0x6feb51b7ec230f25); + + let ops = (0..super::RANGE_MAX) + .map(|_| { + SimpleBinaryOp::new( + columns::IS_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 + // min(RANGE_MAX, operations.len()) rows. In this case there + // are RANGE_MAX operations with one row each, so RANGE_MAX. + assert!( + pols.len() == columns::NUM_ARITH_COLUMNS + && pols.iter().all(|v| v.len() == super::RANGE_MAX) + ); + + let ops = (0..super::RANGE_MAX) + .map(|_| { + ModularBinaryOp::new( + columns::IS_MULMOD, + U256::from(rng.gen::<[u8; 32]>()), + 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 + // min(RANGE_MAX, operations.len()) rows. In this case there + // are RANGE_MAX operations with two rows each, so 2*RANGE_MAX. + assert!( + pols.len() == columns::NUM_ARITH_COLUMNS + && pols.iter().all(|v| v.len() == 2 * super::RANGE_MAX) + ); + } } diff --git a/evm/src/arithmetic/columns.rs b/evm/src/arithmetic/columns.rs index 779be2ee..aafc22a8 100644 --- a/evm/src/arithmetic/columns.rs +++ b/evm/src/arithmetic/columns.rs @@ -32,41 +32,42 @@ pub const IS_SUBMOD: usize = IS_ADDMOD + 1; pub const IS_MULMOD: usize = IS_SUBMOD + 1; pub const IS_LT: usize = IS_MULMOD + 1; pub const IS_GT: usize = IS_LT + 1; -pub const IS_SHL: usize = IS_GT + 1; -pub const IS_SHR: usize = IS_SHL + 1; -const START_SHARED_COLS: usize = IS_SHR + 1; - -pub(crate) const ALL_OPERATIONS: [usize; 12] = [ - IS_ADD, IS_MUL, IS_SUB, IS_DIV, IS_MOD, IS_ADDMOD, IS_SUBMOD, IS_MULMOD, IS_LT, IS_GT, IS_SHL, - IS_SHR, -]; +pub(crate) const START_SHARED_COLS: usize = IS_GT + 1; /// Within the Arithmetic Unit, there are shared columns which can be /// used by any arithmetic circuit, depending on which one is active /// this cycle. /// -/// Modular arithmetic takes 9 * N_LIMBS columns which is split across -/// two rows, the first with 5 * N_LIMBS columns and the second with -/// 4 * N_LIMBS columns. (There are hence N_LIMBS "wasted columns" in +/// Modular arithmetic takes 11 * N_LIMBS columns which is split across +/// two rows, the first with 6 * N_LIMBS columns and the second with +/// 5 * N_LIMBS columns. (There are hence N_LIMBS "wasted columns" in /// the second row.) -const NUM_SHARED_COLS: usize = 5 * N_LIMBS; +pub(crate) const NUM_SHARED_COLS: usize = 6 * N_LIMBS; +pub(crate) const SHARED_COLS: Range = START_SHARED_COLS..START_SHARED_COLS + NUM_SHARED_COLS; -const GENERAL_INPUT_0: Range = START_SHARED_COLS..START_SHARED_COLS + N_LIMBS; -const GENERAL_INPUT_1: Range = GENERAL_INPUT_0.end..GENERAL_INPUT_0.end + N_LIMBS; +pub(crate) const GENERAL_INPUT_0: Range = START_SHARED_COLS..START_SHARED_COLS + N_LIMBS; +pub(crate) const GENERAL_INPUT_1: Range = GENERAL_INPUT_0.end..GENERAL_INPUT_0.end + N_LIMBS; const GENERAL_INPUT_2: Range = GENERAL_INPUT_1.end..GENERAL_INPUT_1.end + N_LIMBS; const GENERAL_INPUT_3: Range = GENERAL_INPUT_2.end..GENERAL_INPUT_2.end + N_LIMBS; -const AUX_INPUT_0_LO: Range = GENERAL_INPUT_3.end..GENERAL_INPUT_3.end + N_LIMBS; +// NB: Only one of these two sets of columns will be used for a given operation +const GENERAL_INPUT_4: Range = GENERAL_INPUT_3.end..GENERAL_INPUT_3.end + N_LIMBS; +const GENERAL_INPUT_4_DBL: Range = GENERAL_INPUT_3.end..GENERAL_INPUT_3.end + 2 * N_LIMBS; // The auxiliary input columns overlap the general input columns // because they correspond to the values in the second row for modular // operations. -const AUX_INPUT_0_HI: Range = START_SHARED_COLS..START_SHARED_COLS + N_LIMBS; -const AUX_INPUT_1: Range = AUX_INPUT_0_HI.end..AUX_INPUT_0_HI.end + 2 * N_LIMBS; +const AUX_INPUT_0: Range = START_SHARED_COLS..START_SHARED_COLS + N_LIMBS; +const AUX_INPUT_1: Range = AUX_INPUT_0.end..AUX_INPUT_0.end + 2 * N_LIMBS; // These auxiliary input columns are awkwardly split across two rows, // with the first half after the general input columns and the second // half after the auxiliary input columns. -const AUX_INPUT_2: Range = AUX_INPUT_1.end..AUX_INPUT_1.end + N_LIMBS; +const AUX_INPUT_2: Range = AUX_INPUT_1.end..AUX_INPUT_1.end + 2 * N_LIMBS - 1; + +// Each element c of {MUL,MODULAR}_AUX_INPUT is -2^20 <= c <= 2^20; +// this value is used as an offset so that everything is positive in +// the range checks. +pub(crate) const AUX_COEFF_ABS_MAX: i64 = 1 << 20; // ADD takes 3 * N_LIMBS = 48 columns pub(crate) const ADD_INPUT_0: Range = GENERAL_INPUT_0; @@ -78,11 +79,12 @@ pub(crate) const SUB_INPUT_0: Range = GENERAL_INPUT_0; pub(crate) const SUB_INPUT_1: Range = GENERAL_INPUT_1; pub(crate) const SUB_OUTPUT: Range = GENERAL_INPUT_2; -// MUL takes 4 * N_LIMBS = 64 columns +// MUL takes 5 * N_LIMBS = 80 columns pub(crate) const MUL_INPUT_0: Range = GENERAL_INPUT_0; pub(crate) const MUL_INPUT_1: Range = GENERAL_INPUT_1; pub(crate) const MUL_OUTPUT: Range = GENERAL_INPUT_2; -pub(crate) const MUL_AUX_INPUT: Range = GENERAL_INPUT_3; +pub(crate) const MUL_AUX_INPUT_LO: Range = GENERAL_INPUT_3; +pub(crate) const MUL_AUX_INPUT_HI: Range = GENERAL_INPUT_4; // LT and GT take 4 * N_LIMBS = 64 columns pub(crate) const CMP_INPUT_0: Range = GENERAL_INPUT_0; @@ -90,8 +92,8 @@ pub(crate) const CMP_INPUT_1: Range = GENERAL_INPUT_1; pub(crate) const CMP_OUTPUT: usize = GENERAL_INPUT_2.start; pub(crate) const CMP_AUX_INPUT: Range = GENERAL_INPUT_3; -// MULMOD takes 4 * N_LIMBS + 2 * 2*N_LIMBS + N_LIMBS = 144 columns -// but split over two rows of 80 columns and 64 columns. +// MULMOD takes 4 * N_LIMBS + 3 * 2*N_LIMBS + N_LIMBS = 176 columns +// but split over two rows of 96 columns and 80 columns. // // ADDMOD, SUBMOD, MOD and DIV are currently implemented in terms of // the general modular code, so they also take 144 columns (also split @@ -100,18 +102,28 @@ pub(crate) const MODULAR_INPUT_0: Range = GENERAL_INPUT_0; pub(crate) const MODULAR_INPUT_1: Range = GENERAL_INPUT_1; pub(crate) const MODULAR_MODULUS: Range = GENERAL_INPUT_2; pub(crate) const MODULAR_OUTPUT: Range = GENERAL_INPUT_3; -pub(crate) const MODULAR_QUO_INPUT_LO: Range = AUX_INPUT_0_LO; +pub(crate) const MODULAR_QUO_INPUT: Range = GENERAL_INPUT_4_DBL; +pub(crate) const MODULAR_OUT_AUX_RED: Range = AUX_INPUT_0; // NB: Last value is not used in AUX, it is used in MOD_IS_ZERO -pub(crate) const MODULAR_QUO_INPUT_HI: Range = AUX_INPUT_0_HI; -pub(crate) const MODULAR_AUX_INPUT: Range = AUX_INPUT_1.start..AUX_INPUT_1.end - 1; -pub(crate) const MODULAR_MOD_IS_ZERO: usize = AUX_INPUT_1.end - 1; -pub(crate) const MODULAR_OUT_AUX_RED: Range = AUX_INPUT_2; +pub(crate) const MODULAR_MOD_IS_ZERO: usize = AUX_INPUT_1.start; +pub(crate) const MODULAR_AUX_INPUT_LO: Range = AUX_INPUT_1.start + 1..AUX_INPUT_1.end; +pub(crate) const MODULAR_AUX_INPUT_HI: Range = AUX_INPUT_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_INPUT_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 = MODULAR_QUO_INPUT_LO; +pub(crate) const DIV_OUTPUT: Range = + MODULAR_QUO_INPUT.start..MODULAR_QUO_INPUT.start + N_LIMBS; -pub const NUM_ARITH_COLUMNS: usize = START_SHARED_COLS + NUM_SHARED_COLS; +// Need one column for the table, then two columns for every value +// that needs to be range checked in the trace, namely the permutation +// of the column and the permutation of the range. The two +// permutations associated to column i will be in columns RC_COLS[2i] +// and RC_COLS[2i+1]. +pub(crate) const NUM_RANGE_CHECK_COLS: usize = 1 + 2 * NUM_SHARED_COLS; +pub(crate) const RANGE_COUNTER: usize = START_SHARED_COLS + NUM_SHARED_COLS; +pub(crate) const RC_COLS: Range = RANGE_COUNTER + 1..RANGE_COUNTER + 1 + 2 * NUM_SHARED_COLS; + +pub const NUM_ARITH_COLUMNS: usize = START_SHARED_COLS + NUM_SHARED_COLS + NUM_RANGE_CHECK_COLS; diff --git a/evm/src/arithmetic/compare.rs b/evm/src/arithmetic/compare.rs index 780053ce..318b197f 100644 --- a/evm/src/arithmetic/compare.rs +++ b/evm/src/arithmetic/compare.rs @@ -24,13 +24,12 @@ use crate::arithmetic::columns::*; use crate::arithmetic::sub::u256_sub_br; use crate::arithmetic::utils::read_value_u64_limbs; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::range_check_error; -pub(crate) fn generate(lv: &mut [F; NUM_ARITH_COLUMNS], op: usize) { +pub(crate) fn generate(lv: &mut [F], filter: usize) { let input0 = read_value_u64_limbs(lv, CMP_INPUT_0); let input1 = read_value_u64_limbs(lv, CMP_INPUT_1); - let (diff, br) = match op { + let (diff, br) = match filter { // input0 - input1 == diff + br*2^256 IS_LT => u256_sub_br(input0, input1), // input1 - input0 == diff + br*2^256 @@ -84,10 +83,6 @@ pub fn eval_packed_generic( lv: &[P; NUM_ARITH_COLUMNS], yield_constr: &mut ConstraintConsumer

, ) { - range_check_error!(CMP_INPUT_0, 16); - range_check_error!(CMP_INPUT_1, 16); - range_check_error!(CMP_AUX_INPUT, 16); - let is_lt = lv[IS_LT]; let is_gt = lv[IS_GT]; @@ -147,7 +142,11 @@ pub(crate) fn eval_ext_circuit_lt, const D: usize>( ); let good_output = builder.sub_extension(cy, output); let filter = builder.mul_extension(is_op, good_output); - yield_constr.constraint_transition(builder, filter); + if is_two_row_op { + yield_constr.constraint_transition(builder, filter); + } else { + yield_constr.constraint(builder, filter); + } } pub fn eval_ext_circuit, const D: usize>( diff --git a/evm/src/arithmetic/mod.rs b/evm/src/arithmetic/mod.rs index 1493b292..ee6399da 100644 --- a/evm/src/arithmetic/mod.rs +++ b/evm/src/arithmetic/mod.rs @@ -14,6 +14,8 @@ mod utils; pub mod arithmetic_stark; pub(crate) mod columns; +pub mod operations; + #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub(crate) enum BinaryOperator { Add, diff --git a/evm/src/arithmetic/modular.rs b/evm/src/arithmetic/modular.rs index 46f6c0fa..73e8acb6 100644 --- a/evm/src/arithmetic/modular.rs +++ b/evm/src/arithmetic/modular.rs @@ -122,7 +122,6 @@ use crate::arithmetic::columns::*; use crate::arithmetic::compare::{eval_ext_circuit_lt, eval_packed_generic_lt}; use crate::arithmetic::utils::*; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::range_check_error; /// Convert the base-2^16 representation of a number into a BigInt. /// @@ -191,8 +190,8 @@ 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( - lv: &mut [F; NUM_ARITH_COLUMNS], - nv: &mut [F; NUM_ARITH_COLUMNS], + lv: &mut [F], + nv: &mut [F], filter: usize, operation: fn([i64; N_LIMBS], [i64; N_LIMBS]) -> [i64; 2 * N_LIMBS - 1], ) { @@ -263,37 +262,35 @@ fn generate_modular_op( // Higher order terms of the product must be zero for valid quot and modulus: debug_assert!(&prod[2 * N_LIMBS..].iter().all(|&x| x == 0i64)); + lv[MODULAR_OUTPUT].copy_from_slice(&output_limbs.map(F::from_canonical_i64)); + lv[MODULAR_QUO_INPUT].copy_from_slice("_limbs.map(F::from_noncanonical_i64)); // constr_poly must be zero when evaluated at x = β := // 2^LIMB_BITS, hence it's divisible by (x - β). `aux_limbs` is // the result of removing that root. - let aux_limbs = pol_remove_root_2exp::(constr_poly); + let mut aux_limbs = pol_remove_root_2exp::(constr_poly); - lv[MODULAR_OUTPUT].copy_from_slice(&output_limbs.map(|c| F::from_canonical_i64(c))); - - // Copy lo and hi halves of quot_limbs into their respective registers - for (i, &lo) in MODULAR_QUO_INPUT_LO.zip("_limbs[..N_LIMBS]) { - lv[i] = F::from_noncanonical_i64(lo); - } - for (i, &hi) in MODULAR_QUO_INPUT_HI.zip("_limbs[N_LIMBS..]) { - nv[i] = F::from_noncanonical_i64(hi); + for c in aux_limbs.iter_mut() { + // we store the unsigned offset value c + 2^20. + *c += AUX_COEFF_ABS_MAX; } + debug_assert!(aux_limbs.iter().all(|&c| c.abs() <= 2 * AUX_COEFF_ABS_MAX)); - for (i, &c) in MODULAR_AUX_INPUT.zip(&aux_limbs[..2 * N_LIMBS - 1]) { - nv[i] = F::from_noncanonical_i64(c); + for (i, &c) in MODULAR_AUX_INPUT_LO.zip(&aux_limbs[..2 * N_LIMBS - 1]) { + nv[i] = F::from_canonical_u16(c as u16); + } + for (i, &c) in MODULAR_AUX_INPUT_HI.zip(&aux_limbs[..2 * N_LIMBS - 1]) { + nv[i] = F::from_canonical_u16((c >> 16) as u16); } - nv[MODULAR_MOD_IS_ZERO] = mod_is_zero; - nv[MODULAR_OUT_AUX_RED].copy_from_slice(&out_aux_red.map(|c| F::from_canonical_i64(c))); + nv[MODULAR_OUT_AUX_RED].copy_from_slice(&out_aux_red.map(F::from_canonical_i64)); + nv[MODULAR_DIV_DENOM_IS_ZERO] = mod_is_zero * lv[IS_DIV]; } /// 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; NUM_ARITH_COLUMNS], - nv: &mut [F; NUM_ARITH_COLUMNS], - filter: usize, -) { +pub(crate) fn generate(lv: &mut [F], nv: &mut [F], filter: usize) { + debug_assert!(lv.len() == NUM_ARITH_COLUMNS && nv.len() == NUM_ARITH_COLUMNS); match filter { columns::IS_ADDMOD => generate_modular_op(lv, nv, filter, pol_add), columns::IS_SUBMOD => generate_modular_op(lv, nv, filter, pol_sub), @@ -319,14 +316,6 @@ fn modular_constr_poly( yield_constr: &mut ConstraintConsumer

, filter: P, ) -> [P; 2 * N_LIMBS] { - range_check_error!(MODULAR_INPUT_0, 16); - range_check_error!(MODULAR_INPUT_1, 16); - range_check_error!(MODULAR_MODULUS, 16); - range_check_error!(MODULAR_QUO_INPUT_LO, 16); - range_check_error!(MODULAR_QUO_INPUT_HI, 16); - range_check_error!(MODULAR_AUX_INPUT, 20, signed); - range_check_error!(MODULAR_OUTPUT, 16); - let mut modulus = read_value::(lv, MODULAR_MODULUS); let mod_is_zero = nv[MODULAR_MOD_IS_ZERO]; @@ -344,10 +333,14 @@ fn modular_constr_poly( let mut output = read_value::(lv, MODULAR_OUTPUT); + // Is 1 iff the operation is DIV and the denominator is zero. + let div_denom_is_zero = nv[MODULAR_DIV_DENOM_IS_ZERO]; + 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_lt() below subtracts modulus - // verify in the case of a DIV. - output[0] += mod_is_zero * lv[IS_DIV]; + // 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]; @@ -371,13 +364,12 @@ fn modular_constr_poly( true, ); // restore output[0] - output[0] -= mod_is_zero * lv[IS_DIV]; + output[0] -= div_denom_is_zero; // prod = q(x) * m(x) let quot = { let mut quot = [P::default(); 2 * N_LIMBS]; - quot[..N_LIMBS].copy_from_slice(&lv[MODULAR_QUO_INPUT_LO]); - quot[N_LIMBS..].copy_from_slice(&nv[MODULAR_QUO_INPUT_HI]); + quot.copy_from_slice(&lv[MODULAR_QUO_INPUT]); quot }; @@ -391,13 +383,21 @@ fn modular_constr_poly( let mut constr_poly: [_; 2 * N_LIMBS] = prod[0..2 * N_LIMBS].try_into().unwrap(); pol_add_assign(&mut constr_poly, &output); + let base = P::Scalar::from_canonical_u64(1 << LIMB_BITS); + let offset = P::Scalar::from_canonical_u64(AUX_COEFF_ABS_MAX as u64); + // constr_poly = c(x) + q(x) * m(x) + (x - β) * s(x) let mut aux = [P::ZEROS; 2 * N_LIMBS]; - for (i, j) in MODULAR_AUX_INPUT.enumerate() { - aux[i] = nv[j]; + for (c, i) in aux.iter_mut().zip(MODULAR_AUX_INPUT_LO) { + // MODULAR_AUX_INPUT elements were offset by 2^20 in + // generation, so we undo that here. + *c = nv[i] - offset; + } + // add high 16-bits of aux input + for (c, j) in aux.iter_mut().zip(MODULAR_AUX_INPUT_HI) { + *c += base * nv[j]; } - let base = P::Scalar::from_canonical_u64(1 << LIMB_BITS); pol_add_assign(&mut constr_poly, &pol_adjoin_root(aux, base)); constr_poly @@ -412,9 +412,9 @@ pub(crate) fn eval_packed_generic( // NB: The CTL code guarantees that filter is 0 or 1, i.e. that // only one of the operations below is "live". let filter = lv[columns::IS_ADDMOD] + + lv[columns::IS_SUBMOD] + lv[columns::IS_MULMOD] + lv[columns::IS_MOD] - + lv[columns::IS_SUBMOD] + lv[columns::IS_DIV]; // Ensure that this operation is not the last row of the table; @@ -480,7 +480,12 @@ fn modular_constr_poly_ext_circuit, const D: usize> modulus[0] = builder.add_extension(modulus[0], mod_is_zero); let mut output = read_value::(lv, MODULAR_OUTPUT); - output[0] = builder.mul_add_extension(mod_is_zero, lv[IS_DIV], output[0]); + + let div_denom_is_zero = nv[MODULAR_DIV_DENOM_IS_ZERO]; + let t = builder.mul_sub_extension(mod_is_zero, lv[IS_DIV], div_denom_is_zero); + let t = builder.mul_extension(filter, t); + yield_constr.constraint_transition(builder, t); + output[0] = builder.add_extension(output[0], div_denom_is_zero); let out_aux_red = &nv[MODULAR_OUT_AUX_RED]; let one = builder.one_extension(); @@ -497,13 +502,11 @@ fn modular_constr_poly_ext_circuit, const D: usize> is_less_than, true, ); - output[0] = - builder.arithmetic_extension(F::NEG_ONE, F::ONE, mod_is_zero, lv[IS_DIV], output[0]); + output[0] = builder.sub_extension(output[0], div_denom_is_zero); let quot = { let zero = builder.zero_extension(); let mut quot = [zero; 2 * N_LIMBS]; - quot[..N_LIMBS].copy_from_slice(&lv[MODULAR_QUO_INPUT_LO]); - quot[N_LIMBS..].copy_from_slice(&nv[MODULAR_QUO_INPUT_HI]); + quot.copy_from_slice(&lv[MODULAR_QUO_INPUT]); quot }; @@ -516,13 +519,19 @@ fn modular_constr_poly_ext_circuit, const D: usize> let mut constr_poly: [_; 2 * N_LIMBS] = prod[0..2 * N_LIMBS].try_into().unwrap(); pol_add_assign_ext_circuit(builder, &mut constr_poly, &output); + let offset = + builder.constant_extension(F::Extension::from_canonical_u64(AUX_COEFF_ABS_MAX as u64)); let zero = builder.zero_extension(); let mut aux = [zero; 2 * N_LIMBS]; - for (i, j) in MODULAR_AUX_INPUT.enumerate() { - aux[i] = nv[j]; + for (c, i) in aux.iter_mut().zip(MODULAR_AUX_INPUT_LO) { + *c = builder.sub_extension(nv[i], offset); + } + let base = F::from_canonical_u64(1u64 << LIMB_BITS); + for (c, j) in aux.iter_mut().zip(MODULAR_AUX_INPUT_HI) { + *c = builder.mul_const_add_extension(base, nv[j], *c); } - let base = builder.constant_extension(F::Extension::from_canonical_u64(1u64 << LIMB_BITS)); + let base = builder.constant_extension(base.into()); let t = pol_adjoin_root_ext_circuit(builder, aux, base); pol_add_assign_ext_circuit(builder, &mut constr_poly, &t); diff --git a/evm/src/arithmetic/mul.rs b/evm/src/arithmetic/mul.rs index d55ab27b..ea93990e 100644 --- a/evm/src/arithmetic/mul.rs +++ b/evm/src/arithmetic/mul.rs @@ -64,9 +64,8 @@ use plonky2::iop::ext_target::ExtensionTarget; use crate::arithmetic::columns::*; use crate::arithmetic::utils::*; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::range_check_error; -pub fn generate(lv: &mut [F; NUM_ARITH_COLUMNS]) { +pub fn generate(lv: &mut [F]) { let input0 = read_value_i64_limbs(lv, MUL_INPUT_0); let input1 = read_value_i64_limbs(lv, MUL_INPUT_1); @@ -96,23 +95,40 @@ pub fn generate(lv: &mut [F; NUM_ARITH_COLUMNS]) { let mut aux_limbs = pol_remove_root_2exp::(unreduced_prod); aux_limbs[N_LIMBS - 1] = -cy; - lv[MUL_AUX_INPUT].copy_from_slice(&aux_limbs.map(|c| F::from_noncanonical_i64(c))); + for c in aux_limbs.iter_mut() { + // we store the unsigned offset value c + 2^20 + *c += AUX_COEFF_ABS_MAX; + } + + debug_assert!(aux_limbs.iter().all(|&c| c.abs() <= 2 * AUX_COEFF_ABS_MAX)); + + lv[MUL_AUX_INPUT_LO].copy_from_slice(&aux_limbs.map(|c| F::from_canonical_u16(c as u16))); + lv[MUL_AUX_INPUT_HI] + .copy_from_slice(&aux_limbs.map(|c| F::from_canonical_u16((c >> 16) as u16))); } pub fn eval_packed_generic( lv: &[P; NUM_ARITH_COLUMNS], yield_constr: &mut ConstraintConsumer

, ) { - range_check_error!(MUL_INPUT_0, 16); - range_check_error!(MUL_INPUT_1, 16); - range_check_error!(MUL_OUTPUT, 16); - range_check_error!(MUL_AUX_INPUT, 20); + let base = P::Scalar::from_canonical_u64(1 << LIMB_BITS); let is_mul = lv[IS_MUL]; let input0_limbs = read_value::(lv, MUL_INPUT_0); let input1_limbs = read_value::(lv, MUL_INPUT_1); let output_limbs = read_value::(lv, MUL_OUTPUT); - let aux_limbs = read_value::(lv, MUL_AUX_INPUT); + + let aux_limbs = { + // MUL_AUX_INPUT was offset by 2^20 in generation, so we undo + // that here + let offset = P::Scalar::from_canonical_u64(AUX_COEFF_ABS_MAX as u64); + let mut aux_limbs = read_value::(lv, MUL_AUX_INPUT_LO); + let aux_limbs_hi = &lv[MUL_AUX_INPUT_HI]; + for (lo, &hi) in aux_limbs.iter_mut().zip(aux_limbs_hi) { + *lo += hi * base - offset; + } + aux_limbs + }; // Constraint poly holds the coefficients of the polynomial that // must be identically zero for this multiplication to be @@ -133,7 +149,6 @@ pub fn eval_packed_generic( pol_sub_assign(&mut constr_poly, &output_limbs); // This subtracts (x - β) * s(x) from constr_poly. - let base = P::Scalar::from_canonical_u64(1 << LIMB_BITS); pol_sub_assign(&mut constr_poly, &pol_adjoin_root(aux_limbs, base)); // At this point constr_poly holds the coefficients of the @@ -154,7 +169,20 @@ pub fn eval_ext_circuit, const D: usize>( let input0_limbs = read_value::(lv, MUL_INPUT_0); let input1_limbs = read_value::(lv, MUL_INPUT_1); let output_limbs = read_value::(lv, MUL_OUTPUT); - let aux_limbs = read_value::(lv, MUL_AUX_INPUT); + + let aux_limbs = { + let base = builder.constant_extension(F::Extension::from_canonical_u64(1 << LIMB_BITS)); + let offset = + builder.constant_extension(F::Extension::from_canonical_u64(AUX_COEFF_ABS_MAX as u64)); + let mut aux_limbs = read_value::(lv, MUL_AUX_INPUT_LO); + let aux_limbs_hi = &lv[MUL_AUX_INPUT_HI]; + for (lo, &hi) in aux_limbs.iter_mut().zip(aux_limbs_hi) { + //*lo = lo + hi * base - offset; + let t = builder.mul_sub_extension(hi, base, offset); + *lo = builder.add_extension(*lo, t); + } + aux_limbs + }; let mut constr_poly = pol_mul_lo_ext_circuit(builder, input0_limbs, input1_limbs); pol_sub_assign_ext_circuit(builder, &mut constr_poly, &output_limbs); diff --git a/evm/src/arithmetic/operations.rs b/evm/src/arithmetic/operations.rs new file mode 100644 index 00000000..8c89a45a --- /dev/null +++ b/evm/src/arithmetic/operations.rs @@ -0,0 +1,170 @@ +use ethereum_types::U256; +use plonky2::hash::hash_types::RichField; +use static_assertions::const_assert; + +use crate::arithmetic::columns::*; +use crate::arithmetic::{add, compare, modular, mul, sub}; + +#[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; + + // Each of these operations uses the same columns for input; the + // asserts ensure no-one changes this. + // TODO: This is ugly; should just remove the other + // *_INPUT_[01] variables and remove this. + debug_assert!([ADD_INPUT_0, SUB_INPUT_0, MUL_INPUT_0, CMP_INPUT_0,] + .iter() + .all(|x| *x == GENERAL_INPUT_0)); + debug_assert!([ADD_INPUT_1, SUB_INPUT_1, MUL_INPUT_1, CMP_INPUT_1,] + .iter() + .all(|x| *x == GENERAL_INPUT_1)); + + u256_to_array(&mut row[GENERAL_INPUT_0], self.input0); + u256_to_array(&mut row[GENERAL_INPUT_1], self.input1); + + // This is ugly, but it avoids the huge amount of boilerplate + // required to dispatch directly to each add/sub/etc. operation. + match self.op_filter { + IS_ADD => add::generate(&mut row), + IS_SUB => sub::generate(&mut row), + IS_MUL => mul::generate(&mut row), + IS_LT | IS_GT => compare::generate(&mut row, self.op_filter), + _ => panic!("unrecognised operation"), + } + + (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/sub.rs b/evm/src/arithmetic/sub.rs index 13f6e8d5..e94e2d5c 100644 --- a/evm/src/arithmetic/sub.rs +++ b/evm/src/arithmetic/sub.rs @@ -8,7 +8,6 @@ use crate::arithmetic::add::{eval_ext_circuit_are_equal, eval_packed_generic_are use crate::arithmetic::columns::*; use crate::arithmetic::utils::read_value_u64_limbs; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::range_check_error; pub(crate) fn u256_sub_br(input0: [u64; N_LIMBS], input1: [u64; N_LIMBS]) -> ([u64; N_LIMBS], u64) { const LIMB_BOUNDARY: u64 = 1 << LIMB_BITS; @@ -28,7 +27,7 @@ pub(crate) fn u256_sub_br(input0: [u64; N_LIMBS], input1: [u64; N_LIMBS]) -> ([u (output, br) } -pub fn generate(lv: &mut [F; NUM_ARITH_COLUMNS]) { +pub fn generate(lv: &mut [F]) { let input0 = read_value_u64_limbs(lv, SUB_INPUT_0); let input1 = read_value_u64_limbs(lv, SUB_INPUT_1); @@ -41,10 +40,6 @@ pub fn eval_packed_generic( lv: &[P; NUM_ARITH_COLUMNS], yield_constr: &mut ConstraintConsumer

, ) { - range_check_error!(SUB_INPUT_0, 16); - range_check_error!(SUB_INPUT_1, 16); - range_check_error!(SUB_OUTPUT, 16); - let is_sub = lv[IS_SUB]; let input0_limbs = &lv[SUB_INPUT_0]; let input1_limbs = &lv[SUB_INPUT_1]; diff --git a/evm/src/arithmetic/utils.rs b/evm/src/arithmetic/utils.rs index ec989c94..7eb33099 100644 --- a/evm/src/arithmetic/utils.rs +++ b/evm/src/arithmetic/utils.rs @@ -5,58 +5,7 @@ use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::plonk::circuit_builder::CircuitBuilder; -use crate::arithmetic::columns::{NUM_ARITH_COLUMNS, N_LIMBS}; - -/// Emit an error message regarding unchecked range assumptions. -/// Assumes the values in `cols` are `[cols[0], cols[0] + 1, ..., -/// cols[0] + cols.len() - 1]`. -/// -/// TODO: Hamish to delete this when he has implemented and integrated -/// range checks. -pub(crate) fn _range_check_error( - _file: &str, - _line: u32, - _cols: Range, - _signedness: &str, -) { - // error!( - // "{}:{}: arithmetic unit skipped {}-bit {} range-checks on columns {}--{}: not yet implemented", - // line, - // file, - // RC_BITS, - // signedness, - // cols.start, - // cols.end - 1, - // ); -} - -#[macro_export] -macro_rules! range_check_error { - ($cols:ident, $rc_bits:expr) => { - $crate::arithmetic::utils::_range_check_error::<$rc_bits>( - file!(), - line!(), - $cols, - "unsigned", - ); - }; - ($cols:ident, $rc_bits:expr, signed) => { - $crate::arithmetic::utils::_range_check_error::<$rc_bits>( - file!(), - line!(), - $cols, - "signed", - ); - }; - ([$cols:ident], $rc_bits:expr) => { - $crate::arithmetic::utils::_range_check_error::<$rc_bits>( - file!(), - line!(), - &[$cols], - "unsigned", - ); - }; -} +use crate::arithmetic::columns::N_LIMBS; /// Return an array of `N` zeros of type T. pub(crate) fn pol_zero() -> [T; N] @@ -139,11 +88,11 @@ pub(crate) fn pol_sub_ext_circuit, const D: usize>( b: [ExtensionTarget; N_LIMBS], ) -> [ExtensionTarget; 2 * N_LIMBS - 1] { let zero = builder.zero_extension(); - let mut sum = [zero; 2 * N_LIMBS - 1]; + let mut diff = [zero; 2 * N_LIMBS - 1]; for i in 0..N_LIMBS { - sum[i] = builder.sub_extension(a[i], b[i]); + diff[i] = builder.sub_extension(a[i], b[i]); } - sum + diff } /// a(x) -= b(x), but must have deg(a) >= deg(b). @@ -186,19 +135,13 @@ where res } -pub(crate) fn pol_mul_wide_ext_circuit< - F: RichField + Extendable, - const D: usize, - const M: usize, - const N: usize, - const P: usize, ->( +pub(crate) fn pol_mul_wide_ext_circuit, const D: usize>( builder: &mut CircuitBuilder, - a: [ExtensionTarget; M], - b: [ExtensionTarget; N], -) -> [ExtensionTarget; P] { + a: [ExtensionTarget; N_LIMBS], + b: [ExtensionTarget; N_LIMBS], +) -> [ExtensionTarget; 2 * N_LIMBS - 1] { let zero = builder.zero_extension(); - let mut res = [zero; P]; + let mut res = [zero; 2 * N_LIMBS - 1]; for (i, &ai) in a.iter().enumerate() { for (j, &bj) in b.iter().enumerate() { res[i + j] = builder.mul_add_extension(ai, bj, res[i + j]); @@ -322,8 +265,8 @@ pub(crate) fn pol_adjoin_root_ext_circuit< ) -> [ExtensionTarget; N] { let zero = builder.zero_extension(); let mut res = [zero; N]; - // res[deg] = NEG_ONE * root * a[0] + ZERO * zero - res[0] = builder.arithmetic_extension(F::NEG_ONE, F::ZERO, root, a[0], zero); + // res[0] = NEG_ONE * root * a[0] + ZERO * zero + res[0] = builder.mul_extension_with_const(F::NEG_ONE, root, a[0]); for deg in 1..N { // res[deg] = NEG_ONE * root * a[deg] + ONE * a[deg - 1] res[deg] = builder.arithmetic_extension(F::NEG_ONE, F::ONE, root, a[deg], a[deg - 1]); @@ -368,10 +311,7 @@ where /// Read the range `value_idxs` of values from `lv` into an array of /// length `N`. Panics if the length of the range is not `N`. -pub(crate) fn read_value( - lv: &[T; NUM_ARITH_COLUMNS], - value_idxs: Range, -) -> [T; N] { +pub(crate) fn read_value(lv: &[T], value_idxs: Range) -> [T; N] { lv[value_idxs].try_into().unwrap() } @@ -379,7 +319,7 @@ pub(crate) fn read_value( /// length `N`, interpreting the values as `u64`s. Panics if the /// length of the range is not `N`. pub(crate) fn read_value_u64_limbs( - lv: &[F; NUM_ARITH_COLUMNS], + lv: &[F], value_idxs: Range, ) -> [u64; N] { let limbs: [_; N] = lv[value_idxs].try_into().unwrap(); @@ -390,7 +330,7 @@ pub(crate) fn read_value_u64_limbs( /// 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( - lv: &[F; NUM_ARITH_COLUMNS], + lv: &[F], value_idxs: Range, ) -> [i64; N] { let limbs: [_; N] = lv[value_idxs].try_into().unwrap(); diff --git a/evm/src/lookup.rs b/evm/src/lookup.rs index ae92e864..d7e12bac 100644 --- a/evm/src/lookup.rs +++ b/evm/src/lookup.rs @@ -116,13 +116,9 @@ pub fn permuted_cols(inputs: &[F], table: &[F]) -> (Vec, Vec } } - #[allow(clippy::needless_range_loop)] // indexing is just more natural here - for jj in j..n { - unused_table_vals.push(sorted_table[jj]); - } - for ii in i..n { - unused_table_inds.push(ii); - } + unused_table_vals.extend_from_slice(&sorted_table[j..n]); + unused_table_inds.extend(i..n); + for (ind, val) in unused_table_inds.into_iter().zip_eq(unused_table_vals) { permuted_table[ind] = val; } From 815113809a37392b33b0b654ef6ea831040a07f5 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Mon, 30 Jan 2023 08:43:48 -0800 Subject: [PATCH 2/4] TODO --- evm/src/witness/transition.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/evm/src/witness/transition.rs b/evm/src/witness/transition.rs index b3a5b795..6f007b22 100644 --- a/evm/src/witness/transition.rs +++ b/evm/src/witness/transition.rs @@ -254,6 +254,7 @@ fn try_perform_instruction(state: &mut GenerationState) -> Result<( fn log_kernel_instruction(state: &mut GenerationState, op: Operation) { let pc = state.registers.program_counter; + // TODO: This is affecting performance... let is_interesting_offset = KERNEL .offset_label(pc) .filter(|label| !label.starts_with("halt_pc")) From 137bc7856574a37271e5f0c1193df89662807675 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Mon, 30 Jan 2023 08:51:33 -0800 Subject: [PATCH 3/4] Prep for publishing to crates.io --- ecdsa/Cargo.toml | 10 ++++++---- ecdsa/src/curve/curve_msm.rs | 2 +- evm/Cargo.toml | 13 +++++++++---- evm/src/cpu/kernel/assembler.rs | 4 ++-- evm/src/cpu/kernel/keccak_util.rs | 8 ++++---- evm/src/cpu/kernel/tests/mpt/hash.rs | 4 ++-- evm/src/cpu/kernel/tests/mpt/insert.rs | 8 ++++---- evm/src/cpu/kernel/tests/mpt/load.rs | 2 +- evm/src/fixed_recursive_verifier.rs | 8 ++++---- evm/src/get_challenges.rs | 2 +- evm/src/memory/memory_stark.rs | 2 +- evm/src/permutation.rs | 2 +- evm/src/proof.rs | 4 ++-- evm/src/prover.rs | 2 +- evm/src/recursive_verifier.rs | 4 ++-- evm/src/witness/util.rs | 2 +- evm/tests/basic_smart_contract.rs | 4 ++-- evm/tests/simple_transfer.rs | 2 +- field/Cargo.toml | 4 +++- insertion/Cargo.toml | 4 ++-- maybe_rayon/Cargo.toml | 4 +++- plonky2/Cargo.toml | 15 +++++++-------- plonky2/examples/bench_recursion.rs | 2 +- plonky2/src/fri/oracle.rs | 2 +- plonky2/src/fri/prover.rs | 2 +- plonky2/src/gadgets/interpolation.rs | 2 ++ plonky2/src/gadgets/random_access.rs | 2 +- plonky2/src/hash/merkle_tree.rs | 4 ++-- plonky2/src/plonk/permutation_argument.rs | 2 +- plonky2/src/plonk/proof.rs | 2 +- plonky2/src/plonk/prover.rs | 2 +- plonky2/src/recursion/dummy_circuit.rs | 1 + plonky2/src/util/serialization.rs | 2 ++ starky/Cargo.toml | 12 +++++++++--- starky/src/permutation.rs | 2 +- starky/src/proof.rs | 2 +- starky/src/prover.rs | 2 +- system_zero/Cargo.toml | 6 +++--- u32/Cargo.toml | 7 +++++-- util/Cargo.toml | 1 + waksman/Cargo.toml | 6 +++--- 41 files changed, 98 insertions(+), 73 deletions(-) diff --git a/ecdsa/Cargo.toml b/ecdsa/Cargo.toml index ed84d5de..04dbd19c 100644 --- a/ecdsa/Cargo.toml +++ b/ecdsa/Cargo.toml @@ -1,18 +1,20 @@ [package] name = "plonky2_ecdsa" +description = "ECDSA gadget for Plonky2" version = "0.1.0" +license = "MIT OR Apache-2.0" edition = "2021" [features] -parallel = ["maybe_rayon/parallel", "plonky2/parallel"] +parallel = ["plonky2_maybe_rayon/parallel", "plonky2/parallel"] [dependencies] anyhow = { version = "1.0.40", default-features = false } itertools = { version = "0.10.0", default-features = false } -maybe_rayon = { path = "../maybe_rayon", default-features = false } +plonky2_maybe_rayon = { version = "0.1.0", default-features = false } num = { version = "0.4.0", default-features = false } -plonky2 = { path = "../plonky2", default-features = false } -plonky2_u32 = { path = "../u32", default-features = false } +plonky2 = { version = "0.1.1", default-features = false } +plonky2_u32 = { version = "0.1.0", default-features = false } serde = { version = "1.0", default-features = false, features = ["derive"] } [dev-dependencies] diff --git a/ecdsa/src/curve/curve_msm.rs b/ecdsa/src/curve/curve_msm.rs index 21bcc404..9faa4a79 100644 --- a/ecdsa/src/curve/curve_msm.rs +++ b/ecdsa/src/curve/curve_msm.rs @@ -1,8 +1,8 @@ use alloc::vec::Vec; use itertools::Itertools; -use maybe_rayon::*; use plonky2::field::types::{Field, PrimeField}; +use plonky2_maybe_rayon::*; use crate::curve::curve_summation::affine_multisummation_best; use crate::curve::curve_types::{AffinePoint, Curve, ProjectivePoint}; diff --git a/evm/Cargo.toml b/evm/Cargo.toml index f6494d7a..f2c192c8 100644 --- a/evm/Cargo.toml +++ b/evm/Cargo.toml @@ -2,6 +2,11 @@ name = "plonky2_evm" description = "Implementation of STARKs for the Ethereum Virtual Machine" version = "0.1.0" +authors = ["Daniel Lubarov ", "William Borgeaud "] +readme = "README.md" +repository = "https://github.com/mir-protocol/plonky2" +keywords = ["EVM", "STARK", "Ethereum"] +categories = ["cryptography"] edition = "2021" [dependencies] @@ -15,13 +20,13 @@ hex-literal = "0.3.4" itertools = "0.10.3" keccak-hash = "0.10.0" log = "0.4.14" -maybe_rayon = { path = "../maybe_rayon" } +plonky2_maybe_rayon = "0.1.0" num = "0.4.0" once_cell = "1.13.0" pest = "2.1.3" pest_derive = "2.1.0" -plonky2 = { path = "../plonky2", default-features = false, features = ["timing"] } -plonky2_util = { path = "../util" } +plonky2 = { version = "0.1.1", default-features = false, features = ["timing"] } +plonky2_util = { version = "0.1.0" } rand = "0.8.5" rand_chacha = "0.3.1" rlp = "0.5.1" @@ -42,7 +47,7 @@ sha2 = "0.10.6" [features] default = ["parallel"] asmtools = ["hex"] -parallel = ["plonky2/parallel", "maybe_rayon/parallel"] +parallel = ["plonky2/parallel", "plonky2_maybe_rayon/parallel"] [[bin]] name = "assemble" diff --git a/evm/src/cpu/kernel/assembler.rs b/evm/src/cpu/kernel/assembler.rs index e9432f66..15fb1d4b 100644 --- a/evm/src/cpu/kernel/assembler.rs +++ b/evm/src/cpu/kernel/assembler.rs @@ -41,8 +41,8 @@ impl Kernel { prover_inputs: HashMap, ) -> Self { let code_hash_bytes = keccak(&code).0; - let code_hash = std::array::from_fn(|i| { - u32::from_le_bytes(std::array::from_fn(|j| code_hash_bytes[i * 4 + j])) + let code_hash = core::array::from_fn(|i| { + u32::from_le_bytes(core::array::from_fn(|j| code_hash_bytes[i * 4 + j])) }); let ordered_labels = global_labels .keys() diff --git a/evm/src/cpu/kernel/keccak_util.rs b/evm/src/cpu/kernel/keccak_util.rs index 38361389..18730075 100644 --- a/evm/src/cpu/kernel/keccak_util.rs +++ b/evm/src/cpu/kernel/keccak_util.rs @@ -4,13 +4,13 @@ use crate::keccak_sponge::columns::{KECCAK_WIDTH_BYTES, KECCAK_WIDTH_U32S}; /// Like tiny-keccak's `keccakf`, but deals with `u32` limbs instead of `u64` limbs. pub(crate) fn keccakf_u32s(state_u32s: &mut [u32; KECCAK_WIDTH_U32S]) { - let mut state_u64s: [u64; 25] = std::array::from_fn(|i| { + let mut state_u64s: [u64; 25] = core::array::from_fn(|i| { let lo = state_u32s[i * 2] as u64; let hi = state_u32s[i * 2 + 1] as u64; lo | (hi << 32) }); keccakf(&mut state_u64s); - *state_u32s = std::array::from_fn(|i| { + *state_u32s = core::array::from_fn(|i| { let u64_limb = state_u64s[i / 2]; let is_hi = i % 2; (u64_limb >> (is_hi * 32)) as u32 @@ -20,9 +20,9 @@ pub(crate) fn keccakf_u32s(state_u32s: &mut [u32; KECCAK_WIDTH_U32S]) { /// Like tiny-keccak's `keccakf`, but deals with bytes instead of `u64` limbs. pub(crate) fn keccakf_u8s(state_u8s: &mut [u8; KECCAK_WIDTH_BYTES]) { let mut state_u64s: [u64; 25] = - std::array::from_fn(|i| u64::from_le_bytes(state_u8s[i * 8..][..8].try_into().unwrap())); + core::array::from_fn(|i| u64::from_le_bytes(state_u8s[i * 8..][..8].try_into().unwrap())); keccakf(&mut state_u64s); - *state_u8s = std::array::from_fn(|i| { + *state_u8s = core::array::from_fn(|i| { let u64_limb = state_u64s[i / 8]; u64_limb.to_le_bytes()[i % 8] }); diff --git a/evm/src/cpu/kernel/tests/mpt/hash.rs b/evm/src/cpu/kernel/tests/mpt/hash.rs index 6c6c6f63..1b49dafa 100644 --- a/evm/src/cpu/kernel/tests/mpt/hash.rs +++ b/evm/src/cpu/kernel/tests/mpt/hash.rs @@ -24,7 +24,7 @@ fn mpt_hash_empty() -> Result<()> { #[test] fn mpt_hash_empty_branch() -> Result<()> { - let children = std::array::from_fn(|_| PartialTrie::Empty.into()); + let children = core::array::from_fn(|_| PartialTrie::Empty.into()); let state_trie = PartialTrie::Branch { children, value: vec![], @@ -85,7 +85,7 @@ fn mpt_hash_branch_to_leaf() -> Result<()> { value: test_account_2_rlp(), } .into(); - let mut children = std::array::from_fn(|_| PartialTrie::Empty.into()); + let mut children = core::array::from_fn(|_| PartialTrie::Empty.into()); children[3] = leaf; let state_trie = PartialTrie::Branch { children, diff --git a/evm/src/cpu/kernel/tests/mpt/insert.rs b/evm/src/cpu/kernel/tests/mpt/insert.rs index cf546969..6d6d6951 100644 --- a/evm/src/cpu/kernel/tests/mpt/insert.rs +++ b/evm/src/cpu/kernel/tests/mpt/insert.rs @@ -66,7 +66,7 @@ fn mpt_insert_leaf_leaf_key_extends_insert_key() -> Result<()> { #[test] fn mpt_insert_branch_replacing_empty_child() -> Result<()> { - let children = std::array::from_fn(|_| PartialTrie::Empty.into()); + let children = core::array::from_fn(|_| PartialTrie::Empty.into()); let state_trie = PartialTrie::Branch { children, value: vec![], @@ -81,7 +81,7 @@ fn mpt_insert_branch_replacing_empty_child() -> Result<()> { #[ignore] fn mpt_insert_extension_nonoverlapping_keys() -> Result<()> { // Existing keys are 0xABC, 0xABCDEF; inserted key is 0x12345. - let mut children = std::array::from_fn(|_| PartialTrie::Empty.into()); + let mut children = core::array::from_fn(|_| PartialTrie::Empty.into()); children[0xD] = PartialTrie::Leaf { nibbles: 0xEF_u64.into(), value: test_account_1_rlp(), @@ -104,7 +104,7 @@ fn mpt_insert_extension_nonoverlapping_keys() -> Result<()> { #[ignore] fn mpt_insert_extension_insert_key_extends_node_key() -> Result<()> { // Existing keys are 0xA, 0xABCD; inserted key is 0xABCDEF. - let mut children = std::array::from_fn(|_| PartialTrie::Empty.into()); + let mut children = core::array::from_fn(|_| PartialTrie::Empty.into()); children[0xB] = PartialTrie::Leaf { nibbles: 0xCD_u64.into(), value: test_account_1_rlp(), @@ -129,7 +129,7 @@ fn mpt_insert_branch_to_leaf_same_key() -> Result<()> { } .into(); - let mut children = std::array::from_fn(|_| PartialTrie::Empty.into()); + let mut children = core::array::from_fn(|_| PartialTrie::Empty.into()); children[0] = leaf; let state_trie = PartialTrie::Branch { children, diff --git a/evm/src/cpu/kernel/tests/mpt/load.rs b/evm/src/cpu/kernel/tests/mpt/load.rs index 79b4918f..292de36b 100644 --- a/evm/src/cpu/kernel/tests/mpt/load.rs +++ b/evm/src/cpu/kernel/tests/mpt/load.rs @@ -134,7 +134,7 @@ fn load_all_mpts_hash() -> Result<()> { #[test] fn load_all_mpts_empty_branch() -> Result<()> { - let children = std::array::from_fn(|_| PartialTrie::Empty.into()); + let children = core::array::from_fn(|_| PartialTrie::Empty.into()); let state_trie = PartialTrie::Branch { children, value: vec![], diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 96dc3e93..344b6d1c 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -178,15 +178,15 @@ where stark_config: &StarkConfig, ) -> RootCircuitData { let inner_common_data: [_; NUM_TABLES] = - std::array::from_fn(|i| &by_table[i].final_circuits()[0].common); + core::array::from_fn(|i| &by_table[i].final_circuits()[0].common); let mut builder = CircuitBuilder::new(CircuitConfig::standard_recursion_config()); let recursive_proofs = - std::array::from_fn(|i| builder.add_virtual_proof_with_pis::(inner_common_data[i])); - let pis: [_; NUM_TABLES] = std::array::from_fn(|i| { + core::array::from_fn(|i| builder.add_virtual_proof_with_pis::(inner_common_data[i])); + let pis: [_; NUM_TABLES] = core::array::from_fn(|i| { PublicInputs::from_vec(&recursive_proofs[i].public_inputs, stark_config) }); - let index_verifier_data = std::array::from_fn(|_i| builder.add_virtual_target()); + let index_verifier_data = core::array::from_fn(|_i| builder.add_virtual_target()); let mut challenger = RecursiveChallenger::::new(&mut builder); for pi in &pis { diff --git a/evm/src/get_challenges.rs b/evm/src/get_challenges.rs index f368b7c2..7e6a1ba7 100644 --- a/evm/src/get_challenges.rs +++ b/evm/src/get_challenges.rs @@ -35,7 +35,7 @@ impl, C: GenericConfig, const D: usize> A let num_permutation_batch_sizes = all_stark.permutation_batch_sizes(); AllProofChallenges { - stark_challenges: std::array::from_fn(|i| { + stark_challenges: core::array::from_fn(|i| { challenger.compact(); self.stark_proofs[i].proof.get_challenges( &mut challenger, diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 8103f883..88dcbf40 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -1,7 +1,6 @@ use std::marker::PhantomData; use itertools::Itertools; -use maybe_rayon::*; use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::field::packed::PackedField; use plonky2::field::polynomial::PolynomialValues; @@ -10,6 +9,7 @@ use plonky2::hash::hash_types::RichField; use plonky2::timed; use plonky2::util::timing::TimingTree; use plonky2::util::transpose; +use plonky2_maybe_rayon::*; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cross_table_lookup::Column; diff --git a/evm/src/permutation.rs b/evm/src/permutation.rs index 4f42a4aa..64223ad7 100644 --- a/evm/src/permutation.rs +++ b/evm/src/permutation.rs @@ -3,7 +3,6 @@ use std::fmt::Debug; use itertools::Itertools; -use maybe_rayon::*; use plonky2::field::batch_util::batch_multiply_inplace; use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::field::packed::PackedField; @@ -17,6 +16,7 @@ use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher}; use plonky2::plonk::plonk_common::{reduce_with_powers, reduce_with_powers_ext_circuit}; use plonky2::util::reducing::{ReducingFactor, ReducingFactorTarget}; +use plonky2_maybe_rayon::*; use crate::config::StarkConfig; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; diff --git a/evm/src/proof.rs b/evm/src/proof.rs index 7025697f..d0f56873 100644 --- a/evm/src/proof.rs +++ b/evm/src/proof.rs @@ -1,6 +1,5 @@ use ethereum_types::{Address, H256, U256}; use itertools::Itertools; -use maybe_rayon::*; use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::fri::oracle::PolynomialBatch; use plonky2::fri::proof::{FriChallenges, FriChallengesTarget, FriProof, FriProofTarget}; @@ -13,6 +12,7 @@ use plonky2::hash::merkle_tree::MerkleCap; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::iop::target::Target; use plonky2::plonk::config::GenericConfig; +use plonky2_maybe_rayon::*; use serde::{Deserialize, Serialize}; use crate::all_stark::NUM_TABLES; @@ -29,7 +29,7 @@ pub struct AllProof, C: GenericConfig, co impl, C: GenericConfig, const D: usize> AllProof { pub fn degree_bits(&self, config: &StarkConfig) -> [usize; NUM_TABLES] { - std::array::from_fn(|i| self.stark_proofs[i].proof.recover_degree_bits(config)) + core::array::from_fn(|i| self.stark_proofs[i].proof.recover_degree_bits(config)) } } diff --git a/evm/src/prover.rs b/evm/src/prover.rs index 8a293fb4..b4ea0d90 100644 --- a/evm/src/prover.rs +++ b/evm/src/prover.rs @@ -2,7 +2,6 @@ use std::any::type_name; use anyhow::{ensure, Result}; use itertools::Itertools; -use maybe_rayon::*; use once_cell::sync::Lazy; use plonky2::field::extension::Extendable; use plonky2::field::packable::Packable; @@ -17,6 +16,7 @@ use plonky2::plonk::config::{GenericConfig, Hasher}; use plonky2::timed; use plonky2::util::timing::TimingTree; use plonky2::util::transpose; +use plonky2_maybe_rayon::*; use plonky2_util::{log2_ceil, log2_strict}; use crate::all_stark::{AllStark, Table, NUM_TABLES}; diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index 4de74921..1fba88e3 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -107,7 +107,7 @@ impl, C: GenericConfig, const D: usize> where [(); C::Hasher::HASH_SIZE]:, { - let pis: [_; NUM_TABLES] = std::array::from_fn(|i| { + let pis: [_; NUM_TABLES] = core::array::from_fn(|i| { PublicInputs::from_vec(&self.recursive_proofs[i].public_inputs, inner_config) }); @@ -279,7 +279,7 @@ where num_permutation_zs, ); - let init_challenger_state_target = std::array::from_fn(|_| builder.add_virtual_public_input()); + let init_challenger_state_target = core::array::from_fn(|_| builder.add_virtual_public_input()); let mut challenger = RecursiveChallenger::::from_state(init_challenger_state_target); let challenges = proof_target.get_challenges::( diff --git a/evm/src/witness/util.rs b/evm/src/witness/util.rs index 74303359..74970553 100644 --- a/evm/src/witness/util.rs +++ b/evm/src/witness/util.rs @@ -140,7 +140,7 @@ pub(crate) fn stack_pop_with_log_and_fill( return Err(ProgramError::StackUnderflow); } - let result = std::array::from_fn(|i| { + let result = core::array::from_fn(|i| { let address = MemoryAddress::new( state.registers.context, Segment::Stack, diff --git a/evm/tests/basic_smart_contract.rs b/evm/tests/basic_smart_contract.rs index 44b7fb4d..e52b7a76 100644 --- a/evm/tests/basic_smart_contract.rs +++ b/evm/tests/basic_smart_contract.rs @@ -56,7 +56,7 @@ fn test_basic_smart_contract() -> anyhow::Result<()> { }; let state_trie_before = { - let mut children = std::array::from_fn(|_| PartialTrie::Empty.into()); + let mut children = core::array::from_fn(|_| PartialTrie::Empty.into()); children[sender_nibbles.get_nibble(0) as usize] = PartialTrie::Leaf { nibbles: sender_nibbles.truncate_n_nibbles_front(1), value: rlp::encode(&sender_account_before).to_vec(), @@ -110,7 +110,7 @@ fn test_basic_smart_contract() -> anyhow::Result<()> { ..to_account_before }; - let mut children = std::array::from_fn(|_| PartialTrie::Empty.into()); + let mut children = core::array::from_fn(|_| PartialTrie::Empty.into()); children[sender_nibbles.get_nibble(0) as usize] = PartialTrie::Leaf { nibbles: sender_nibbles.truncate_n_nibbles_front(1), value: rlp::encode(&sender_account_after).to_vec(), diff --git a/evm/tests/simple_transfer.rs b/evm/tests/simple_transfer.rs index 72801388..2d27abfe 100644 --- a/evm/tests/simple_transfer.rs +++ b/evm/tests/simple_transfer.rs @@ -83,7 +83,7 @@ fn test_simple_transfer() -> anyhow::Result<()> { ..AccountRlp::default() }; - let mut children = std::array::from_fn(|_| PartialTrie::Empty.into()); + let mut children = core::array::from_fn(|_| PartialTrie::Empty.into()); children[sender_nibbles.get_nibble(0) as usize] = PartialTrie::Leaf { nibbles: sender_nibbles.truncate_n_nibbles_front(1), value: rlp::encode(&sender_account_after).to_vec(), diff --git a/field/Cargo.toml b/field/Cargo.toml index 1242dfe3..afc084e8 100644 --- a/field/Cargo.toml +++ b/field/Cargo.toml @@ -2,13 +2,15 @@ name = "plonky2_field" description = "Finite field arithmetic" version = "0.1.0" +license = "MIT OR Apache-2.0" +authors = ["Daniel Lubarov ", "William Borgeaud ", "Jacqueline Nabaglo ", "Hamish Ivey-Law "] edition = "2021" [dependencies] anyhow = { version = "1.0.40", default-features = false } itertools = { version = "0.10.0", default-features = false, features = ["use_alloc"] } num = { version = "0.4", default-features = false, features = ["alloc", "rand"] } -plonky2_util = { path = "../util", default-features = false } +plonky2_util = { version = "0.1.0", default-features = false } rand = { version = "0.8.5", default-features = false, features = ["getrandom"] } serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } static_assertions = { version = "1.1.0", default-features = false } diff --git a/insertion/Cargo.toml b/insertion/Cargo.toml index c007a975..e39e2bd3 100644 --- a/insertion/Cargo.toml +++ b/insertion/Cargo.toml @@ -6,8 +6,8 @@ edition = "2021" [dependencies] anyhow = { version = "1.0.40", default-features = false } -plonky2 = { path = "../plonky2", default-features = false } +plonky2 = { version = "0.1.1", default-features = false } [dev-dependencies] -plonky2 = { path = "../plonky2" } +plonky2 = { version = "0.1.1" } diff --git a/maybe_rayon/Cargo.toml b/maybe_rayon/Cargo.toml index b3c1e78a..11eebc96 100644 --- a/maybe_rayon/Cargo.toml +++ b/maybe_rayon/Cargo.toml @@ -1,5 +1,7 @@ [package] -name = "maybe_rayon" +name = "plonky2_maybe_rayon" +description = "Feature-gated wrapper around rayon" +license = "MIT OR Apache-2.0" version = "0.1.0" edition = "2021" diff --git a/plonky2/Cargo.toml b/plonky2/Cargo.toml index 9474095e..6cbc1b6b 100644 --- a/plonky2/Cargo.toml +++ b/plonky2/Cargo.toml @@ -1,19 +1,19 @@ [package] name = "plonky2" description = "Recursive SNARKs based on PLONK and FRI" -version = "0.1.0" -authors = ["Polygon Zero "] +version = "0.1.1" +license = "MIT OR Apache-2.0" +authors = ["Daniel Lubarov ", "William Borgeaud ", "Nicholas Ward "] readme = "README.md" repository = "https://github.com/mir-protocol/plonky2" keywords = ["cryptography", "SNARK", "PLONK", "FRI"] categories = ["cryptography"] edition = "2021" -default-run = "generate_constants" [features] default = ["gate_testing", "parallel", "rand_chacha", "std", "timing"] gate_testing = [] -parallel = ["hashbrown/rayon", "maybe_rayon/parallel"] +parallel = ["hashbrown/rayon", "plonky2_maybe_rayon/parallel"] std = ["anyhow/std", "rand/std"] timing = ["std"] @@ -24,10 +24,10 @@ hashbrown = { version = "0.12.3", default-features = false, features = ["ahash", itertools = { version = "0.10.0", default-features = false } keccak-hash = { version = "0.8.0", default-features = false } log = { version = "0.4.14", default-features = false } -maybe_rayon = { path = "../maybe_rayon", default-features = false } +plonky2_maybe_rayon = { version = "0.1.0", default-features = false } num = { version = "0.4", default-features = false, features = ["rand"] } -plonky2_field = { path = "../field", default-features = false } -plonky2_util = { path = "../util", default-features = false } +plonky2_field = { version = "0.1.0", default-features = false } +plonky2_util = { version = "0.1.0", default-features = false } rand = { version = "0.8.4", default-features = false } rand_chacha = { version = "0.3.1", optional = true, default-features = false } serde = { version = "1.0", default-features = false, features = ["derive"] } @@ -38,7 +38,6 @@ unroll = { version = "0.1.5", default-features = false } criterion = { version = "0.4.0", default-features = false } env_logger = { version = "0.9.0", default-features = false } num_cpus = { version = "1.14.0", default-features = false } -plonky2 = { path = "." } rand = { version = "0.8.4", default-features = false, features = ["getrandom"] } rand_chacha = { version = "0.3.1", default-features = false } serde_cbor = { version = "0.11.2" } diff --git a/plonky2/examples/bench_recursion.rs b/plonky2/examples/bench_recursion.rs index bf6b0e6b..5a1196e1 100644 --- a/plonky2/examples/bench_recursion.rs +++ b/plonky2/examples/bench_recursion.rs @@ -9,7 +9,6 @@ use core::str::FromStr; use anyhow::{anyhow, Context as _, Result}; use log::{info, Level, LevelFilter}; -use maybe_rayon::rayon; use plonky2::gates::noop::NoopGate; use plonky2::hash::hash_types::RichField; use plonky2::iop::witness::{PartialWitness, WitnessWrite}; @@ -20,6 +19,7 @@ use plonky2::plonk::proof::{CompressedProofWithPublicInputs, ProofWithPublicInpu use plonky2::plonk::prover::prove; use plonky2::util::timing::TimingTree; use plonky2_field::extension::Extendable; +use plonky2_maybe_rayon::rayon; use rand::rngs::OsRng; use rand::{RngCore, SeedableRng}; use rand_chacha::ChaCha8Rng; diff --git a/plonky2/src/fri/oracle.rs b/plonky2/src/fri/oracle.rs index 90890134..cddcf048 100644 --- a/plonky2/src/fri/oracle.rs +++ b/plonky2/src/fri/oracle.rs @@ -2,8 +2,8 @@ use alloc::format; use alloc::vec::Vec; use itertools::Itertools; -use maybe_rayon::*; use plonky2_field::types::Field; +use plonky2_maybe_rayon::*; use crate::field::extension::Extendable; use crate::field::fft::FftRootTable; diff --git a/plonky2/src/fri/prover.rs b/plonky2/src/fri/prover.rs index e65a9731..9e933528 100644 --- a/plonky2/src/fri/prover.rs +++ b/plonky2/src/fri/prover.rs @@ -1,6 +1,6 @@ use alloc::vec::Vec; -use maybe_rayon::*; +use plonky2_maybe_rayon::*; use crate::field::extension::{flatten, unflatten, Extendable}; use crate::field::polynomial::{PolynomialCoeffs, PolynomialValues}; diff --git a/plonky2/src/gadgets/interpolation.rs b/plonky2/src/gadgets/interpolation.rs index 1ab35660..daf51d21 100644 --- a/plonky2/src/gadgets/interpolation.rs +++ b/plonky2/src/gadgets/interpolation.rs @@ -1,3 +1,5 @@ +use alloc::vec; + use plonky2_field::extension::Extendable; use crate::gates::coset_interpolation::CosetInterpolationGate; diff --git a/plonky2/src/gadgets/random_access.rs b/plonky2/src/gadgets/random_access.rs index 73e3de8c..85d2c714 100644 --- a/plonky2/src/gadgets/random_access.rs +++ b/plonky2/src/gadgets/random_access.rs @@ -57,7 +57,7 @@ impl, const D: usize> CircuitBuilder { access_index: Target, v: Vec, ) -> HashOutTarget { - let selected = std::array::from_fn(|i| { + let selected = core::array::from_fn(|i| { self.random_access( access_index, v.iter().map(|hash| hash.elements[i]).collect(), diff --git a/plonky2/src/hash/merkle_tree.rs b/plonky2/src/hash/merkle_tree.rs index 7a9cd1f3..e0c8e79d 100644 --- a/plonky2/src/hash/merkle_tree.rs +++ b/plonky2/src/hash/merkle_tree.rs @@ -2,7 +2,7 @@ use alloc::vec::Vec; use core::mem::MaybeUninit; use core::slice; -use maybe_rayon::*; +use plonky2_maybe_rayon::*; use serde::{Deserialize, Serialize}; use crate::hash::hash_types::RichField; @@ -84,7 +84,7 @@ fn fill_subtree>( // Split `leaves` between both children. let (left_leaves, right_leaves) = leaves.split_at(leaves.len() / 2); - let (left_digest, right_digest) = maybe_rayon::join( + let (left_digest, right_digest) = plonky2_maybe_rayon::join( || fill_subtree::(left_digests_buf, left_leaves), || fill_subtree::(right_digests_buf, right_leaves), ); diff --git a/plonky2/src/plonk/permutation_argument.rs b/plonky2/src/plonk/permutation_argument.rs index 7052cabe..f8d07d4a 100644 --- a/plonky2/src/plonk/permutation_argument.rs +++ b/plonky2/src/plonk/permutation_argument.rs @@ -1,7 +1,7 @@ use alloc::vec::Vec; use hashbrown::HashMap; -use maybe_rayon::*; +use plonky2_maybe_rayon::*; use crate::field::polynomial::PolynomialValues; use crate::field::types::Field; diff --git a/plonky2/src/plonk/proof.rs b/plonky2/src/plonk/proof.rs index fb9e6cde..ea616e8f 100644 --- a/plonky2/src/plonk/proof.rs +++ b/plonky2/src/plonk/proof.rs @@ -2,7 +2,7 @@ use alloc::vec; use alloc::vec::Vec; use anyhow::ensure; -use maybe_rayon::*; +use plonky2_maybe_rayon::*; use serde::{Deserialize, Serialize}; use crate::field::extension::Extendable; diff --git a/plonky2/src/plonk/prover.rs b/plonky2/src/plonk/prover.rs index d45fa573..f8f66ea1 100644 --- a/plonky2/src/plonk/prover.rs +++ b/plonky2/src/plonk/prover.rs @@ -3,7 +3,7 @@ use alloc::{format, vec}; use core::mem::swap; use anyhow::{ensure, Result}; -use maybe_rayon::*; +use plonky2_maybe_rayon::*; use crate::field::extension::Extendable; use crate::field::polynomial::{PolynomialCoeffs, PolynomialValues}; diff --git a/plonky2/src/recursion/dummy_circuit.rs b/plonky2/src/recursion/dummy_circuit.rs index 38f51aea..c8b98a96 100644 --- a/plonky2/src/recursion/dummy_circuit.rs +++ b/plonky2/src/recursion/dummy_circuit.rs @@ -1,4 +1,5 @@ use alloc::vec; +use alloc::vec::Vec; use hashbrown::HashMap; use plonky2_field::extension::Extendable; diff --git a/plonky2/src/util/serialization.rs b/plonky2/src/util/serialization.rs index c3ad0887..b33ec52e 100644 --- a/plonky2/src/util/serialization.rs +++ b/plonky2/src/util/serialization.rs @@ -804,12 +804,14 @@ impl Buffer { } } +#[cfg(feature = "std")] impl Remaining for Buffer { fn remaining(&self) -> usize { self.bytes.len() - self.pos } } +#[cfg(feature = "std")] impl Read for Buffer { #[inline] fn read_exact(&mut self, bytes: &mut [u8]) -> IoResult<()> { diff --git a/starky/Cargo.toml b/starky/Cargo.toml index cdeb189a..4f459613 100644 --- a/starky/Cargo.toml +++ b/starky/Cargo.toml @@ -2,11 +2,17 @@ name = "starky" description = "Implementation of STARKs" version = "0.1.0" +license = "MIT OR Apache-2.0" +authors = ["Daniel Lubarov ", "William Borgeaud "] +readme = "README.md" +repository = "https://github.com/mir-protocol/plonky2" +keywords = ["cryptography", "STARK", "FRI"] +categories = ["cryptography"] edition = "2021" [features] default = ["parallel", "std", "timing"] -parallel = ["plonky2/parallel", "maybe_rayon/parallel"] +parallel = ["plonky2/parallel", "plonky2_maybe_rayon/parallel"] std = ["anyhow/std", "plonky2/std"] timing = ["plonky2/timing"] @@ -14,8 +20,8 @@ timing = ["plonky2/timing"] anyhow = { version = "1.0.40", default-features = false } itertools = { version = "0.10.0", default-features = false } log = { version = "0.4.14", default-features = false } -maybe_rayon = { path = "../maybe_rayon", default-features = false } -plonky2 = { path = "../plonky2", default-features = false } +plonky2_maybe_rayon = { version = "0.1.0", default-features = false } +plonky2 = { version = "0.1.1", default-features = false } [dev-dependencies] env_logger = { version = "0.9.0", default-features = false } diff --git a/starky/src/permutation.rs b/starky/src/permutation.rs index ee4225f3..bba42712 100644 --- a/starky/src/permutation.rs +++ b/starky/src/permutation.rs @@ -4,7 +4,6 @@ use alloc::vec; use alloc::vec::Vec; use itertools::Itertools; -use maybe_rayon::*; use plonky2::field::batch_util::batch_multiply_inplace; use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::field::packed::PackedField; @@ -17,6 +16,7 @@ use plonky2::iop::target::Target; use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher}; use plonky2::util::reducing::{ReducingFactor, ReducingFactorTarget}; +use plonky2_maybe_rayon::*; use crate::config::StarkConfig; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; diff --git a/starky/src/proof.rs b/starky/src/proof.rs index 86093857..6bd5f787 100644 --- a/starky/src/proof.rs +++ b/starky/src/proof.rs @@ -2,7 +2,6 @@ use alloc::vec; use alloc::vec::Vec; use itertools::Itertools; -use maybe_rayon::*; use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::fri::oracle::PolynomialBatch; use plonky2::fri::proof::{ @@ -16,6 +15,7 @@ use plonky2::hash::merkle_tree::MerkleCap; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::iop::target::Target; use plonky2::plonk::config::GenericConfig; +use plonky2_maybe_rayon::*; use crate::config::StarkConfig; use crate::permutation::PermutationChallengeSet; diff --git a/starky/src/prover.rs b/starky/src/prover.rs index dc445f24..2350830c 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -3,7 +3,6 @@ use core::iter::once; use anyhow::{ensure, Result}; use itertools::Itertools; -use maybe_rayon::*; use plonky2::field::extension::Extendable; use plonky2::field::packable::Packable; use plonky2::field::packed::PackedField; @@ -17,6 +16,7 @@ use plonky2::plonk::config::{GenericConfig, Hasher}; use plonky2::timed; use plonky2::util::timing::TimingTree; use plonky2::util::{log2_ceil, log2_strict, transpose}; +use plonky2_maybe_rayon::*; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; diff --git a/system_zero/Cargo.toml b/system_zero/Cargo.toml index 03aaea20..b8604984 100644 --- a/system_zero/Cargo.toml +++ b/system_zero/Cargo.toml @@ -8,11 +8,11 @@ edition = "2021" anyhow = "1.0.40" itertools = "0.10.0" log = "0.4.14" -plonky2 = { path = "../plonky2" } -plonky2_util = { path = "../util" } +plonky2 = { version = "0.1.1" } +plonky2_util = { version = "0.1.0" } rand = "0.8.4" rand_chacha = "0.3.1" -starky = { path = "../starky" } +starky = { version = "0.1.0" } [dev-dependencies] criterion = "0.4.0" diff --git a/u32/Cargo.toml b/u32/Cargo.toml index 273db263..0fd11a4a 100644 --- a/u32/Cargo.toml +++ b/u32/Cargo.toml @@ -1,14 +1,17 @@ [package] name = "plonky2_u32" +description = "u32 gadget for Plonky2" version = "0.1.0" +license = "MIT OR Apache-2.0" +repository = "https://github.com/mir-protocol/plonky2" edition = "2021" [dependencies] anyhow = { version = "1.0.40", default-features = false } itertools = { version = "0.10.0", default-features = false } num = { version = "0.4", default-features = false } -plonky2 = { path = "../plonky2", default-features = false } +plonky2 = { version = "0.1.1", default-features = false } [dev-dependencies] -plonky2 = { path = "../plonky2", default-features = false, features = ["gate_testing"] } +plonky2 = { version = "0.1.1", default-features = false, features = ["gate_testing"] } rand = { version = "0.8.4", default-features = false, features = ["getrandom"] } diff --git a/util/Cargo.toml b/util/Cargo.toml index 4419db2a..f3cb862e 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -2,6 +2,7 @@ name = "plonky2_util" description = "Utilities used by Plonky2" version = "0.1.0" +license = "MIT OR Apache-2.0" edition = "2021" [dev-dependencies] diff --git a/waksman/Cargo.toml b/waksman/Cargo.toml index 7be3f414..98d76a52 100644 --- a/waksman/Cargo.toml +++ b/waksman/Cargo.toml @@ -9,7 +9,7 @@ anyhow = "1.0.40" array_tool = "1.0.3" bimap = "0.6.1" itertools = "0.10.0" -"plonky2" = { path = "../plonky2" } -"plonky2_field" = { path = "../field" } -"plonky2_util" = { path = "../util" } +"plonky2" = { version = "0.1.0" } +"plonky2_field" = { version = "0.1.0" } +"plonky2_util" = { version = "0.1.0" } rand = "0.8.4" From ca002aeafaac920af9ea1828759c81188e7a9566 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 31 Jan 2023 19:23:54 +0100 Subject: [PATCH 4/4] Optimize `ecrecover` ASM (#840) * 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 * Minor * Hamish's suggestion * Working * Cleaning * Clippy * PR feedback * Minor PR feedback --- evm/src/cpu/kernel/aggregator.rs | 3 +- .../kernel/asm/curve/secp256k1/curve_add.asm | 73 +- .../kernel/asm/curve/secp256k1/curve_mul.asm | 71 -- .../kernel/asm/curve/secp256k1/ecrecover.asm | 130 +- .../cpu/kernel/asm/curve/secp256k1/glv.asm | 104 ++ .../asm/curve/secp256k1/precomputation.asm | 74 ++ evm/src/cpu/kernel/asm/util/basic_macros.asm | 2 +- evm/src/cpu/kernel/constants/mod.rs | 30 +- evm/src/cpu/kernel/interpreter.rs | 3 +- .../cpu/kernel/tests/{ => ecc}/curve_ops.rs | 70 +- .../cpu/kernel/tests/{ => ecc}/ecrecover.rs | 18 + .../cpu/kernel/tests/ecc/ecrecover_test_data | 184 +++ evm/src/cpu/kernel/tests/ecc/glv_test_data | 1048 +++++++++++++++++ evm/src/cpu/kernel/tests/ecc/mod.rs | 2 + evm/src/cpu/kernel/tests/mod.rs | 3 +- evm/src/memory/segments.rs | 6 +- 16 files changed, 1578 insertions(+), 243 deletions(-) delete mode 100644 evm/src/cpu/kernel/asm/curve/secp256k1/curve_mul.asm create mode 100644 evm/src/cpu/kernel/asm/curve/secp256k1/glv.asm create mode 100644 evm/src/cpu/kernel/asm/curve/secp256k1/precomputation.asm rename evm/src/cpu/kernel/tests/{ => ecc}/curve_ops.rs (81%) rename evm/src/cpu/kernel/tests/{ => ecc}/ecrecover.rs (82%) create mode 100644 evm/src/cpu/kernel/tests/ecc/ecrecover_test_data create mode 100644 evm/src/cpu/kernel/tests/ecc/glv_test_data create mode 100644 evm/src/cpu/kernel/tests/ecc/mod.rs diff --git a/evm/src/cpu/kernel/aggregator.rs b/evm/src/cpu/kernel/aggregator.rs index f8d2860e..0ff48666 100644 --- a/evm/src/cpu/kernel/aggregator.rs +++ b/evm/src/cpu/kernel/aggregator.rs @@ -28,12 +28,13 @@ pub(crate) fn combined_kernel() -> Kernel { include_str!("asm/curve/bn254/curve_mul.asm"), include_str!("asm/curve/bn254/moddiv.asm"), include_str!("asm/curve/common.asm"), - include_str!("asm/curve/secp256k1/curve_mul.asm"), include_str!("asm/curve/secp256k1/curve_add.asm"), include_str!("asm/curve/secp256k1/ecrecover.asm"), include_str!("asm/curve/secp256k1/inverse_scalar.asm"), include_str!("asm/curve/secp256k1/lift_x.asm"), include_str!("asm/curve/secp256k1/moddiv.asm"), + include_str!("asm/curve/secp256k1/glv.asm"), + include_str!("asm/curve/secp256k1/precomputation.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/secp256k1/curve_add.asm b/evm/src/cpu/kernel/asm/curve/secp256k1/curve_add.asm index 790fb116..17fd5b9a 100644 --- a/evm/src/cpu/kernel/asm/curve/secp256k1/curve_add.asm +++ b/evm/src/cpu/kernel/asm/curve/secp256k1/curve_add.asm @@ -15,7 +15,7 @@ global ec_add_valid_points_secp: %jumpi(ec_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 @@ -33,9 +33,9 @@ global ec_add_valid_points_secp: EQ // stack: x0 == x1, x0, y0, x1, y1, retdest %jumpi(ec_add_equal_first_coord) +// Standard affine addition formula. +global ec_add_valid_points_no_edge_case_secp: // stack: x0, y0, x1, y1, retdest - - // Otherwise, we can use the standard formula. // Compute lambda = (y0 - y1)/(x0 - x1) DUP4 // stack: y1, x0, y0, x1, y1, retdest @@ -174,66 +174,49 @@ ec_add_equal_first_coord: // Assumption: x0 == x1 and y0 == y1 // Standard doubling formula. ec_add_equal_points: - // stack: x0, y0, x1, y1, retdest - // Compute lambda = 3/2 * x0^2 / y0 - %secp_base - // stack: N, x0, y0, x1, y1, retdest - %secp_base - // stack: N, N, x0, y0, x1, y1, retdest - DUP3 - // stack: x0, N, N, x0, y0, x1, y1, retdest - DUP1 - // stack: x0, x0, N, N, x0, y0, x1, y1, retdest + %stack (x0, y0, x1, y1, retdest) -> (x0, x0, @SECP_BASE, @SECP_BASE, x0, y0, x1, y1, retdest) MULMOD - // stack: x0^2, N, x0, y0, x1, y1, retdest with PUSH 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffff7ffffe19 // 3/2 in the base field - // stack: 3/2, x0^2, N, x0, y0, x1, y1, retdest MULMOD - // stack: 3/2 * x0^2, x0, y0, x1, y1, retdest DUP3 - // stack: y0, 3/2 * x0^2, x0, y0, x1, y1, retdest %moddiv_secp_base - // stack: lambda, x0, y0, x1, y1, retdest %jump(ec_add_valid_points_with_lambda) // Secp256k1 elliptic curve doubling. -// Assumption: (x0,y0) is a valid point. +// Assumption: (x,y) is a valid point. // Standard doubling formula. global ec_double_secp: - // stack: x0, y0, retdest - DUP2 - // stack: y0, x0, y0, retdest - DUP2 - // stack: x0, y0, x0, y0, retdest - %jump(ec_add_equal_points) + // stack: x, y, retdest + DUP2 DUP2 %ec_isidentity + // stack: (x,y)==(0,0), x, y, retdest + %jumpi(retself) + + // Compute lambda = 3/2 * x0^2 / y0 + %stack (x, y, retdest) -> (x, x, @SECP_BASE, @SECP_BASE, x, y, retdest) + MULMOD + PUSH 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffff7ffffe19 // 3/2 in the base field + MULMOD + 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 // Push the order of the Secp256k1 scalar field. %macro secp_base - PUSH 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f + PUSH @SECP_BASE %endmacro -// Modular subtraction. Subtraction x-y underflows iff x (@SECP_BASE, y, x, @SECP_BASE) + SUB ADDMOD %endmacro // Check if (x,y) is a valid curve point. diff --git a/evm/src/cpu/kernel/asm/curve/secp256k1/curve_mul.asm b/evm/src/cpu/kernel/asm/curve/secp256k1/curve_mul.asm deleted file mode 100644 index 892d57c0..00000000 --- a/evm/src/cpu/kernel/asm/curve/secp256k1/curve_mul.asm +++ /dev/null @@ -1,71 +0,0 @@ -// Same algorithm as in `exp.asm` -global ec_mul_valid_point_secp: - // stack: x, y, s, retdest - %stack (x,y) -> (x,y,x,y) - %ec_isidentity - // stack: (x,y)==(0,0), x, y, s, retdest - %jumpi(ret_zero_ec_mul) - 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_secp) - -// 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_secp) - -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' - JUMP - -odd_scalar: - // stack: x', y', x, y, retdest - %jump(ec_add_valid_points_secp) diff --git a/evm/src/cpu/kernel/asm/curve/secp256k1/ecrecover.asm b/evm/src/cpu/kernel/asm/curve/secp256k1/ecrecover.asm index a1c2ff3c..3aa98948 100644 --- a/evm/src/cpu/kernel/asm/curve/secp256k1/ecrecover.asm +++ b/evm/src/cpu/kernel/asm/curve/secp256k1/ecrecover.asm @@ -6,19 +6,7 @@ global ecrecover: %ecrecover_input_check // stack: isValid(v,r,s), hash, v, r, s, retdest - // Lift r to an elliptic curve point if possible. - SWAP2 - // stack: v, hash, isValid(v,r,s), r, s, retdest - DUP4 - // stack: r, v, hash, isValid(v,r,s), r, s, retdest - - // Compute v-27 which gives the parity of the y-coordinate of the lifted point. - SWAP1 - // stack: v, r, hash, isValid(v,r,s), r, s, retdest - PUSH 27 - // stack: 27, v, r, hash, isValid(v,r,s), r, s, retdest - SWAP1 - // stack: v, 27, r, hash, isValid(v,r,s), r, s, retdest + %stack (valid, hash, v, r, s, retdest) -> (v, 27, r, hash, valid, r, s, retdest) SUB // stack: v - 27, r, hash, isValid(v,r,s), r, s, retdest SWAP1 @@ -62,70 +50,58 @@ ecrecover_valid_input: %mulmodn_secp_scalar // stack: u1, y, hash, x, r^(-1), retdest - - // Compute (X,Y) = u1 * (x,y) - PUSH ecrecover_with_first_point - // stack: ecrecover_with_first_point, u1, y, hash, x, r^(-1), retdest - SWAP1 - // stack: u1, ecrecover_with_first_point, y, hash, x, r^(-1), retdest - SWAP2 - // stack: y, ecrecover_with_first_point, u1, hash, x, r^(-1), retdest - SWAP1 - // stack: ecrecover_with_first_point, y, u1, hash, x, r^(-1), retdest - SWAP3 - // stack: hash, y, u1, ecrecover_with_first_point, x, r^(-1), retdest - SWAP4 - // stack: x, y, u1, ecrecover_with_first_point, hash, r^(-1), retdest - %jump(ec_mul_valid_point_secp) - -// ecrecover precompile. -// Assumption: (X,Y) = u1 * P. Result is (X,Y) + u2*GENERATOR -ecrecover_with_first_point: - // stack: X, Y, hash, r^(-1), retdest - %secp_scalar - // stack: p, X, Y, hash, r^(-1), retdest - SWAP1 - // stack: X, p, Y, hash, r^(-1), retdest - SWAP4 - // stack: r^(-1), p, Y, hash, X, retdest - SWAP2 - // stack: Y, p, r^(-1), hash, X, retdest - SWAP3 - // stack: hash, p, r^(-1), Y, X, retdest - // Compute u2 = -hash * r^(-1) - MOD - // stack: hash%p, r^(-1), Y, X, retdest - %secp_scalar - // stack: p, hash%p, r^(-1), Y, X, retdest - SUB - // stack: -hash, r^(-1), Y, X, retdest - %mulmodn_secp_scalar - // stack: u2, Y, X, retdest + %stack (u1, y, hash, x, rinv, retdest) -> (hash, @SECP_SCALAR, @SECP_SCALAR, rinv, @SECP_SCALAR, u1, x, y, pubkey_to_addr, retdest) + MOD SWAP1 SUB MULMOD + // stack: u2, u1, x, y, pubkey_to_addr, retdest + %jump(ecdsa_msm_with_glv) - // Compute u2 * GENERATOR and chain the call to `ec_mul` with a call to `ec_add` to compute PUBKEY = (X,Y) + u2 * GENERATOR, - // and a call to `pubkey_to_addr` to get the final result `KECCAK256(PUBKEY)[-20:]`. - PUSH pubkey_to_addr - // stack: pubkey_to_addr, u2, Y, X, retdest - SWAP3 - // stack: X, u2, Y, pubkey_to_addr, retdest - PUSH ec_add_valid_points_secp - // stack: ec_add_valid_points_secp, X, u2, Y, pubkey_to_addr, retdest - SWAP1 - // stack: X, ec_add_valid_points_secp, u2, Y, pubkey_to_addr, retdest - PUSH 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 // x-coordinate of generator - // stack: Gx, X, ec_add_valid_points_secp, u2, Y, pubkey_to_addr, retdest - SWAP1 - // stack: X, Gx, ec_add_valid_points_secp, u2, Y, pubkey_to_addr, retdest - PUSH 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 // y-coordinate of generator - // stack: Gy, X, Gx, ec_add_valid_points_secp, u2, Y, pubkey_to_addr, retdest - SWAP1 - // stack: X, Gy, Gx, ec_add_valid_points_secp, u2, Y, pubkey_to_addr, retdest - SWAP4 - // stack: u2, Gy, Gx, ec_add_valid_points_secp, X, Y, pubkey_to_addr, retdest - SWAP2 - // stack: Gx, Gy, u2, ec_add_valid_points_secp, X, Y, pubkey_to_addr, retdest - %jump(ec_mul_valid_point_secp) +// Computes `a * G + b * Q` using GLV+precomputation, where `G` is the Secp256k1 generator and `Q` is a point on the curve. +// Pseudo-code: +// precompute_table(G) -- precomputation table for the combinations of `G, phi(G), Q, phi(Q)`. +// let a0, a1 = glv_decompose(a) +// let b0, b1 = glv_decompose(b) +// 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) +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) +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) +ecdsa_after_precompute: + // stack: a0, a1, b0, b1, retdest + PUSH 0 PUSH 0 PUSH 129 // 129 is the bit length of the GLV exponents + // stack: i, accx, accy, a0, a1, b0, b1, retdest +ecdsa_after_precompute_loop: + %stack (i, accx, accy, a0, a1, b0, b1, retdest) -> (i, b1, i, accx, accy, a0, a1, b0, b1, retdest) + SHR %and_const(1) + %stack (bit_b1, i, accx, accy, a0, a1, b0, b1, retdest) -> (i, b0, bit_b1, i, accx, accy, a0, a1, b0, b1, retdest) + SHR %and_const(1) + %stack (bit_b0, bit_b1, i, accx, accy, a0, a1, b0, b1, retdest) -> (i, a1, bit_b0, bit_b1, i, accx, accy, a0, a1, b0, b1, retdest) + SHR %and_const(1) + %stack (bit_a1, bit_b0, bit_b1, i, accx, accy, a0, a1, b0, b1, retdest) -> (i, a0, bit_a1, bit_b0, bit_b1, i, accx, accy, a0, a1, b0, b1, retdest) + SHR %and_const(1) + %mul_const(2) ADD %mul_const(2) ADD %mul_const(2) ADD + %stack (index, i, accx, accy, a0, a1, b0, b1, retdest) -> (index, index, i, accx, accy, a0, a1, b0, b1, retdest) + %mul_const(2) %add_const(1) + %mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) + 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) +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) +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) +ecdsa_after_precompute_loop_end: + %stack (accx, accy, ecdsa_after_precompute_loop_contd2, i, a0, a1, b0, b1, retdest) -> (retdest, accx, accy) + JUMP // Take a public key (PKx, PKy) and return the associated address KECCAK256(PKx || PKy)[-20:]. pubkey_to_addr: @@ -207,13 +183,13 @@ pubkey_to_addr: %endmacro %macro secp_scalar - PUSH 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 + PUSH @SECP_SCALAR %endmacro // Return u256::MAX which is used to indicate the input was invalid. %macro ecrecover_invalid_input // stack: retdest - PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + PUSH @U256_MAX // stack: u256::MAX, retdest SWAP1 // stack: retdest, u256::MAX diff --git a/evm/src/cpu/kernel/asm/curve/secp256k1/glv.asm b/evm/src/cpu/kernel/asm/curve/secp256k1/glv.asm new file mode 100644 index 00000000..3c380054 --- /dev/null +++ b/evm/src/cpu/kernel/asm/curve/secp256k1/glv.asm @@ -0,0 +1,104 @@ +// 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 +// 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 +// +// Z3 proof that the resulting `k1, k2` satisfy `k1>0`, `k1 < 2^129` and `|k2| < 2^129`. +// ```python +// from z3 import Solver, Int, Or, unsat +// q = 115792089237316195423570985008687907852837564279074904382605163141518161494337 +// glv_s = 37718080363155996902926221483475020450927657555482586988616620542887997980018 +// g1 = 303414439467246543595250775667605759172 +// g2 = 64502973549206556628585045361533709077 +// b2 = 64502973549206556628585045361533709077 +// b1 = -303414439467246543595250775667605759171 +// 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 +// +// s.add(Or((k2 >= 2**129), (-k2 >= 2**129), (k1 >= 2**129), (k1 < 0))) +// assert s.check() == unsat +// ``` +global 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. + PUSH @U256_MAX + // stack: -1, N, N, N, k, retdest + PUSH @SECP_GLV_MINUS_G1 DUP6 + // stack: k, g1, -1, N, N, N, k, retdest + MULMOD + // stack: (k * g1 % -1), N, N, N, k, retdest + PUSH @SECP_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 @SECP_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 @SECP_SCALAR PUSH @U256_MAX + PUSH @SECP_GLV_G2 DUP7 MULMOD + PUSH @SECP_GLV_G2 DUP7 MUL + DUP1 DUP3 LT + SWAP2 SUB SUB + // stack: c1, N, q2, N, N, k, retdest + PUSH @SECP_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 @SECP_SCALAR DUP5 PUSH @SECP_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 + +%macro sub_check_underflow + // stack: x, y + DUP2 DUP2 LT + // stack: x=y, x (x, y, b, a, c) + SUB MUL ADD + %stack (res, bool) -> (res, @SECP_SCALAR, bool) + MOD +%endmacro + diff --git a/evm/src/cpu/kernel/asm/curve/secp256k1/precomputation.asm b/evm/src/cpu/kernel/asm/curve/secp256k1/precomputation.asm new file mode 100644 index 00000000..6ecdd6a0 --- /dev/null +++ b/evm/src/cpu/kernel/asm/curve/secp256k1/precomputation.asm @@ -0,0 +1,74 @@ +// 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: + // 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 + PUSH 32670510020758816978083085130507043184471273380659243275938904335757337482424 PUSH 17 PUSH 55066263022277343669578718895168534326250603453777594175500187360389116729240 PUSH 16 + %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) + + DUP1 DUP1 %mul_const(32670510020758816978083085130507043184471273380659243275938904335757337482424) SWAP1 PUSH 1 SUB %mul_const(83121579216557378445487899878180864668798711284981320763518679672151497189239) ADD + PUSH 9 PUSH 85340279321737800624759429340272274763154997815782306132637707972559913914315 PUSH 8 + %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) + + DUP1 DUP1 %mul_const(83121579216557378445487899878180864668798711284981320763518679672151497189239) SWAP1 PUSH 1 SUB %mul_const(100652675408719987021357910538015346127426077519185866739835120963490438734674) ADD + PUSH 25 + %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) + + DUP1 %mul_const(91177636130617246552803821781935006617134368061721227770777272682868638699771) SWAP1 PUSH 1 SUB %mul_const(66837770201594535779099350687042404727408598709762866365333192677982385899440) ADD + PUSH 24 + %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) + + // Then store Q, ±phi(Q), Q ± phi(Q) + %stack (Qneg, Qx, Qy, retdest) -> (4, Qx, 5, Qy, Qx, @SECP_BASE, Qneg, Qx, Qy, retdest) + %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) + // stack: Qx, @SECP_BASE, Qx, Qy, retdest + PUSH @SECP_GLV_BETA MULMOD + %stack (betaQx, Qneg, Qx, Qy, retdest) -> (Qneg, Qy, Qneg, betaQx, Qx, Qy, retdest) + MUL SWAP1 PUSH 1 SUB + // stack: 1-Qneg, Qneg*Qy, betaQx, Qx, Qy, retdest + 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) +precompute_table_contd: + %stack (x, y, retdest) -> (6, x, 7, y, retdest) + %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) + PUSH 2 +// Use a loop to store a*G ± b*phi(G) + c*Q ± d*phi(Q) for a,b,c,d in {0,1}^4. +precompute_table_loop: + // stack: i, retdest + DUP1 %increment %mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) + %stack (y, i, retdest) -> (i, y, i, retdest) + %mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) + PUSH precompute_table_loop_contd + DUP3 DUP3 + 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) +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) + DUP2 DUP2 + 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) +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) +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) + %add_const(2) + DUP1 %eq_const(8) %jumpi(precompute_table_end) + %jump(precompute_table_loop) + +precompute_table_end: + // stack: i, retdest + POP JUMP diff --git a/evm/src/cpu/kernel/asm/util/basic_macros.asm b/evm/src/cpu/kernel/asm/util/basic_macros.asm index c7da8c60..2fbd2bd1 100644 --- a/evm/src/cpu/kernel/asm/util/basic_macros.asm +++ b/evm/src/cpu/kernel/asm/util/basic_macros.asm @@ -133,7 +133,7 @@ PUSH $c // stack: c, input, ... GT // Check it backwards: (input < c) == (c > input) - // stack: input <= c, ... + // stack: input < c, ... %endmacro %macro le_const(c) diff --git a/evm/src/cpu/kernel/constants/mod.rs b/evm/src/cpu/kernel/constants/mod.rs index c9094338..26b5fb0d 100644 --- a/evm/src/cpu/kernel/constants/mod.rs +++ b/evm/src/cpu/kernel/constants/mod.rs @@ -63,7 +63,11 @@ const HASH_CONSTANTS: [(&str, [u8; 32]); 2] = [ ), ]; -const EC_CONSTANTS: [(&str, [u8; 32]); 3] = [ +const EC_CONSTANTS: [(&str, [u8; 32]); 10] = [ + ( + "U256_MAX", + hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + ), ( "BN_BASE", hex!("30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47"), @@ -76,6 +80,30 @@ const EC_CONSTANTS: [(&str, [u8; 32]); 3] = [ "SECP_SCALAR", hex!("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"), ), + ( + "SECP_GLV_BETA", + hex!("7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee"), + ), + ( + "SECP_GLV_S", + hex!("5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72"), + ), + ( + "SECP_GLV_MINUS_G1", + hex!("00000000000000000000000000000000e4437ed6010e88286f547fa90abfe4c4"), + ), + ( + "SECP_GLV_G2", + hex!("000000000000000000000000000000003086d221a7d46bcde86c90e49284eb15"), + ), + ( + "SECP_GLV_B1", + hex!("fffffffffffffffffffffffffffffffdd66b5e10ae3a1813507ddee3c5765c7e"), + ), + ( + "SECP_GLV_B2", + hex!("000000000000000000000000000000003086d221a7d46bcde86c90e49284eb15"), + ), ]; const GAS_CONSTANTS: [(&str, u16); 36] = [ diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 70b96829..777c09e9 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -32,7 +32,7 @@ const BN_BASE: U256 = U256([ ]); impl MemoryState { - fn mload_general(&self, context: usize, segment: Segment, offset: usize) -> U256 { + pub(crate) fn mload_general(&self, context: usize, segment: Segment, offset: usize) -> U256 { self.get(MemoryAddress::new(context, segment, offset)) } @@ -120,6 +120,7 @@ impl<'a> Interpreter<'a> { println!("{}: {}", get_mnemonic(i as u8), self.opcode_count[i]) } } + println!("Total: {}", self.opcode_count.into_iter().sum::()); Ok(()) } diff --git a/evm/src/cpu/kernel/tests/curve_ops.rs b/evm/src/cpu/kernel/tests/ecc/curve_ops.rs similarity index 81% rename from evm/src/cpu/kernel/tests/curve_ops.rs rename to evm/src/cpu/kernel/tests/ecc/curve_ops.rs index 3410cee8..3198cef3 100644 --- a/evm/src/cpu/kernel/tests/curve_ops.rs +++ b/evm/src/cpu/kernel/tests/ecc/curve_ops.rs @@ -134,10 +134,12 @@ mod bn { #[cfg(test)] mod secp { - use anyhow::Result; - use crate::cpu::kernel::aggregator::combined_kernel; - use crate::cpu::kernel::interpreter::{run, run_interpreter}; + use anyhow::Result; + use ethereum_types::U256; + + use crate::cpu::kernel::aggregator::{combined_kernel, KERNEL}; + use crate::cpu::kernel::interpreter::{run, run_interpreter, Interpreter}; use crate::cpu::kernel::tests::u256ify; #[test] @@ -146,7 +148,6 @@ mod secp { 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_mul = kernel.global_labels["ec_mul_valid_point_secp"]; let identity = ("0x0", "0x0"); let point0 = ( "0xc82ccceebd739e646631b7270ed8c33e96c4940b19db91eaf67da6ec92d109b", @@ -166,12 +167,6 @@ mod secp { "0x7872498939b02197c2b6f0a0f5767f36551e43f910de472fbbff0538b21f5f45", "0x294e15025d935438023a0e4056892abd6405fade13cf2b3131d8755be7cebad", ); - let s = "0xa72ad7d8ce24135b5138f853d7a9896381c40523b5d1cf03072151f2af10e35e"; - // point4 = s * point0 - let point4 = ( - "0xd8bec38864f0fe56d429540e6de624afb8ddc7fba1f738337913922a30b96c14", - "0x5b086b2720ac39d173777bc36a49629c80c3a3e55e1c50527e60016d9be71318", - ); // Standard addition #1 let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0, point1.1, point1.0])?; @@ -192,10 +187,6 @@ mod secp { let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0])?; let stack = run_interpreter(ec_double, initial_stack)?.stack().to_vec(); assert_eq!(stack, u256ify([point3.1, point3.0])?); - // Standard doubling #3 - let initial_stack = u256ify(["0xdeadbeef", "0x2", point0.1, point0.0])?; - let stack = run_interpreter(ec_mul, initial_stack)?.stack().to_vec(); - assert_eq!(stack, u256ify([point3.1, point3.0])?); // Addition with identity #1 let initial_stack = u256ify(["0xdeadbeef", identity.1, identity.0, point1.1, point1.0])?; @@ -211,36 +202,29 @@ mod secp { let stack = run_interpreter(ec_add, initial_stack)?.stack().to_vec(); assert_eq!(stack, u256ify([identity.1, identity.0])?); - // Scalar multiplication #1 - let initial_stack = u256ify(["0xdeadbeef", s, point0.1, point0.0])?; - let stack = run_interpreter(ec_mul, initial_stack)?.stack().to_vec(); - assert_eq!(stack, u256ify([point4.1, point4.0])?); - // Scalar multiplication #2 - let initial_stack = u256ify(["0xdeadbeef", "0x0", point0.1, point0.0])?; - let stack = run_interpreter(ec_mul, initial_stack)?.stack().to_vec(); - assert_eq!(stack, u256ify([identity.1, identity.0])?); - // Scalar multiplication #3 - let initial_stack = u256ify(["0xdeadbeef", "0x1", point0.1, point0.0])?; - let stack = run_interpreter(ec_mul, initial_stack)?.stack().to_vec(); - assert_eq!(stack, u256ify([point0.1, point0.0])?); - // Scalar multiplication #4 - let initial_stack = u256ify(["0xdeadbeef", s, identity.1, identity.0])?; - let stack = run_interpreter(ec_mul, initial_stack)?.stack().to_vec(); - assert_eq!(stack, u256ify([identity.1, identity.0])?); + Ok(()) + } - // Multiple calls - let ec_mul_hex = format!("0x{ec_mul:x}"); - let initial_stack = u256ify([ - "0xdeadbeef", - s, - &ec_mul_hex, - identity.1, - identity.0, - point0.1, - point0.0, - ])?; - let stack = run_interpreter(ec_add, initial_stack)?.stack().to_vec(); - assert_eq!(stack, u256ify([point4.1, point4.0])?); + #[test] + fn test_glv_verify_data() -> Result<()> { + let glv = KERNEL.global_labels["glv_decompose"]; + + let f = include_str!("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(()) } diff --git a/evm/src/cpu/kernel/tests/ecrecover.rs b/evm/src/cpu/kernel/tests/ecc/ecrecover.rs similarity index 82% rename from evm/src/cpu/kernel/tests/ecrecover.rs rename to evm/src/cpu/kernel/tests/ecc/ecrecover.rs index 944c1a71..2453ab1a 100644 --- a/evm/src/cpu/kernel/tests/ecrecover.rs +++ b/evm/src/cpu/kernel/tests/ecc/ecrecover.rs @@ -25,6 +25,24 @@ fn test_invalid_ecrecover(hash: &str, v: &str, r: &str, s: &str) -> Result<()> { Ok(()) } +#[test] +fn test_ecrecover_real_block() -> Result<()> { + let f = include_str!("ecrecover_test_data"); + let convert_v = |v| match v { + // TODO: do this properly. + "0" => "0x1b", + "1" => "0x1c", + "37" => "0x1b", + "38" => "0x1c", + _ => panic!("Invalid v."), + }; + for line in f.lines().filter(|s| !s.starts_with("//")) { + let line = line.split_whitespace().collect::>(); + test_valid_ecrecover(line[4], convert_v(line[0]), line[1], line[2], line[3])?; + } + Ok(()) +} + #[test] fn test_ecrecover() -> Result<()> { test_valid_ecrecover( diff --git a/evm/src/cpu/kernel/tests/ecc/ecrecover_test_data b/evm/src/cpu/kernel/tests/ecc/ecrecover_test_data new file mode 100644 index 00000000..115e9691 --- /dev/null +++ b/evm/src/cpu/kernel/tests/ecc/ecrecover_test_data @@ -0,0 +1,184 @@ +// // `ethers.rs` code to get ECDSA data for every transaction in block 16141392. +// #[tokio::main] +// async fn main() -> Result<()> { +// let provider = +// Provider::::try_from("https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27") +// .expect("could not instantiate HTTP Provider"); +// let mut ans = String::new(); +// let block = provider.get_block(16141392).await?.unwrap(); +// for tx in block.transactions { +// let tx = provider.get_transaction(tx).await?.unwrap(); +// let typed_tx: TypedTransaction = (&tx).into(); +// ans.push_str(&format!( +// "{} 0x{:x} 0x{:x} {:?} {:?}\n", +// tx.v, +// tx.r, +// tx.s, +// tx.from, +// typed_tx.sighash() +// )); +// } +// let mut f = File::create("ecrecover_test_data").expect("Unable to create"); +// f.write_all(ans.as_bytes()).expect("Unable to write"); +// Ok(()) +// } +37 0x71e206f9a89076270d57e93486946ce5803dbcb76279780aa41bf258763fad23 0x5f7e34e4a781f7572f8192b845de80fe24d77f13552dfefb67e03c94388934c 0x7a7f78a2af5aef01a889e8713083ab77dcc9fc9b 0xf457bef979aae3c1cbbb294a8f015a8e149a60ec0146383a4bec6fb097098d24 +1 0x57d71702dde291f2d7c6ea8b9d8172523b72e93db5a7426c5b898bb74d555a3b 0x47303ce56b157b8c237bc0206a610975ef690bd71912315a35fd01ea428b7520 0x1113efd5c8896ccf251ea360bb9d91f113707f80 0x65a5c566f3dea5f436bb33e8803be619c1c6d3857b0199335630e643a1e3774c +38 0x6cb3fd5d878b8b234906bc447c9b6abac34fec019d82d1e1419874bff40f08e1 0x4de3f2f5babfa400a014739e705f64219824ed75ab16058835452366e00b7498 0xea320901e51d9268a7f5789d305b1330657fc52b 0x3c922df979a5c1922d2baedca63215577d603b13b72862b57fbf240ca501bb20 +0 0xa34136f1b02ba36d0644d6aa695bca911c9a7ce5e19272f97b82056b03f481cc 0x26eb167cf65e0ce1f8f0c3809863e3c8542f706b8ef07ad849793fe208bcc7e6 0x4e80744fa23cec76e1621ce0dfaceb4b1d532e12 0xf4e022d9b83d279611395823e4b436d1b795054e59d0ddc663c42c41075026c4 +37 0xf4fa7bd652968977947eb8a9bbb216735ded98405e95555cafdf48615c1d4866 0x77545ac93bf7065641161ec455c23a8fe2b0e6f1f9d8ab8efefa39e6ae3ae0 0x9acbb72cf67103a30333a32cd203459c6a9c3311 0x2727dd81db2f9141fe2828b150dff11b073a825b88e514b1d33f3bec117e3c5a +38 0xca14396f151b4c21129bb15c70db48c97969809f681d7521841d2dea13bd7be 0x3438d0e665fdaea72759ad3db4bab68674314cb50d902fb58a57ff8f61c89703 0x595063172c85b1e8ac2fe74fcb6b7dc26844cc2d 0x26c3de6bd2e155436e031928d2cec402853cf6797a4718cbcc89b48ed05d623c +0 0xad3fd1fb3cf701e53cd5687926ca64b2fa978a062efdb1f6d78d15c7610ef5e5 0x33c82cf8f9cc1072307ac99ff833c76775e0a4c5cb0a177eb4b1b415cb96239c 0x8a89e016750479dc1d7ad32ecffcecd76e118697 0x548757e275d1152805810855b75dfbe12a421023aecab6da0cfa257156695d53 +1 0x95846057f4d5f4e8940f9f16b17d864b60f89a85257f5256988ce75748d24450 0x6a3accdf56960dcc8541971a12484022ab54d1359601dac5b93072e5c55e1f10 0x6da4980ab9a35ff025774203e721a4f63f78f953 0x8814c7e0039cb36f6050a9c5c72095f492bd50f156f3246c516be072f4a7b1b7 +1 0xa437b58f9d8dcb09593565cd607ebcc9ac72df40380d35332d1cc8e3e242e61e 0x6391ca50a4be1ccb9f7e46d4beae2196984f88ef0d3e9bd47a85cb2ad3d2aac5 0x19ff156cbeaabd04ffa2df84eb6eca93f266ba0a 0xe3ea3b265ff5bbf8746b3bd81c934ddcbfda502ca3047bc2fb41d6218c43c8f6 +0 0x912e235df87ed93dcfabb8646e4fbe7a5b5aac00a27b0c0795492df682311f01 0x4a08239f03720362976203222bd9a60536746a0717e951d20656149833ee8aee 0xdd0eddc7d2ac6fcc3a8bb54b4d329b71f96ec8ba 0x8e65adea948f96cdf3dbf74b65b0611455c6c8d8295715744af921a3d2b73fb5 +38 0x1c1f7fd0febe597836a8650dce0e8c72e1b506dfaac11aabb909821a77fe657a 0x23fc3c8b0e73eb6077a9e861cad7e38ea26920ccff72aa46616b241ae16eefa8 0x120051a72966950b8ce12eb5496b5d1eeec1541b 0xa2f59b3fb815c9d3d5ad410e793ec97962032e965f81ecb07bc8606821faaf7e +38 0x88de71d84e271c4213391d1f78e47d2c7efb017b8b21fdebc97b8e2fc0d121f4 0x17a58f16c998f04e22577b88484ac1f7493a0473d2eb4dcdf5ccd659593712cb 0xe93381fb4c4f14bda253907b18fad305d799241a 0x2614f325a54ba5bcff2bb9221bda406e7ac17515a94abfc85e8764340f28f3c3 +38 0xda16a02577ac6d17701ad03320d1a1786320b7941680ae242d9faf5b117d5c2a 0xa475e4d3f36b7afa9af0e558f5ffb68377ff001d7f842b118416840cb7ed231 0x1062a747393198f70f71ec65a582423dba7e5ab3 0xe5bccf131464da99d721c610b1bda164418b645dfba3c98f01c5048e8dfb7934 +37 0xcefe9615f951106d5b52ff9f5e07c42319c998b70c3d291eb2218dd0c497ae18 0x52fca3109c234eac060b8daa57f673fb6b8b2c1278fd6a167f12deaee1071819 0xc55eddadeeb47fcde0b3b6f25bd47d745ba7e7fa 0x22cf2c4159f1fea8c78f1175b8c7f3743b31894525f5c272f027f8b20b56eba7 +37 0x8ec6fb0236f0a39bca70802551fab9d7258346399f6deb0bfab834af93beda56 0x6f273debd0a0e8e98ac63d6078d23fa93a7b5786a7456bb5cb3fecc19b1cebc7 0x75e89d5979e4f6fba9f97c104c2f0afb3f1dcb88 0xfafe13916c038b9b01e476ef5675341a75bd39f198500732b116221d92620781 +38 0xb244a0d2c66cf36f35455ecd8731f2960c64db35603ad2a76ffa34d22bdb821f 0x1a7038fceb3cbaa08fae08d5857192e44dae37666cf841b11c6e2bf84e655078 0x75e89d5979e4f6fba9f97c104c2f0afb3f1dcb88 0x17c8c48fb411df67de52aee98dc4a586f3725704199761f5bd23158437cb5872 +38 0x59ed90e66529f9b21ff820ab501e5ee0f18dde32c1debd66b5dc6e3342032b62 0x3f4b7b15b0af3339d791c8e73a7976d16bbd91316025d256d9161ff1ba894c69 0x75e89d5979e4f6fba9f97c104c2f0afb3f1dcb88 0x4a34d102022fa077a95899e102ef294cbd5941145fce1e29e2af39c6c831ce77 +38 0xd2bd67f5a2ff75cc124ebf944f7c26d87172d580cd92493d50002af32781c02 0x6536865fc12e5121a546458a45b480ca4a14b71d1cb899cf53c936044ad756f 0x75e89d5979e4f6fba9f97c104c2f0afb3f1dcb88 0x8d635892680e858fa7c2564751262f0ef074134ac6a46149b1ff02f16c0276f5 +37 0x274407742377dea657519cee052763f9f0247fb2fc8d6a587d3290b4056abd2e 0x1ab13a59094aaac36a7292c7cd6814d11119bd34363f313c993e46f4a4fe157c 0x97b9d2102a9a65a26e1ee82d59e42d1b73b68689 0x9d22cf1bda837de201fce2be661c2bcf5e6c3829fd75f58232a7200fe89abad1 +0 0x9613c1e52f935a04483aab79c0ef76f8d427f79feaa2543ba4c94a91cbff7f6 0x3d75a82cae83a3647c248f44752185b662055ef9f3f21abf2fe5260f6885002d 0x86e122e39d467eb13d82765881cdd944c94ba922 0xfe0f34ef96a36515135bff2c1fb7964a552c0bdfb88a1ad773414f2a05c77132 +37 0x37f09f9d9d034a9ea8892c3a7b4826de643778f59e217ded5199d5cd32b3237a 0x311f3b9d4f7d771015eaaa0a1c2cd5759ed798902ecc03632a18de47c078149a 0x9cbf099ff424979439dfba03f00b5961784c06ce 0x9d08d5163de3f8437d991d7fff25178eeb821ae6e272cbf42a86f4f1a22023b1 +37 0x28210d2f4b817744db46b71c7c993fd3318ad30a96975678f185cf5e8067b9dd 0x646aba390d36afcce34eeb928b92c8d0e12cfc5ad598569045de59d4b2de3526 0x41d9294128b6c8815dd92199700953220b1bd375 0xd4711cb46f4155bb887a2d978f2c4d877cf9bef8122ca0d30395d5815bc5a886 +0 0x4455fa740404acaec4e670be00843afe7cc6cfa379108ef02eb8368210791943 0x83f2557e17faf4951e9cb767b278f57487776927928cc53f5f1e4f028982cb1 0x0b5c4a7fcda49e0a8661419bb55b86161a86db2a 0xe676662e29b77af78c77311801b74666dc37ca016da761c164a3ddfe971502bf +0 0xdfb1180544b6b942c23ddadbe4561c821dea1727735fb4442c2c0ad02185111 0x6524e1961d5357471ab6d7e4ff52fc434c88a962ac0a8f203dbf67cb4e971727 0xdcdf0feeede933ceafc6131b663f1ee210ac61ae 0x6f9cf3180ba5ffa60436b7539de12d8b11e0343810b0d3f85d6457745219b86c +0 0xe3f04a7f4b367c274a018bf88c3e3929ace1ae203e6cbceb4809f4b6453b665 0x57da3d1b4b3e6adc031deeb9baf408b241d178e12577c105f97d15eb71945151 0xdc5432c573a1d4874d305a5cd7e62aed2b0bc522 0xd79be8888d4ac1d12398740b93f7cf5adbffa332356677973663b2580d45c7e2 +1 0xa29cc033a00f3efda65699bba97235eeede560f1adb51d307a168a2db841d8a8 0x6f761fa28ed8c0aa16773f7a11ab14c34dec9d31a085887a798dabcd00fb1905 0x19f494583c7c933be7b0ee58104ddafac1e8adfa 0x21f2778f84b87d28bd417f57a952fc3b86b86641717008fda61ebfc956dd7166 +0 0xa18dde11da20682e5d3904fce5591b2d499bea1980e113a6b851a786f9551415 0x7f29dab88a65cb3cef6b8fdd633e61f565ba385459998637b4e54de7a69c017b 0x4a3646402d1aed59cd97972474beb7d1c68b303d 0x889719b175613719e30dbdf7c59b0766ae3a642c0700fb43e6959b369796e218 +0 0x563776eb92b4ddecc85ceb07b5968ed78b47956554735cfc212db00e03fdaa04 0x28bac43c2efd049b4e9ab7c46d5961e216da1baab59c4e2930bf19ba146c5e21 0x8216874887415e2650d12d53ff53516f04a74fd7 0x4aa2be7cb95d651d91b8feb3d32c04f4abb1c8c36eb0b2c88a72b1a1630cdda3 +1 0x24dd078a4a604496c592fd0568c50e340c2df5d3343392b62f6669128c43777d 0x54a4e08b684ed36cdf20f132164ec18599b07763da289787ef4ecd21635e7bdf 0x64917e321c516a2dbdad48bef7e923873eb7854f 0x2335ef16b1d02b9e206f952b7d98bde5f453afd6e95391a6083fc4d6fc829e2b +1 0x96942017e0365f7e059d8822e8bdb10f772e186e5f6dec136baac903f037863e 0xd94dc604b36c0b77740a4bbf70a051d239185611ee36f2a6224c8604b638016 0x0e7518b332f469a6a2f59e690f225cef5157cca9 0x62b0621f47ddb24e8581b2165cde8f4f883222c991fcb1ba2610d01948a5882e +1 0x5e5f424b80fb1351a178d36348840d9bd5cf363c78550d829322f21b58a2adfb 0x41a2fb3e6fe3c5a4449106ba5fd2d0d5e9efc10e26e8c17e7133fecf6faf138d 0xa7fb5ca286fc3fd67525629048a4de3ba24cba2e 0x4a3adb07cc5bd5b0cd6dbdafb1d643e41e353c9ef1cf83ac1fda32815b09d461 +0 0x17216ef4d8539f74333914926d1fdec20b06ce4594d846af59bbb0995ad80bdc 0x5806f2eeef10c734ac9e577f3a7c73b338ef2553238b5543a1c2dcc35c3481b8 0x7f72687d4981e85d2d2746c17a0219d8054a2148 0x9a3fbe852ed1abfe59e63eca1d8e2453a1060fadc622360d4f6bc40cbb8e326d +1 0xf765aa91e69fb3436969124c747b888e705d49e8150a63a545343ade5a425ac6 0x19235da122cf688b31101015a9f4c67ddd557f7139206641182187c308f608ed 0xb6e145b74eb1a016231483272fb622ffb68d3d7c 0x2537f24f5d73879d63799050aa0eaf5fce44bd670068aa2b1b997913859106b4 +1 0xa2c13c62165475ed08b64524b7c9a8c1e6f7d4fd041c579eca29306c0ccae15b 0x3fab181d182a0f0d9768d75bbb8461f2a9a6b3416212e8b91209680e29c5a31 0x24c09a812b6419ae0e929b95d562f635097d4590 0xf9113ed129138f590b498a572380efaa06b59e436689772534842d7837e092e8 +1 0x3dd17bdd0a94e61f79b21917b937bb45cac40ee88cb37a09c987ddd346ee551b 0x32017f92d89ed4ac0f4a235c5f6f41982faa4341076ac2dbfb29a7a8efd15e1b 0x41f0bbe07573e14944304c0a544c5dbe6f468cf9 0x7df3db64dad8bf775cd8e6e585a61065cc739ec914cde160b3aee6d0bcfeba72 +0 0x7ab5cbca681be515dce228b1fb30dd588e1fa9eaeee60a9c869a9e4d6a44da8e 0x6567506dacb0bdf3be1aa5ebd55bb97b159258d728d7f2e54034555648c434a8 0xf031dd3ef38f677b55e8c4a191c3b9e343c07758 0x76a21141c922911e735c493c8bac70d04c6c83af1793046cc74d417deb23c8ef +0 0x541aad8b51542ac5f71abb002099c2107dc0a87da05751675deb23f683fd26a8 0x51b3820e49ba8cb365fcffb97269a30b826b509eaebc05b03c37be93ffba06ed 0x9079caa7968d77ca87967ced769bb4fef312a834 0xd570c731edcc02bf4f06f3631de76b4e2427fb64f315e9ea94080125d4478818 +1 0x896ac1bcf80a4a2c503f92bb030f0db43aa545e8f44693955b4f577bc6df260a 0x65c170a802c5b98a4567dbbeff8ec95d55434eaad260c12f37582334cbe013de 0x792eafab3593fd79f01c8dd5282965dec1bba51f 0x2c9a7a5e841d0a432a3d09a657c0a465604467f4ed0a8bf1d34583d2f49245b8 +0 0x9cd74d9898571e0af4e013337231472e033d7b95f70523a10f7122eb4a63497 0x4494e798d8905e9662b48b99c89835a2166b070be8581ede50bec94cfaac332e 0xaa9a1ec22aef4fd5119eafda899a72ffa0cae258 0x5c378afcde7290d00b044893da06469e9c2181a1558598a85830c3bae8de121f +0 0xca04356a33bf7351027ecc535e3e08537abdd333f2907a98e7d16a2e4408997 0x6871c6fdd8152913909322eab21e55baa51ec4bddf161b0e7d71251da17e3b9e 0x4976a4a02f38326660d17bf34b431dc6e2eb2327 0xe814b149e4d1a7404f17678f8a845795364f99fb830b9db51a84421ae4891f17 +1 0xd390febaf6781e5ddaddc823f1791c133b6818588e4d071248e7c3418977c2b5 0xbd2a753d9c86388a052044df07193bdafce4d5dd36e4a4e3973ebbdf0a7bb91 0x4976a4a02f38326660d17bf34b431dc6e2eb2327 0xc20c2be74014e4d9032c422635038ec1ee0925b2e2da0b7922f5f3c0d2d3ce25 +1 0x54c0524f19276d1404c067e69f7288ee5eee955ad12226daec56d74b7894405a 0x4335aa0002e02ddabea351271f791f0a55f69119a88a5dd64048bd7c241c57ce 0x28c6c06298d514db089934071355e5743bf21d60 0x59c08d63ebe74a3ac3c3d2dd65e813ece338b4df7878d5842732a24b3bc886fb +0 0x3f5bd3f07b802df407ac00f150b6b091f45a877c4dbb65c9d4ffacda8cadd858 0x4b02042d475f722e862608ebd4625dde1fe5b6c18a09025d61694e6e1a986ea6 0x28c6c06298d514db089934071355e5743bf21d60 0x2d81d7671ecfd4830d97fb7f2fad04440062a3cff4f10bfe7fb2f458fdc758fb +0 0x4a704b00ae04e370b365d5d1f7d54f5b4554428bac0db7f24cb2d8e52b66a045 0x705197257bc2464a40381a5a6e062cce897e621013ea10c9647bafe1156ac2a2 0x28c6c06298d514db089934071355e5743bf21d60 0x368efdc35f07630059054c541f0cc8c259d57c2de9dd57fb3b72083f2fd1ca0b +0 0xee6ee3b45d0bfb27c6c424bfe3e268b5c88c0c0a09863005e907f22cc9935aa6 0x5c5ff76d39ffc6483116642b4b9d4ce851fd9a30f3a6c7ab963c1e2e151d82f8 0x9696f59e4d72e237be84ffd425dcad154bf96976 0x9252f22653436777ec2df3e6f5d532d11cf7c4e1084bd4e86c0821cc2ebc719e +1 0x81da46ac5a2c6a97e4c58a5e1081b6d68f97971bbd27a22cc3499c31fb557ded 0x140b455d2d09f1af1fea8777b828662b3b348265a5f9b336b4edc149434967dc 0x9696f59e4d72e237be84ffd425dcad154bf96976 0x2d03a34ba34a3ca1a8c8ba3cee10f0ca128c36f211b7e50b8728651fc8544d39 +0 0x60064f428d0d60a5bd28057f4b86e82c0d5592b3bdae2feea0d79881e5f72fb5 0x774b1055a8f27c9307bbe7eaaf9029bbb7e920acd9c0038b0646eb0538ef38fa 0x56eddb7aa87536c09ccc2793473599fd21a8b17f 0x0432c2c29c6bfa4242054e26254776f91678f21c99e672eddeccd3b9b2f92729 +1 0xc33b4f64a2183316ad21136cb72cf4f2260a864561cde658fd8a6ab5764a2f8b 0x59f25f6bcd7643bb349495b8816db070cad1776d6d6e18b0b119fbf9d8e4d001 0x56eddb7aa87536c09ccc2793473599fd21a8b17f 0x086ce311a8a9090542543571cab974bdfbc6c44abda76c8828b02672335da789 +0 0xa758e894173ce8be91f4d5544a7ba339744bfa95b535162a03acb61f5a9ea09a 0x39edb3cda1abb60a2ebd59636d9375ae08776745cb1a962eda45048fc1bd234e 0x56eddb7aa87536c09ccc2793473599fd21a8b17f 0xcf40ff9251650690129f50bc19a5a395ecb7d9b1bff94d16b14d759394f428e7 +1 0x50e249764da0ce92ad0e832b544d7526729eacfc43f3011fa6f555cbb6d394ce 0x52d85c3e2d5fc6505078b3200676e8efa8ef1626720938ec52ec3cac9f44ad33 0x21a31ee1afc51d94c2efccaa2092ad1028285549 0x584f942852d1ce266b8159c971f552e5b1692809b0fb98489e3605811b7f5de2 +0 0x673b134b030bc26cd02e4cea1ddd63effbdf0a178c9cacc932c2440042e1e18b 0x2aa60ee96d3dc050a0558fde204f482a85cb246ca431416b9ce9d18290a213af 0x21a31ee1afc51d94c2efccaa2092ad1028285549 0x978cbde7348b61241687919e376b531e1e9ab62eccd5c90c6fe2a8a0226e3689 +0 0xd626eabfbd7555bbbfde2f763b342a3145e85407efe2d2616f5138f816b73d11 0x217cb61fdf4d4a342cccfe81da91ed5fee89bd7e0d66218564c5cee47cbb6050 0xdfd5293d8e347dfe59e90efd55b2956a1343963d 0xe364db52cea6b264fd6f311bd700f91aa300194d4972f8f33cbd1dadcd5093af +0 0xe311926be6a3abbf18ea2d8b71559b60679acba9026b6a2870e1bbac1467f91e 0x21c747f249093f5772501f380b56b42e96a8ddf77bb592f377b9bfea06d9e0e2 0xdfd5293d8e347dfe59e90efd55b2956a1343963d 0xb56f149f71adaaf2674917db32c1cd864214518c58cf5574703733f924e873a1 +1 0xd4b5e0226fe10e85df2ce70a16a1ca5422bcbf83a685e6da2be48bc388c932dd 0x256ca3b5e0f3c0b422f441bed3ae5f851347990adfb2d11cf79879f54bad2cf8 0xdfd5293d8e347dfe59e90efd55b2956a1343963d 0x1d5ffce90110bd94c5d7810642e8283e721e33aa987d570b1593b04ae17b8377 +0 0xd50421f2321a69714cd7a52ff57c4c3482ca01a94eee507afe06828c4e47898 0x27e919a35fdc99ceb109873286aaeeb3e90aee201211a14496192cb80e8906f9 0xe6ab9e371e39d1daf921f24cd83ae3c837d2cce5 0x785488a8196174e5efb61add20c39c19887fa1cbf6c2c4e6f2ff9db79b966c52 +1 0x71e9f2b77a94f63614bac8249564393fcf3b3531ccf23a232b3be77ccaf1f18a 0x445c4e2eba9856f71c6c7b6f5eb81e1b451c39086458ac88f931667a1705de5c 0x5804869f1e8a5b99f2eab8c6b1943bd0dd5b26f3 0x340b702ff1d5feb24988a865d19143b47854f8526c04bf427be42e7bafb37fc9 +0 0x742855759e28bd15e07562aa6f35ff93538daf448ada687416fed7844e793658 0x73fec36e58ada180a2bb4a8847142e8ed88047d485d926a226bb8fc2feb1c804 0x1d05754e9cd39e987a34343cc862a82370942e93 0x2b0b92eab8fff875b52a21071b2ce8dc23d56652ffad308a0ff14719e4dd81b9 +1 0xe05dc5b2eadf7292c2c3c14f97e08daef9dd1aa499ad11a235d77ef4d03956a3 0x119e1653354da4b22fb38cdb11fe3ad1d18d8d174812448a6f8dd412f7ebf812 0xe41ee97cfb75f1646d143aee40fcb4991450aad0 0x2dff8c6bbec665b3bcc66782635d9192025aa733a09e7c2dbe10008e15317c08 +1 0xb1d0d41adea5daf645b5e0600f2e451aae67b2eaa191d2208db210f6a6fc61a9 0x1dd9aee6dd6cfea5c8dea1b93f0cb55c97f036c0b91fd0b167a479c72a8e84d1 0x5e746b94aa1fbf9af9559c6e04a93ef0a7abd0de 0x95366c33a8a3012c6273656ed6954e28f790943c0fdb2fcdd58290b00e853af5 +1 0x76d1a4d73ed92b786f848998c7dc8643aed6d80700faedfdf1c7c54fb8af0775 0x59f7addf6dc96f924714bb1727513d2d546ed11913087be3240f7a91b87d1d4a 0x170b4cadf7deaa017b1f595de5ec7591f7a686ea 0xb69182c459c9e8d84abaec3af791abeec972544eb880845b361e43daf36aa66b +0 0xb32e62641c4428e8d24e65ade6b2046b2deb890b887a89334ed8118f6f4f38d6 0x3b088d3fd2d2842f1a16da028cbff9d1f5014ee35f914e665538ac4c4936b548 0x285b3bc1f46d2bf1142966a5e1c4834b86b82aba 0xbc9b62e2e020145c9fe7933a0de0d6112fadab5f8a6bf701f5c360709668f743 +1 0x1dac69e7c23884811a95371d7b91e65ed6a364cbb5eee706bb560858759b1206 0x7a1b11dcc4f7219f8a8ed4d72357b66d9794fe4beab2902f5049e4e9ab64037b 0x5ae41ec3d27c0d14809be1786f35aaad447643af 0x9b21f4d49fd6e579933afb48b6535fd55ba2060a123bbd0263657622cbfe696f +0 0x51a7b36b6d1b7fc9d2cf6eacc16aa98bfd9d36a40d06f390cc2887dd3b064c19 0xbe87f07c87c4ae834004a8dfd4ed9e3e08b7a639aebe4f3235f745a05071f74 0xc6d7425c44ed9ce936578f8c150b6e589f9b0b92 0x793900f4ac8f983513c4fdc6f8603fb8dec6a21965cd8c7f64771c167327976c +1 0x5965e82f900e7f98922584a7aaab65615c69494054f9a0dcbbc50502c463d13f 0x23217a916260a082687453297be97454bc64e5d1617d9ff9973db5485f7fb235 0xeb2629a2734e272bcc07bda959863f316f4bd4cf 0x0386907e53874176bcc54754145fd0e8602730f9ac1ab6e680a4acd6ad584b65 +0 0x73b358681787d5541d791f3e97f851ccb6cf55b91e2c5640b52b4f3ebcf95666 0x1375414789a136c47ee9df880858d5f729b8da5b6776db925687ab58dad50880 0x95a9bd206ae52c4ba8eecfc93d18eacdd41c88cc 0x438abe80d5baf46affeca09ee17b6f85ffa3a8c438520c8a805ca95173d09fc1 +1 0xc42bea9052ad930f8041eaa138d9062a57ea4cfc53c109d429fa9fc10d01790c 0x758222e7ef5f17699e36737b1a1bd8d2870185cc80d42a7caf0b189238eebf34 0x95a9bd206ae52c4ba8eecfc93d18eacdd41c88cc 0xf8dcd114a2a0961f9614764ad17a1cc1b0d22106dd3275fa8d62a20a3cf6e0cd +0 0x1b42517479fa75ee42fe8b1f26dea68fde5f0d8777d32ccf94357c94e4a2b90d 0x4f82ffafaadbba64bc338890ad1f24c278dfe6cc445efa4e7a55b6625f905056 0x95a9bd206ae52c4ba8eecfc93d18eacdd41c88cc 0x164372f5e578564fbe521627f408f8ebddc6310ef689dbbfdf4d7d3c7e65bd4b +1 0xf4894537eabe5b3c19638fffa3416049469a3a216c8e5a97668c035cfc9e07d1 0x28b06efbe60e625e1be9cd81448a9b240b93a76657c1a18844d368c617ba882 0x2f043b4bb857a7f4b6831219184dac3105aca34d 0xdb1e4681868be2072d639150c9cff48b4e7883fa6c2c6665768389e5fa4f469d +0 0x480de57d4582c3a33c16f1049e8261a0b27df1d9e34a77e41c9d45fef01e2142 0x4fb836362b300da267c0273193445df1ed68fe28327d87acc2a22e583e64749e 0x7830c87c02e56aff27fa8ab1241711331fa86f43 0xe967340f3203ff60407a16976795d14c3fce0447a68f4b98ba98d4176057afcd +0 0xf80a9f48dfbcf60ffc4fba1c3a820bee256006608db5267fe8ea3d38949ecbfa 0x58ea47140e36a6754e2674bf5f13f444ebb9efcc076aafc9b925d531e14b456c 0x7830c87c02e56aff27fa8ab1241711331fa86f43 0xae9506abfb4b1aa41effa3510687329b80e824241e7e60e18c7bb3a2768f151e +0 0x816e21a58cca55cb134caf496c8c7bcc247d63a74ceef9bcfc505cd4382fd20f 0x18e9867681a483785254bc2bc23c0a6ec5fe7ab98fffd7bab2ba2f69edc59ceb 0x7c195d981abfdc3ddecd2ca0fed0958430488e34 0x1c416922452e82a5c2910d7b481923a0d3fea94856707acdbbe88842401b7473 +1 0x99f8c14bf31ec945e4bdb7af86b1d584c3263e0304aaaca78a804d5db56fcbb5 0x2e2958ece32f5999aedcf12d20a16d52dafc4296744985fd171958d95d948bb7 0x7c195d981abfdc3ddecd2ca0fed0958430488e34 0x3cd70e64bba999d4c64ecfdca7257b59d3aa8c6f034bf6adfe8ee5088c17cdd0 +1 0x221b8aa1c94cef0192f7fe4499d18e2404d463e98d4840006c606b36cc5ab5db 0x3e26c8ee8ed31948c4fd1dde9721279647ad4e17238deb46fc668feb47582d6e 0x9396d062fef353765721fa3f700fe858703a4a48 0x03b4189ff4b99e8378f60d10e10ad47bdee0e9b56828162291c60ec3ff6b74bf +1 0x28c8e1fff48f440d92b877725d28e5d31ddbefc59412caa2559d07fb74dbd4dc 0x32833c2b615db914646a55b3d16802901476fb2f1c78a73bb4d98dfc8521db63 0x760418292d007fd98e392ee3750876c27a6460fb 0xa3562fdde603f1da197b6824b951d8f4bb70b839129333b18b6f71897a67bf30 +0 0xc906182e3c40e1f5d259dbb4cea62d2ef3fec32efed97cd307a68e38bfc1761f 0x39e57fc10bbec73cc8521dd1d2db3b86d2e9b0f03cc73fc2ee0b5b6a3df28180 0x747197638a7af760e221636405c747aab7952cc6 0x7e689bb1704555c3dc0416989cfc372199b5dcf14819a66809c1a8e2dea8cec8 +1 0xed0248a764b72ccfb9549c1d17c855d8c810e25c829bc78f81bb22ff96f899d1 0x487912ba2a9979735b05ccdf0a777fba98923139e1a1b5cccc1068640b202a6 0x5b8972964bf9076eef6610e453a014516529a52b 0x778fb142afd5ef84a7ddb1f3d71b68d20e33b2e3243d7f4f47199897ab02c495 +1 0xf2b668b356cbdfc191da4079d9823b27dae5c1f3597a70d8bbf8db5256540539 0x2f5c858f4f81da7ed09f134fb95016e487cd2f4aa2a2da1f3076f46888981505 0xe6c6ba7ea35c1f200e72056f1467b188eb441ea5 0x5a34e826e965d0c9e7afa46f61ca73e97efb3f02d294a45e263be4029a2d3d65 +1 0xaa08c519409dfdbca88be959445ee38d7577c3de9674a6db64a8beec7449264c 0x4c85250f383d7ac2af72dc1f0d6ed5de9ada483eb896c49d32c2d56a8f0c84b1 0xff214c36ac0673bc49268ff1913b11e93534c86c 0xfee2ac113b09f4b1e9b33be3ebb4b227f2d2a0b6fde20b794aa834abb8348833 +1 0x98ffea8800969c9916a6214e763ade3d6d0cc30984d42330c8c131f9464fbd16 0x7a15dcfbddbf3c64817dbcd7d4e2c61d73d2ee4792f5e89103e9b27f4c36e7bb 0x0637d40e523c2dd75a9bee55967b7b18e2cc6ac8 0x520396bd99ef0e44ca80ec8ccd1c86a32d47f1c4fda146b0666475d5372af9a4 +1 0x611d611e426f6b2d4992dd556472bedab585481fd74f90081c40c5f18e16611a 0x2f32e37f67a5edd8e8a0da6675c13859d9944ea17f35e3e5815a997f06cce35b 0x68e0389f5678f1f15346a136d3e3096a84040c9c 0x4eee7476b26f9a3b565ad5f63edf4ed9f02c0463227c46a831a915584e4c2b6e +0 0x893a0acacaa60ff5364228c38f5a555773c0aa819538a6c64e9684162b567263 0x4e43e2381e6829acc6e3f017899f2c0f9b82e0e6672dacc38e4399889796df46 0x18e7c0e8a28822b390b7e076eb712c0850409c41 0x39e094c665cecbb18a113d1ccfc64976f4cd9887ec5463d8403bafcc77b55fce +1 0xeaa0f379c110a5ab9ccc71a5660230ba3be968a67de2d810efce78920da14066 0x699aa12379d9f2292bb619bfe2e713041bd49bfb02b85d67e05df54b76d66a2f 0xf795bc11cb62785c31f3e638aca0091b02b051fb 0xd632206bd8dffccc98d5a4b9ebbb612d63c2dbf05eff858324b931aca4c201bc +0 0x3405d2887bf37cf4270a98517595c78c3100e158cdf262ef59a3c4c1017ede93 0x66ac9b238933dd3072333f178e5b292ed7e18e68646d064038849b91d7cb62c 0xd0702fd5da78f3ce9267753d77d0da9f17043c24 0x664d7d927bdefc8bd1151c907124bb817c257246747f703f3de8a7365caea912 +0 0x27915f3c65833e9e80794d5342d39ca391ee459f654c49d8e84fff9c831dc50 0x4f3472eba8a1a164b982b17a37906a3e89e4e97dac88d93e51b63aecb88ee7ba 0x8edaa40f4b511ced61d2006e591d2b165807c3f7 0x9bc40c99ca374fc638d17e7c82d3b0f2d04b8ae9fac6865437b5b0708bb8567b +1 0xea0b758241ff151595d091982295c276316f47f17f871a2590a2628b2016c40b 0x23c56e6a0226e175d6751e36f2e2d59f3846c5fe17ddbc1d4a4b2f89676e1b43 0x4425abad97059fd35ae14c9e4d8ca8ccdfa16cd3 0x18f6e255eb1c86612447f6821769389df7f348829489a10f940b0c06fb30be2e +0 0x3695c2a9ca1a52204a47a3576963990541cc402e1515b21c9835260e4f6954a7 0x797890b5905545293eb8428ec6e46cbcdc9da057c4e7791229f9188772c8d057 0x50525410d59beadf26d271f9d693d1f4191e5a04 0xc0e8063a935706d67cb5d9c9cb99daca1413d00c6248df3f04238fa4d4cf08eb +0 0xba1400e2353ebfa00d5b24c1b41a16751d979e7442dfa163a25d6c4e9c6d51d8 0x6c38c1250a3394029822fa3cdb43e24fdb0bb4324d5bed1291170a348fd24955 0x4d29b7f953ba471fb650fc5842127b05e35949b5 0x17198f8a656bcc2511d33b20db5c6ad0c22c8a9d67ae7c5622096f779dd4abbb +0 0xacc2bedb2fb80896b078301ad3b4cc677200fdcb5bf08674742c2a89c7e9dfde 0x630b75ca131a42236fd0fc94e1c1e69a8fdbc7cd93fe924aba6642791e4947c8 0xf959ff8366171a9e19d142340896cd94e1aa87ea 0xacd9925b4a9f9ee88ec57fc159881c18ea86e357b90c0240ab1d6bd5fe86e850 +0 0xb6179293816a9bd1889393b88c34d466d43ffc96771ecf0fe4ab7233446236fd 0x65b1acf754fa586c454495a479023b2d901b202044813a652ba664c5245acb2f 0xb8c26e86f38e22356d2d16c875d38289b6905021 0x0b97a1ed7e0d454011bcd52be2c016519ece9db2df56800acc626ce39cea9233 +1 0xcf67b4dd5b9d926004d1abf2fa23ed3f0e914423c231a4ae2dd08e19a3e9e4d5 0x24ee89ce5d32a50a4f890b44839b738feaaa59ac2508f6438b8aeeeb8be61363 0xc6554650ec0e3e48ea68b3bee277c552ae0debd7 0xcf807523ad7785086404c6a753d9aa332c761a36b89644f4caf9c465d348cdc7 +1 0x374aaca7f94e7e4d62b984b492a06df25968944ca9c250f10169a3628d05b0f9 0x15177b91e2404caf1bc42858b20e38e33bd99371f61814d01b686d6466d015be 0x6d0bce74c9375731d41d59ead889d5637bed4af0 0x97fb69fa87acc3c0b0950c22ff5d2adaa87f4d1d46026018938f266809c69a04 +0 0xeb8ff1cd76391a8ae07237568c08dac28d4e3bdc29575ad191bb8d335bbe2ed4 0x312ed03ddf43c67bacb693434da730d42b8929faf6c06a4204688f158f45dbea 0x68e3c86ba31081d59373df5f32dd0086939a36ed 0xcbd0757cbe0d6762392fbffcd0291d0c653f0470e3a28f30381c6d42ae594220 +0 0x2eab5ed09657a381c33b279b1670be03708b1e77ac91370087b426ee9aade920 0x5fc053a70338a0c868f11b2351b26d9be087172a77979fe0533c2053bd1df74c 0x8268d3d9bcd233db97641001e0a5f1c6785c3a1a 0xd4ab95a7d77b2859aaf8b8674608460c8e53732d2beba89d7fe76466292c4980 +1 0xabfe7fd3bfcef4f049c6a11e0717b22fba93a9bdf375132a3f2d4ecf82c999b4 0x52658fa3ca3d2a527ebf071c78051671545f0d987084b46460fabe57d9367404 0x3eafb74f0fb78383e4e4bcffff6ee4a9fc3f8c73 0x892d21e91838d7d29f4841129cf9a86539e9b2fece80cbde027070be1ded88ae +0 0x2b4a7137d0b31a7ead72ed979ae69d1b4841334900194bc381dd5f71b6a2c337 0x4c393d7e881888cc50f195dba68cbc78f2ff55b3ae0a91e195b77f582cac3d2e 0x48a591f904d0266d32d3ae188504e2a7910b703e 0x97ccfaa3728c749711a1eb28fb888860cf38e67711dea979cf8b91cc1d999f35 +0 0xbd6f6a6af08d750e28f99357f0dfe768de9e8ed1772f9ed1e312980e1a830167 0x17e56aa8e9630383c197d6282404b659c10e2889ee50bf301e12629420484bba 0xd413fb3e1d268eb37eeb38de04734343ecf6c512 0x626eb6781f01e2a71fefb336122ff174e6f079b721bc47dfb6489a1eac87f35c +0 0x7e42fb76826c7f00952ef47f200d5f5519fbc4c78e7f8b958f585306379a1a34 0x738cd052fc0152c03dac09f5b117ec6a1c21d92a2d2e636ac12a05536ecc44a9 0x261318280a2593780fc0b49463d2065bc4b9acea 0x2b6a207376c1f17c13d8e4da40ed4b2e60e1e8734125275ba89866a5abefdede +1 0xad10c20fbaf33d13bb5fe184a340fb29f3d32a83c375adedc4d5178929472f4d 0x7f1c763ce7e89e090da9ec44153759833dfcbd2990e0beda598f0e041dbbba45 0x0109480dfabae54463568ce807b6818ee156f84a 0x457a1e31e350640cb9d4a8709d91a874fefbe6ed69984c87d72dad406f510ed1 +0 0x2aeae7d1d3bc76fd6f02f1234e5a4cd5a78f3f9ed2ca6f6664e0c6bd821f55c 0x19250127f033b2cf9783099f65801b3edce8bf6d26b32fbb5e44abba2d37e9e1 0x455ce87ee8207a3f8771cc3df3d80807e098ae9b 0x22f009e48382685fa1ed6ec3287b17fa144bee9c04c13c504101c9eb2cd1a1d7 +0 0x86d9311b4a2c1bce624d2a88e626614514fe3ba58dd656899f4fe5e6a170b37b 0x6dd4305274e769c830758afe3a56ee4745e4528501d635c48dfb3f80b3398214 0x6b1baa2a2343f50f422e9e7038826e703c584dc3 0xfd6b690eb2fd74672721123d5ae7cafc6cf053626e39f530571c74d0a5d7d666 +0 0x4f4e9705b7c7fdc08af4869b151154ea8f011ec61297ac1c419a2229985c7501 0x33fe5b95279c277bb2cb42dd9524453a8bf4c3600ccfceee3ba01557022e8514 0x5c7cf7b750b74b91212e97c4edb502b13ef7c603 0x8adebd9c90f7c9f54c9e28b17010732f84ecf9cad9c97d042c229572015cddd2 +0 0x3a105f1f5e1dcae92a1d5462222aaeabe96d71f02746692d946b2e092cb40292 0x724bea7cf27436bec762f4928cac3c738d0addc9e68a0ea1edb45a2fc7e99bb3 0xff90f956c9a7d52ac21c37fe7a34801849887f19 0x425245f8953a77bd17f6a99e85de70472a58fd712c2cd062661febfe4eef0f6b +0 0xeb3165ba04a9df88da13aa967733573a9cdb76836b29e5565f4fdd1c10a555da 0x3387d1afba42b8659a13f020271deb5712b6c3146acf6f06e5fdfacaeafac00b 0xabfed9eaae37dbc55c03dbe890b64e59b965141d 0xc08a357bb6684da5fd835fec9b978f4b0d447492012ddfc5b02177f79362b432 +1 0x7ee780ccabdde306b172ff8e62ec05bcbc741e4fa8d5057bfc172d18b4121ece 0x3401ab66a0b0531b209239bf6ea6438aa5c0c7621d443c87f4ae50fc867c672b 0xbe7ab77ae4126496e193c97aaf83ab9cda87fc26 0xe557ee4e9aaf6a265f8572722719032b27a3dbf20519e7fd932db9310cf0ff4d +0 0x282e2f0d2acbcbb5397705187713d73f3f320afc6e7e12992551ba2b844ba4ba 0x262117d4a997afd5f1e2bb8d2438e091e1f6e9f9e792e424e58bf98a3e438bf7 0x4a58bb7ec2b8064c4a03abc7c52d9d8a6b0116ab 0x4ee60a3d8de3688398227eab55b80b4ad52ee0429be1aaaec1075f07af01f26b +0 0x35e88824f5d5e0019afb1dc818b338ef707fd32487126d089743a94e526a72dc 0x1f9a7cbe5ad46a1ba8d14ca47893b890ada28f4f69530b538d7535c9ca450fe1 0xa34df27ba953cdde360364eaa1b011e98a449174 0x3fe021424adf83aa8e582435a962724eaf11e264d463dffbbb1d5a7e96041da1 +1 0xe5437ebfd6f54a4580506a80e9baf1399546e270c1965daa1b317a3ae119c532 0xf486022026b018521fadf86ddbb73da9556fbce3e41ab6ac4982d23aba34732 0x59d9099ab334d4a3f19a6e125c427912c68fc32b 0xbed8dc0ed9fabdbd5168148daf1218f025a14ace2ffc3e8d8cc24a9a0f6e2643 +1 0x11378e51ed9404c97fa5c1a6d241a55c8cfb09202abd30380abeb8f7fd7c8f07 0x72867b97b9ce0562f5c6b0ee3a844e042fab36a56e36c35e040ff29122355b1f 0x4559ca770e7f95fce15bc54c8d09abdd3b5c660c 0x2224f14bcbc55c2a2de9a1c5a4daecb269745286056d28e61c10e5a745cc94f0 +0 0xacb3fa89cdea2bceef0f27eb974a4be19b33bce137d9e5b5efcc98ef67661726 0x515491ffd2a473f6d3323a318818f2c9df9cd553032922bfe9227daa2e303768 0xa838b09ae1b2223298d1dc43dce715d06aacd47f 0x7a345cb9dae9806b45d6b3067bae813643a5d455f5c20f2e475d6898e3f804fc +0 0x35fab5e947d0fffab73351208b12ad003cb62172763113dd181016ab8297cbd8 0x3c6a548de3e59210c32f3d896fb149f18a7d6ca27a3d101a087e9ece3536d0a6 0x971e954dfc651a4b1743b7132fde87f6dcbe502e 0xa1c0b30b92991df606a0f37303ed0ecfbd3c23239cca4146e0aab00d907476c3 +0 0xbeb835e9af305824af71fef83f68da275750bda01bc9247f3f1ae946a35b0ed9 0x4f075ca8be453f38b035958699dac32d8370de3adb284a5d743253a6c8f1116a 0xf6926d15fc0ee0113ac4840e7d881d92cf193a7d 0xc5b5a0064d6b4e78407e1087a55d81456d914dd106d8bfb4d5d5791e30181a04 +1 0x257ca4a4fb774fa12d04ce2cbe4f7c2560a5d93bbd6b0dfae377907bb91d0b5a 0x541f5c5d044a15390ee23b9f4ae66810b38c3e7678f09ea8736709b4a05328b3 0x17be3026e13c693e72ba8cceb3adb589ed36f14e 0x612648064356c01dcc3dbcc6f8e8be1d80b1206ba4341003f2d122b0d673479a +0 0x3bd7e74261648e61873edca54585ffc1b7dfc0ed11217bc032e3f5159bfb4817 0x79dd87b940b8552f48849cfa433223943faafe7aba5af8f778c645cace8fe409 0x5425324987996df91143b257c4e54a129fd6e85b 0xc533049e9303ccc8dd3b7e38c45db85ad01dfecc3784a1e1b59b54bd8c3c701a +1 0x98c4a283642a6840580b138e65fdba451c87f9bd925329e83744263fd7be5048 0x69410c8d7f022f129f862f029083194c720a955b18970b5cd1a8a83ce172dff6 0x158d9cefd58c505021c54528feeb14ef73c9abff 0x15669ae7e934f1144b36117c84b02db899cffa206710ba72614820121fac67a9 +0 0xfae698f64c9b17dc1ea1c28055c28789fd5857c8dce25e46502e68694aae6c85 0x635303afde839cd5a566b6493a0d068f4796e57c238c35db7bfc64a9d950ebe4 0xfe52a5d0a116efce195baba279758964172e83ed 0x2526f94f656aea49757e6d11eca88a0f102752eebf43da5988b619959f9fde8e +1 0x4982c820b96c2a6fb89724db1df3a0acfae4fd7bd5cfca1d88e40d77f6c5ae9e 0x4e4481b9cd42bb55fd00bebe0b6b90ad468cc02c29ed0ca1a84fe8738e2067fd 0x8d1f185fbf60bcacaf783d8f6436e117d1493658 0x3a14ca8d74432637ec0e4e5f24d0c61e54a82746fd12f146ebd9c7507ec08fef +1 0xbf64b639128b416fbd70c247b7a8e33183f7cf186a1dcef142ae0c288d8a1e5a 0x64f71c1dd604e64c6881e8fb3783cab7b9c8f80db9307337b26429cc19096060 0xeb280aacafb6dc0b0f811f3e2c1291ddfaaff76e 0x93ffd8a9781f73bbf3d9849bf05b40571456d01e47fdc015a70ec6d0a53d8b7f +1 0x7a0879264b7b0aaba53f632acd1c3dde1f03eab3b4adaf2086afbd4f1a26fdb9 0x4d2d10cfe5fd159c64bfdb72b9c39d576fe71ec7d1020741066d853138c896df 0xdec977d8d5f2c79018fc185ba5341f8cba15f0d6 0x97f1fe90d4298fd74c44458f70938a990ad00befce1fed9c9fde7334f8f2e59c +0 0x3dd1e43bb8c1f5c634ab3b4c0c09da6fac71fc65ceebdcf825779ba9fa96dad7 0xe09348612309ac2f310bc0cd0d84f8fdbf67b3cedb93608792259257fe1af98 0xfeed5cf7d996ab0cacb4b0baf2450f6c51b1efde 0x5564e87cdfbd1d4d91051523f79d029604462b3c0a326c99ae2cf1920d3969be +1 0xdf54d1d5e32037affe7401bebe5d24aec562c37b13bba5feaab5b77826c27c38 0x528086fb0042ec2e80aec346fa0b6f08a879c962208e6dd227303ae3c9abaafc 0x927940b4fe41d9fe519f9580a29a351e57203dc7 0xfec469c2a340cc83511598aceb236cb647bd3a47440ee7e8bd8d002ce3b3f6b8 +1 0x5077e7de2d1fd106ade42889ed4bfe3495da0768ce7db5ef58d0211a9458a646 0xaa9b688acd7b6ab5b4b1c089045ce03e22490cd2254278a38f61618bbae820d 0xfe512fa3651ff2289172b14fded9d6975ad9d96e 0xadd8c860fbb1dcb130d9398500809d625601123c8bdf0f9f37cdc04f5478eb77 +1 0xcc606a4b8339906e3aba6ca08c0eb4c0864ba8fe458cf34fc84a95293cba96e 0x22449b2384a73d51d3559f7d93034096727dfb8eb76623e8b10b1850f11414ea 0x120609da75cf68f9aacf4d7e8b627eb9186ba27b 0xbdc844e89608de09ec2b1bce2c8d370e17a394ed6a5148b38b6511fc62c630a2 +1 0xecf21e5f54d766a37377ddef3c805bad94e1de49486ee0a8267e36dffbd824ac 0xb61dd021b9fc6b7d39ccb56c66e48d1670b264169f9e68dd245ab1c50a62af4 0x6c0fc6ccd718f8d58c3033c13a5afa3adb10456f 0x3eb16ea21e33eb3fe986727dab3ad306cb27feaa6fb42aefbb00e66694154795 +0 0xc9f7eb02af669bb4b793edbaf3b0e7c8f131d459f94e71d7cf715991e67aa165 0x54b2deb2c57eeaed6c93a8313775cd55ba346ff11d89e91fa12c26f1cc45970e 0x8ec10af9662dc17432717d72ce8b62f4ac24f218 0x7303a277360590a8a25a495153748fcd4db789f9bcf4d73bc303b4ea02b1fee4 +1 0x226c7a8bebaaecd3304d01ca9df5a9b485fa31fbc44e5c9168d8eeff7cdbcdfc 0x6c170b6fcf4b8d2fdf17569d332f0a8cc52aceb4a7389458ed1606a7948d5e48 0x550d8a88ce3e8154d28233ad9a27945058b8bfe0 0xb04b8fc72c305357f84fb647136b5d85f0e2101d43e3e5bd0eea98519f5d1b9e +0 0x8ab2815963748344b4aeaaac472c048145b4f5e1cc86093638300cae9bda1a23 0x83da77b10ffe076980296ed4276b1b81cd58a15f7c573cf83586e6139deb93e 0x945a21e3277e6aabda145da4cbabbf6d592fbce4 0x889dd7f6de4e9c49153c728d0b9d293bf27d69d2ba4463beee85ce75e4732edd +1 0xea57e0ee8bc336c781a5ab966976188c16ef0fa5a8f36f858d11add5343c8ee 0x459d6f341d6231c1197f4af6c413c469d97d61425c0a17454ef6bb18db2ff703 0x599357d348a8fa302ced7d6b99d532d95457b543 0xed26da3f378bc480599b79a5fb45f054290abfd91cb463e193988aa78a133a86 +1 0xf0b422543404ad03d959f34c890d5de1e08e25ca7b7ab02159aebc6a0e83486f 0x4dfa3945f699962e264b18fae4da81bde6ba40091b952199f9f365b0bff00e3f 0x0b0669b9a9f43a2967a428f838191e8c5b84d3bc 0xb673f86a1928f94c69834fb94b522e18a9424022c0ca00d487a5a6af26a56d13 +0 0x84cba050dfb80413562659c50431c99eb10a345d87e555d092aa0a9bc911a37f 0x3bc0e3ca54d03b706d7ca51bd20590b4c020e1335865e8c945a8558cee5fc7e2 0xb223a6a0b9a4877429f984243c904325aaed59d7 0x40bfd52c11577ec77d87258121035b41aefeaa1da5e7752421d5cb51b640bddf +0 0x1cd25acc3cc1aa3a47007f5442118baef3d61f6f30ba4672ad01f3fe2c389081 0x3149344942e7dd5525f676f9032e2d7a3bc3e154c3ca0469ed77df3639385307 0x1b56eb67f280add7cfe89727aca1134274443a39 0x6f534d1d9bf2218cf30617dd1e0293de6f84ef58390e73579f98bf7d5f947d44 +0 0xc308adac5cd7459f5aba700720966697f3b4ea36a4d9414e11d6e72e24571a6 0x74915e161c7b94d5844c22f3c6a34b3ad49a945180794ce94eb3d3410f45093a 0xab639feb2463513a9ee703cfa33f916bbe7408b7 0x2fffc85af8c55e07853ecc59522361f58eaa7063a10fd0f89205b62ac5cc7df5 +1 0xb55aeea98ea3bbef39431d8cc8a21309e288db69b9cb87685f82f9a3badf84e 0x740b80528ceb16bb4fa20ea0327fe93eb6a37580bfbd149981a7d00728ff1b36 0x7b28cc1860b9fb47cf7f3f000564bf6deacc953b 0xb51689bd8af058131fee38840f92da123b41d9dbc4b7ff43186a0c9368a4af2b +1 0x95d2923d538780533d6db5fb1112fdb426f1d4f16399d2a732d4e140eff611a 0x37987b15715cfd43f44e4395b6f449bce0de642330a9785fb48b4f00c7f4ca7e 0xaa917265aa6903d94a63c1cbc38312e19c11a053 0x78205715444565c45daab6b0c0787ca35b5219e3e0ce0bd4445ca421ce407997 +0 0x1ed4e98c251bbcceaf2a0e967cc854cfe4d92fe978909891054042779c5461b0 0x556626dcb3e0081e9e7152af9a0da2aaab65fe584c718b2bb1fc8c78c52ec167 0xd59f6c5882f2279097e401938d4c6796c07c7b9e 0x23a88397f97ef6f4000c7a666e7a803c8733ed4d19a2156f0f3c6093321e5fe8 +1 0x6450a2b80accb717e3a9c219caa01c86177ebb8d4bb84b9529c1335eb74fceb4 0x31966d6ca31437a33710998adb1c1e0c3c0d5cbf7c1524ece60f3c404df70b5f 0xac10ca20fe0977ea9f448e73f6b670d9ffd42ebc 0x674136946178355f8ec71797957e066bd246cba36a6d8f7dd5a0ea56eaa509ce +0 0xc10537417a178682e6e351a2eb7f1b971d37f7d9376ee2697c08b0e723079e67 0x4edc34693d7dff3adf57b018e87d929903e1eaea0f2500f52afed8b1e25efea2 0x32b53c2434f03884164f6cdc2dd7508b31f558d0 0xeccfc571b6b9a9a61f78e03bc5292971e96e542c1da09ab9d1a8af0cc4643413 +0 0xa0b659da4e9bebb27acb959197979e51cb56b63cf4324ef981d5bd01f815c0fb 0xb05f93b0cb57b04aff64501bc3ab1f84a7b25a1b4956b86729bf88964ed934 0xe33f06b81ddb8042a93648b4c13f9cbce8a04c42 0x76e03dd437fef2b02edf4a786afd1a24b06cbe73e7d21f85faeffdd26a4be022 +1 0x97341ea7db9c1541fdb2193bf0180e7515bf91215f4368ea17cd6e88358266be 0x3a4c5d0715f86221d1aeb7a0bec2a4fc5c0087a0e65c232149f8fdee7ab7bb93 0xcec616376b1da143d9f51738d4ca5ac96a5cecd8 0x12f61e7256fe5514b5139e8b6c24d5d2553555fd0a8fabf660da4305f9cefd76 +1 0xe0024310417f142d87d3b3443330c0b025aa927b307804f5db6a359cdbbfd478 0x3046a8a2c6ef41026d8dabb1d3804fe8b36338029df6de0b6fee303fb272b211 0x653f99f5cb0723e919b6dd7ca0bf9253b963b06d 0x4d2f79577e6e2cff93ebf1f0607039bebf0a24a88cc4a406dcdfb0f8c0216ef7 +1 0xfca536181c563f910b2593a75a68759a1cbe9e17add9e5350e5a9043bf2f6177 0x72967ecbebb5062aaae304aecefd33e585769c95785463f7494e0c685b22576b 0x7f9fa6f0fdf344c7176776febddc15d4740f5540 0x5cef49c7e6514cafec83cba5f7293e56fa077a92fa1164c60878e36579e70fe9 +0 0xd1169af593cf12684f1f1dc5ec0099210cf6eba379622ab27d54db6afeb0cbb2 0x7ea3c6458ce7d2f454d79f7a951872d4baf1236050c142f42448cca41c4dd65 0x50de7b9f1e46f80becbf9ccc1de9730c0fbb17ba 0x6b23f37c6bd54ed9304788fd2617d2775a21dfbd19bfb06177c9068194ab9608 +1 0x228682fd3644c4f2a2464fd9179b1489602485b84cac0cc434bb13be6f52e750 0x4443018948d31e38c24074103e882cf6628facdfbfc0f1bbbd3b73a7abd1b8c4 0x7a9adb31ec1623e463138dd4ce88df7e791c6f03 0x8a33ea11489e13b66d6fabbb2abfdc6f13273fbb9d95a4d936cc073f3b370473 +0 0x68b4d46ba8c97044fccabbcedc53bbf362f85dac7d5b6f20087909bf428cd977 0x1e6a77282107ca56253584a6577d937107c13c914fe96b00a49e66ac06a8a878 0xc17e64a2b177468f6d4c9b556b63d110499112f0 0x4782e3d06c981933650a9aeb4034d47557099cd5bb82cff7563837517982171d +0 0xadeae9e3caefa9e5220a88447eeb24837f2e8d7d3b9f4b80f105cf8e055ad663 0x3728bf1007a5f106d50004df752b5bc611046734225bffa10d4553530c360b70 0x74dec05e5b894b0efec69cdf6316971802a2f9a1 0x27d57b0a0c8f8a6c784d860f42f7eee5bd459d7ef0e76676bfb41279bc59b4be +0 0xe25df026f631d49fec33a23fba5c81e63b14b7255dfda8abd1faf24603460935 0x63e42f2844b2ed1a81738ccaa006b4c74465d360d529cd94df16774d8e38931b 0xe128efc01e4244ff423cfd6cd41e1cc56bd6866f 0x84013bea6d8f9df22d66fe7959145c0629e7367c1b951ce7ac198c2496a69c84 +0 0xa1784d0efa4a3075f07968e3c0969e064b986336a9c80a95cfbe4e314b13ddec 0x456bcd8e76c21c691658ed4c28158173c1f69b35178c2ed8c2a602b76cdfbede 0x983873529f95132bd1812a3b52c98fb271d2f679 0xac1c41d319b7e8f7aecd0d6130e8c94b9f748333bbfa03036aa69ba4376dfad5 +0 0x6526f92963f2338eb771ad90fb772d11904d4d4befa98b8c272c6a0abfab081e 0x5aec457455ba8d5aeadf17a688ca73754680ea1f6265c34ddf6718c85ff1e1b0 0x19e34d09c2664d4f0829b1431ea73dac1b2bea93 0xc61a5fc29b169a36e990142a63b49823dd06fabbbbf54a4f52dd25ff443ed640 +0 0xa026fb5c2ea92a4bced092ece32b06ad2664eca004ba355bfc9686b442131862 0x4dd3e552d74fb306c7e1a8f1c6c4f041d8bbe50fff482389e7959ebd90d67259 0xe697fa7a3f3165caae7458a7e7437fcf80a327b2 0xbc1705ec12a7475ace7e60c33bfa6a64fff88c538675d302ce603dd572f2f14d +1 0xf2a1cf25ff00c08820ff9030f63ad96aa1e6d2f89ba9a270953610a7f5e9ac27 0x14c87ef1845068dc7d81686b4bf201676d48fe2359a05785c40bb674873673ab 0x6faea455ea5309259cdfb076caaf779488ddf8de 0xc7f21e2ddb3c14b1b29e355050dab2eddb82e1e8306ed184e277193a53256043 +0 0x9f97613b227a9f5c66d7c4c44897aef59bf482c72434dac864b48089906f4372 0xb35e44f4d594816de88c2e9c48662134b69892690ae7c844dfa35c73e6bb7a4 0xd6f440196f5060e81a943fa5110508c8430b31f2 0x8d6ad1adfadd98aaf2365632ff00dd9bd479b687f3ff43d16de4e0ce67c90bf2 +1 0xb34b1d4a28568a653ac8c1695dbb0f8da387fec9700985879beb2a0871eac428 0x7aa14d38f04dfd090c3d688f4847d0f55c2a8785719c63ca12702c3b2c755ed8 0xa6a688f107851131f0e1dce493ebbebfaf99203e 0x874c23e08d44be9a421c4c1131448c5877015d9ba1b76dbd94dabd6809ae0bdb +0 0xc513f6281e4c95bca3e17a3ff8eaef844b09b17f7d7fd24a2637e81136aecd1d 0x75810f150e589d54765f58a44263fc7b26cc791bb88e771777aead7823327431 0xf61a659713d29221646b451d2c675f657e33c88c 0x1cf1502b1f131cf5eb85243f25aac443dbb2c8727c9e4d718ca614012da4f14d +1 0xacb42d7d82bf160d188dda513982a48c9eb055c84a700bbe425b96ad5e1244e7 0x47cdb51f4a5b8fba9f4cdbd5b4ebcf42438fcd9f2ac996cefca27745140802c6 0x710bda329b2a6224e4b44833de30f38e7f81d564 0xdd0aa6a391179f21e01e5670e46b6664d1e23869a25b68e94bfc203148c8363c +0 0x10077281c6b1283c4f8f7b3058aff92c72e5694d9cc53f1767d8977a50c4408c 0x3a02b1f7a35f12e0233f307489037fa3dbcb0aac33f23291a659068a0c15295d 0x22a98f59ded87c4143cf26323b0bd779580ead02 0xbd137a0fc25ca845d0863454dfbf05a78d8f364d821d569f27fc85dd7aaf6ff1 +1 0x58c87c54c33e54e868d9f6eaab464787256e5d46db9f7fa550ff6daa3f0d25e0 0x7b716d06c4163062b503e32650faaca47ee01f3ec269d62cbd9c9d6cd37f0968 0x9b2c4b8c833278b71789999a3d808b1bc0995fcb 0xfdc089847347bca14dd6a782cc102755a398f711f213d9b50548f1e33a66793f +0 0x6c3f7f4cc50cb15e4d5b1454088e860fcf3484bea53979597056238bee8d2eca 0x354f75da6626ded0a649d6749a6e6f570a353504e4cf86878397771993397cc3 0x55fe002aeff02f77364de339a1292923a15844b8 0x9d1e9fda1dbec9bb273fdc32b4b3f93a478861d0e4a4f44f059efaccc4ca3aa2 +1 0xcbe9c14778b0b3204ba7a286bda1efd94eb79f00141fe807168954aaf2bc8887 0x7ddc9f6c4d6be095ad846a2d1e12196fbd31f144bbbdb672c7471e8a0c64ce3a 0x299c7265388216f6baf12c04bfaae0391c2b1be5 0x6a247771ba60f3e5b1ff3a95ca6ce256a75a7c8b4c2cc74fa02541458730bf39 +1 0xae6d5ee5e33792e97504b10f2a11e5e0945fe6e540ba0cb530ab49a3b0a6aed7 0xcf7f67a2f5a6a8297b80ad4214879ed6b8b6b0d5003afa6676bc62010aa152e 0x6dfc34609a05bc22319fa4cce1d1e2929548c0d7 0x33460c004ebe3b3c87b07a5228f1a6b163eb970181ad59ff11f8d03b729298e0 +1 0x77853702c849e381674e839ff6779d66ded67a7cfc325cec9dd8b93d577f1d5 0x7809c39b7692e2fa010a78ba9d521155cb6151b316daf15098684dcd3973cc72 0x4069d8a3de3a72eca86ca5e0a4b94619085e7362 0xb5e51e980067f75f75981e80b4b2d60da80dedf8fbcbadce9ae871ea02ea3c89 +1 0x7f8c8449f22a576d8868bf3e8bbe14dad8de838b4e45ff320fe4fc794051fe59 0x1ba41d48005b10b79602ee801ede56845ea5b700aef13dc2dfe93812672ef053 0x6887246668a3b87f54deb3b94ba47a6f63f32985 0xaad2bb9310cf9aea16caa84141dfcd3de424b7cbeba41d20a319b3c35342c2b1 diff --git a/evm/src/cpu/kernel/tests/ecc/glv_test_data b/evm/src/cpu/kernel/tests/ecc/glv_test_data new file mode 100644 index 00000000..eeddd62b --- /dev/null +++ b/evm/src/cpu/kernel/tests/ecc/glv_test_data @@ -0,0 +1,1048 @@ +// Sage code to reproduce this: +// ```sage +// p = 115792089237316195423570985008687907853269984665640564039457584007908834671663 +// F = GF(p) +// E = EllipticCurve(F, [0, 7]) +// q = E.order() +// SF = GF(q) +// +// P = E.random_point() +// s = 37718080363155996902926221483475020450927657555482586988616620542887997980018 +// beta = 55594575648329892869085402983802832744385952214688224221778511981742606582254 +// +// a1 = 64502973549206556628585045361533709077 +// a2 = 367917413016453100223835821029139468248 +// b2 = 64502973549206556628585045361533709077 +// b1 = -303414439467246543595250775667605759171 +// +// g1 = -303414439467246543595250775667605759172 +// g2 = 64502973549206556628585045361533709077 +// +// 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('out', '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<<129 +// assert abs(k2) < 1<<129 +// assert (k1 - s*k2)%q == k +// +// f.close() +// ``` +// +107686458338979513480781602362120102289984183046072577606170007916778439289747 0 356346894760760276087626226488432151004 217318197015539988822336610002398511790 +97731722947559024681716452282219957975263978740712958876927310887143319903906 0 112952311639597105943171331608306471017 175336324915113800172956243369347117300 +2257829552013235716068488356709347701442172630826500503717521931809955398781 1 217177198954269660712428636031296017582 3684030775039237366050299655648599135 +71761682687641178423116382761747336210578685609908626585985077055172279137544 0 411613887075019707474159017744114450590 151273920832553029973546975684718652297 +62783928497728404736644220003419467199424172530427306334104012091526950581001 0 356354992720035849196985968820610194620 121830846826914942895951448813855579471 +112971472393035011922242209868007500713878640043238016872448018125944618781583 0 264487018368704977724165996943173512042 285484700390970001296784595154826996438 +25461175730825076518388883710792204517183536466495211664518207534042643023271 0 53545034099897480198264594975145531554 165190004103626257367815278381722433459 +10962584108805305863094865199814437137956130063005282662939859589523585772615 0 90483095476203826306018406018219770404 5326811612449241559045288462600857043 +21408645369894329120094779746078410565876089108909007382205008045148662398111 0 169498534374881290135975052358458371275 199886353275111787414759613295836178129 +65326375044696264566560466301481064601793419054459415922031817773354408022228 0 326889546721088012701270218031953215910 26583831855696219665477220232194327011 +111842137291305858378969699712312320844113500301818822648488609509780224870030 0 358588530025521890293873553071904842301 88674112578391012970148365669818035400 +48388182292356997229143390949095736048040642837574748490699810267691249078960 0 191116952754346254899628671681107582298 3408390702158000891839529313245505366 +49911865742480051061735560563065284927074022126277784335249746631933669970897 0 264037823145107342980854497957288112985 145855430071763322302379346046587506549 +88011064228057586998752667343704087902618094280685377517902958647570583123745 0 290085543470595515239894653753370693430 14756770119558000118490886433198818485 +30009968535060400275055240120220617898678094191724689489436884141916706936534 0 211620448108618886279172652130665812535 74467405322271372584718259590825243476 +34092438719895845022868954519742157397608392841511147838641931896689655705253 0 253712681046270797953577480659601778665 66858007125234224691217453473504358854 +19933194854798030957468286196052665333686276181935348426681986462367228827670 0 375367433699417734388966846244889936297 153372293450489296720756152887743704611 +32787073799415355718997863631125631233718703175697269759886364921392938882062 0 32891808855102024456953515253011980368 77940487930511577869859814604957376151 +47161791275725104506288614798750474473280618024790840884854100012964742065946 1 312347252717255682891067286523421318301 21856946250000738457657820811270992835 +58396576252135286293850441649962814493101241331772452868792542593467258981246 0 386435860687283118326199191789962070616 255307079693023397139205720942342135557 +115060515468385408144672212905490573998274019736758361938753096758293685607743 0 262798188520125902195962934507858841959 189803084283929572864130171271092212858 +19779012972836662453985156894338304952374603293381723464387692605961521693557 0 171081274587860547547912949102174979013 57465589301127450463945502034367865282 +59557764191509441867598013645714777503349852305403580697469336648469031101694 0 429701868031669440868051107540793792442 177795303910926751081426844989960403303 +16019578563607143196328852982379858266649546080168856864469298112235597680474 0 98958663551074463176660687631490644568 233393791878168160719378568078865021477 +10562725777839522399058666198047373981313723669074850829621258512867268073802 0 292926780624317082485256650443783091710 170012541910497166675603099940072757057 +84254931149527559985613453333063169649668630660679537074894341142629900907576 0 132837232740090251375656071644472384961 244976582628543094928301908837538413276 +20380321447429541046345292124123014967993322597369899508073271459317646942626 0 111539426712964967017058439075348544721 136457255271890938279674096934490549597 +35823478843930347084388595466123917813382623935222519582824342772355368930668 0 344747824596064962262223971586512093242 51297481552000860172020127988198453167 +80241848325182036157993488624805524857219782665695992444750535276735583413862 0 82340229918369821712300343919820498725 63733637513653433109167300290555281634 +97226356911065213675760229768195495128204471731783944444577985090257197910378 0 446014776705927330915460883244932821691 177469707620482546783366938995769886978 +109428260880714527563984277495597265036651066654038657919872567580138165291774 0 161672361239146061712035067011012533028 135318658267642259767200100178501163101 +2456794270872000782363234481512922390542878827204431899833426565233895023893 0 74828733009148785536127298517662120591 179295736453864396519345103177285033687 +110358259814550367489159660516984924641020557067772775167486584593266233675942 0 138136909202792061850367051759964362547 264843241982079857099222090528590000425 +45020250230141050051522559508238120581888241267457937696474728271972253722265 0 87549999760912063842087076828777369676 238515148236919925661625570698391472724 +95065683369763634417612024676489081265800406235065297734312257621781169765813 0 118370857981563183490914121076784546191 326659433929093684815387239002458454431 +69584751392302470219889523867615373496183607643011599446665472330683539767819 1 377919218162491144365821268230495796416 17705788605085926950263754377896912006 +76415630284491267089238244223629171147644120761858316199029811577615719231449 0 267622097681648402314140457745168493587 89588280990502222245207445170640720753 +65364085707349281956236549866501748823856136841940266620950177684430018843445 0 132267725211117920683502313183506048518 264214091460596903951215959616574011471 +68496349228346023078504695033699882360944999476909408396270127390352298425491 0 191993299428900172162222144049340318833 79634457289786421733400673100175598545 +102993915200052545800426448078657787436216212063164501066260753636143148124940 0 163440127586444525288823079326270664890 325506850940071318900779564616253601267 +52741671333983024545651352953613405692257890552619354868798426411597863112154 0 133590669472473250569301044874251397694 237372562462678299696726017426468829440 +42639987468269725754102300157336909309545563953519496854334708256248948394766 0 431063745053160358927691763847782968808 193669788629057692406011889207843014678 +29206368839571441042561819182046811696223127479198968497360791862630672671515 0 277986923316053516002841054408403183641 59592687435443357148382971479680131059 +71973220179522107822101404659171226257144464633003514672612098121557338672548 0 322909954267474945791248655897066141536 293050217180619359512277537010235537383 +56189834032086453332601369686267448852455834917612092225939516574071065332626 0 332660145724338149855163033301023693251 12611492445534493307445430497075200035 +31982793788179558790671330796787049422460889252209689229489001432296602499953 0 269464330447411793712601526912009659097 167182666821688542355385880931284236234 +16518078972553593549758733820107997479660283577033904866692689456727784786478 0 304102879054138054435565170351520810045 142905791587513981498092170520643879699 +94244716909336223464503837369998701109892007583988212494377056030355472895376 0 467093138757680915509388829449293013646 226426884035766381418834144446771642547 +44467883430830968001822170634058366419732129331210332931260658447763801150864 0 281703191174596317562742425945281032238 95931914823602058485208452447767234443 +60757843444627208177611775662180748251067331633733354506962740528567550678786 0 364792843578455602540859258722930806013 22985918465963329342631241716188503937 +84104433821828104212024577481718029429333421426779064758944750873197256369894 0 194568855013271786800809508380541745446 229446727694933983025749486384962695170 +108248973610767750691070182098073572949318237420294605687534705238530770145606 0 234790458844778294547046820000183347960 136853423736681350580492611886974941498 +34786748041297346844402381070612885237669631090846858820446664948156258332298 0 129736897365921587142334978346099758200 107959792757944633156449214295086334266 +102810527058268355991029023336552190810340318709366271113520137917014391654159 0 148985653000104585855012217007602848810 285556390393026559166136104430077303381 +107236246924500784286686156332239333699163162126136960941861933347432937834765 0 313200289458568331799816761858395780373 142680872231228032149222767938200767657 +85553968400939667567930200707730584737604794801667126532720383360162315234514 0 307472696532882796737703457423188514874 296914695978283876709758037260643553508 +82837814572589462755293438031137912897564739078397083645207882986761988791535 0 405681676908352708549992171820572503559 64874756718813142564650981326069701866 +14834821979711174342347418586239866307795518884311084112398157923083605490093 0 218906139321825148960327027014716059865 5398134016072562185525333168620524647 +67348245348505927914271664564446811443588683749779783570350111017143847898097 0 254609381839337906765057986784175733591 270406925366933860203965644997129234345 +2063429500958983634064267943275200028101999214514376275082775637608230047487 0 285864921863374935451402978288991111210 140974939702532814318715017185194082867 +43625545265751642319530548668823021113923533296212745434154635093925340318646 0 125478893142043257031943264609007785461 37430901158365833149937667891377811071 +60254767192676745108319277057026673966649840682353512960220473255788137185527 0 85569735097631560223833443532550979934 34248462751654285214565728640379369301 +22601862891817703523530064842405371545632680395612261401387375143186171365012 0 283966364359137844268584710239840721584 170741157684288371503271360770647850109 +36688882390308047125609554909707677384136557823091781950850198703673629328949 0 304937505424244255406772251632073662854 107941815984703440592696953275956588043 +33208816598042721195745544971211794044038312604167126475397073583741011379950 0 257404336173099263206930483012004812962 170164618789543610213365586260734182170 +39297649570333858765003888694639533352497515176239825056560515607741259731075 0 156145052967030386105499135884582210078 54497667467930899753813922299913172250 +54637872245524887881032600305350015290272875899972729610331439185620118773290 0 395077376010241740810432918864166035177 93852844258976704940002931907949151024 +88477016900610413158940577652743317396447829713610837687077702531128464718461 0 323324411142508508530456441228668291478 211151247246769617452166123756966142392 +41598936008198731079884859239223639848304990290666848641634620437372496446222 0 85805582579665251120480456720466090942 143694931912132722751347446727602834816 +373480321144091356922533540606283894942988247604407185340481756647747484585 0 345549321492139170474742394376382095527 71251793039915354846447941102663431084 +84258609088809671502152097635250182843416245478956553930721477471568942918630 0 359958445115437616146884546791900422760 209622554133411960664551020684164294079 +9440446501513893859379538380329446864351560840221628307260846615526215927592 0 339246878875613927684268691787103865751 110409799457409467134833722481337492812 +55925186015874123052867495961134073970363053110642834575553633177237020070937 0 281806784121012042937659611423662380249 35752974295355739414008327687425901013 +14313518312862202971090156290618992104192763570936898894813659282869959208403 0 172509775212271055239057363437171917737 59754434248169448120987066734387889569 +111268754146758837697264983055593223063084625726338412602745581213589619484669 0 148588766757522357474370807946949472808 183406433701379055960416600165388394509 +33583005638673182091493970585896796225218364310267915560147416484149113721070 0 269477523980104579175731301759168643949 9676709873142520048074747075857741191 +62097300932243039207983132599103561090744852675653078622645608620927335304774 0 362208007634632770907526194795846985085 125485932487517280707796055413738475504 +69470136884318670533235450273081862983478556458391822259275447541346132086775 0 310210316710143552841177917772786585488 159613051942327765254896657661971890408 +55554003436990105290716930775529027270038014504123775431060453476565014226861 0 198391601153947071608821549634517134760 270804420573327015379180992719114487262 +63521986644990571683711131485684886420035901156096079339255962619956100319834 0 362368363525137175376647673230424566061 125768744712639967948794763559892058734 +39440924532772377254918215550310495861094652949498307275772521934026985851631 0 414440413855403119148203784033017710604 106720348967568700157952776507351152697 +113650350794964295667693497007295227479992987161195416203529076544156258245391 0 343190891658990042243000059774980232540 189993165912278571614396125540525755774 +12060978044921297112545705497869188349722438667919780638052952059493726821175 0 276069257613135208945773030655414028504 136285308043821734911594367112649257958 +105789307135807612086098817909267525375954832198992605847542954772931099584768 0 433325692578475708153991436382791427019 129921641458780719273520811559813133899 +69433632214344479101211792095644966808721160926046498793260353342027682868057 0 322509369037623913505107743632239544075 113299268149946372520910291713402743904 +24460402077968983409763057622899795447251649366692157671392145935347337291340 0 377175009018645852452875383590598444502 189664810756168184247801967219877824466 +13105151318101002473679798160027174567013844662394185145706518743566167126478 0 323031433307377102344922565088285052842 252869998470404916934473703725896742636 +108857916118591629460855844580279511079247120430501133673969373400115772057125 1 426082041095436570749164513448724776222 1344996918599353003175194108411055268 +30899793031967542499393325401869717204968022446691456422484518182039754464155 0 41189065439231721965660637070014940974 74657786728707478548459140791844785156 +42660487964938323261980840451036707322486281830165866447724589559779889415976 0 245870233345930804561144731219490223224 127141851294877882025178836436796455630 +37387140132555614810821730754277949363835605068841039899738621980294997933895 0 150159182755561417060435659522370429791 81581054221512957112735258396165241956 +17159447254990270892119166147401308949163847794289817104630361802056282161232 0 178479656888104473931660197150990396438 267664410606094914100059386520135392190 +83456707800243832322540744996978769631335456434082303223204532562382353057651 0 189539581116876000304501442051787974114 162910289043751013042300136827979906800 +113368421840773273729985473673927531323544016485117672517168688889432454633908 0 154358647219745178004669717477745117496 307142436563599095163767756819125424615 +20869713211088908516610321654109922950750719114597192027196125805185700198151 0 163910418799610197682168483144539230176 269759729726322927219190687653596003404 +26735315184114124272334548425279443508385712213611146968009696504465911588206 0 205240627494169192287993034411152245884 55210888999056597595042722926199789722 +76797233958219684103435366228834420519498314052856011503922550071298714493968 0 353752913925531198606092538070475073285 100999184689090208645863968218391875151 +11501316262162300375979127025292112839206756428095011600235204382430772976348 0 195671623720846234345100097909164659134 259549311467580394168940695171794399335 +5499954749584490909339566957350502760759610806298213247834888213930980529510 0 209970326288894062270149213135164632202 275990158587359783835324624759442792678 +53847515309899500393679932310704128764673276395570498453241526064973054637402 0 386748687160004843705523391715572288230 183566373845857071957970382941286627183 +94398654494023851108752608167055715860772634239165124544357529124948024842340 0 318202463785473336931564575125916415816 215994684981510630809277694393380501973 +36656493266386369358485910842591170308555076019463367278838084994120459557571 0 308392141620030032329036878262287747065 64988565224703418797277861698620670809 +75480014152354054865761886712676991351270344326843864004260186153405983133998 0 372437256584161244703639260690957361080 140504990402702255500316200785438278833 +67845944893478136372397715294465752305976041976019929831679688643550119531129 0 197110320955908842776146885729560089629 288003601122109895835307983914370681478 +76194119006688802604288385379861769162591789626501159400751522647724594545567 0 232558586344143021136166563949968790726 242936528625421354837084801661531293926 +66263326507660643835841406515807849024321176870372435186922902565826928481575 0 363141205821968169207217125362702694057 24124123552981932184055754629505844469 +100510734743907488806160588666365988971386897327222149443147607306346120261973 0 146736382026094522592000203597444045683 251130708647348225763809270898929689802 +106838670139740114119336784417179584792902530057009714546831487361012906555206 0 156729733027065941832300837780302104252 314861685206513924841930242904808419007 +33543180143488561525058529457587069373805642245465749999484886718779422530027 0 302636364615690574441985222636910148420 258201795009911603182967969270428344982 +42376293550195555517070764212355171491212649845139146053182718419396563879086 0 214505598767744465807795931495247614301 220250312117221124128132161319846219342 +41708315326527927606530605609929967186544623196878484244947991822616843065461 0 422452959016497408790412858352857214388 193203881470148065100005062751331770200 +68512452037079184245084712141521930742510675547646233864503804508664757066257 0 404116759055926990018715495649759339336 211724799995620267979461639457416986532 +104278993513583527216365596218140828406818577821077366637253515736225107924080 0 219309700960175345765354469995710958723 41957493761365642465468141776438224115 +23637687988062074535536836626460736022932610867290146670504972195502765686749 0 411466320159322219953604506914875629030 144850037182536950770779086952733885072 +34454085065583771863220809391041817105488111965216058846489348716533968860820 0 89959921400489954802373905038318511088 120246207887238084347671651848073672275 +83553466658358116983877563026067672426658017002912831240063725191815978852290 0 285297639590115733445449398893921571018 16618371484094867032452824560460987383 +106198016004554220782885861301842171203986404151380864747699232902842796851626 0 82665368441597391846158743996519206499 158699660772547503142947674726622999636 +2412046251728349490904636523731547054707439872777184064590980716301678379955 0 156029064792326411997735878049914303030 218327007734106160900186455975212329478 +85549827248433465299692130794783213012323775338788528838269829150138773070298 0 400798738417935822187015281301838737917 126255752295172999358790232265876709022 +56352906677674206650190885055609903018155000067480028198287796592433882235873 0 279904281291796913379742004083696154037 263833140725893713509007441691993020269 +81826269121952853408314729261928669609030833610338260737908228645747312338832 0 134892863149796872258368488750128817992 162517329111328234573209639422681668758 +56192008211982036059885991452268511541572369650877121131529085746842798763033 0 122089862950970893509061696914554705584 58982273009957731101030239303057896571 +72969593427223901509521127044123018193637515344888377582668090558118196608574 0 132240554445150133654143114818269875107 189669618181584005553841464996459584285 +18328339301450858873090279430757744015200259200501814832168986949584625725490 0 359202512377664498590475216792029932971 123257610887559928378144140918120429530 +60638789934299733936784399627375548216969992414721042760121400070452307937624 0 114153834385596405328713011002635197965 252208745458521911410440985341855391000 +39302655386201146230203589603929357089209031319943145241601153862248390961598 0 245881750656469107620797822329785123154 59493301861461980289711225197270903538 +69396760039125483326058793292015824350124552955244762939941142697368970814268 0 351157470476796464652698194217452216874 21643874324289592596755357037707639110 +20535950781569867376563125582768411086661820770085237838146059637004394828911 0 431953114624320033151147002488328164121 245815169393118219952697522361411364932 +95121134607611326587738731974176168869837605985253199468365088889980426998940 0 267010157077067600510381420530670486662 183036785513882089881706100332512907168 +19629727298714885008735622662701818401578464489410057243094275872510923397981 0 220358951735665276554304676012010710476 254145395943712100158581493904225904910 +69671638667537117488131073889392566761401068871481382531070263314464613860678 0 69056235331427993483760583800081151105 124754089428314407649437400438936726289 +28993459126274780495107896804579658024211301127568437350377718845634520894713 0 298455815268012824135884557287574759551 4794090264119311332396989692765353504 +89761943469243289476283906798675689866679823313433228732020830561517478388824 0 399463084252418649889895101742496902247 245564026403136720960406465193122249679 +109621469860163359145663670457995339397120167407940051359444037531682453172545 0 187706625031856063108595783789212967818 45267692027740481755416519834423823344 +89533059741796556701309514685872486857355849436478824201757288604908496315520 0 96145768181638766238564699773883789513 202859337082096288446273484607087706525 +7052142626234127131278711767465087469620224499263816620236823031378038261583 0 44684112013554671633482441103257848950 108786762018060784431611146634573775133 +76372277700927543926434312039671137469190189818239218068171440307970896950403 1 398384683894498832929840186777610581837 15060027996217390549566490505935444867 +57452572127789947239197063776344530705110919010085403933059675941259414747176 0 408415118116495990283414083492667118445 19229766959546016444656260010354720097 +41074937408067080469651391810021338217110614605478547192515247788168770147453 0 110829011585924686654593902114597734686 14514710357218698528567906570472249737 +39565873390410328712511580521194688755520392853001546259091627361794011718290 0 147931211474428516348954052223008014957 2588686350403333618370043585490009986 +9826678039325364490473689190515508401563329435284668997320086698867856934255 0 195797482441694993396111250000283635857 154021363903785179735743928631177139756 +47938676311893291269849706683448233314183144557151810435959258051065283751283 0 112924845918034404651101981769484556558 77045379775970796699414848534633760591 +71280459448672541213810878643784450624893365773407140136482510584158209506372 0 412415320742932991425965119325100414895 205398485939974504492701585811034096583 +56136494246493873092362864296885437030888105729827194451785041791252590886555 0 78271412631416603230407001933789238649 156539486922688533421810125234614178561 +97220457038178489409044356585078630069994270515711296427064275102646138023197 0 401677981147679741428515429286157573795 234901169159723393706688755534272933512 +9936686929793661024205298602943782206842448991340446358953916652771898493785 0 166682409278865922763927516477134526839 239939588260149385681946633859540110130 +73158125280390226328901439047101480593831683011232038025208679646122436209708 0 322252110430085864632835552835789590145 60608131422872334079399203192201475401 +75596514392433941652126887357147719521411447674408113482569870058884772895033 0 427813700056627402616435280397998944735 258925339245893692194870314468013162915 +92104326898045663311563598015572331332221632606001369655192275731634661716112 0 252475817205242971313185174077361301842 174574447298800844157595944527795475139 +115648415624298922529299626265007807924889984318586766673914644144735596839791 0 334035635946906900319782684662104390328 256222817555844913316965571302356597159 +68528965075314528868241019864294759209065875611047065352248370959089715748312 0 354249326587048369706079780379656990841 9071609176030518254552653890532698679 +6493720826777272010219754767878621856847833056483968151344682344527356401216 0 64690257836628859433491183382415517022 270553441017767597919733526573570548335 +74737977389066359520246737039956224501630934516588353461735475942440073505099 0 360722839308287381989237886713337233304 166365158168343739025019532972824801674 +38641809621060324783204452968806356511108081265403385317083815872191515260014 0 233285441155530910933312261438140720116 299072699675056268654299289065111200066 +113241536051480593921132180009716394974001276244270387047659027113495058805273 0 200094400389145243570747324794273688297 207565658324395748922212794711812384196 +46374949107912558987654406526413966453479234253965515811883186349068538659838 0 276535711503275908044142150450954657673 33488856278112708449059869370284955165 +48708364512462655022183777800085424406100552839256300979576485613708305348367 0 368673357190852255015466191911008530182 258458976139480343642907561687340601100 +58869771928646734995023223900369996889213611540105781588570072958329420491352 0 411733062035113837895936835540976533185 207325122073538485869564778454019659921 +13039874849372162693811090207647407157421912552340943237957446384961246230064 0 393467039660542937313068351274195467751 226387756467557126878258701523160727843 +70708709078413839327827592321009800838513525105589134781912260550211615101411 0 159043565849294663527112552234310939292 107186750345985662761735768868999797689 +111449249851085345373519329115394426512320426814411922923678842772841815203101 0 384430572339810087022687951908007631113 294436832047561625049851639322195119209 +95212522140045499358195003386605924239706903286944070930612552309939573361388 0 360805494026108653222812742046739183697 44862166059004280846249627164412641709 +76311362581257297595862774964350279200160099323972425271945563196205835969094 0 303495255639381138175053652058926850240 147946062066742741097451151008734845717 +16590523621523829769736593743756664853868516862373709939503106609882913004158 0 208261604824176268699340201077875676198 255261112577065668550437811354614953137 +97787882548901035642194649701428196976089279109645493340915956472244481600994 0 457193015607441853999894851510410094474 162904773835346057042994116462157841191 +114663704823400572069278633380201655908495654121725667785579346463920283409296 0 381271948388930168696016063222370669246 293816564276425888233725092499868077187 +32985426751861889171403733787250464591926680611683109721051757428207468166329 0 312327761779950991192207269050114559296 133372034223061973875210230339337366902 +103989099299736521783342108002543609878541574647480035010516899214401264986340 0 278122146841053418984055387878358497002 189872418767479637050157932551944381490 +3527133595506876107097506069805339889787471681957627449340931450676004364 1 266954934219050095529858292805700730612 38769145246993091471242421809643171689 +13293370375084288077583501982003508466678165982077681581390924288013707959719 0 281885492115153704108213224890658215436 144277596950497586325414740907485872954 +63450296552934118426272732881796529680506909960273359652483834825952267048457 0 147570686581787226235768457833119577223 189369491567100560972625008125086070690 +45972899145387151825246815392258217600518994758068477649815755739062352487430 0 275265603445678589526962279084078148213 140946103067560437062669985558435954882 +82330656483096018106836576401633239841977324384788059967721359041151169815702 0 305881519407200485164507576246510546748 129621832635451677012234904332380345053 +55730540626150645624256897294914682332980222970065671326473234966747543161399 0 223108849385083607428776806817565846229 91886705720723966022110420721142811017 +44549283782336095066238452716458632750509605976491086604093617185616609321588 0 367179127610181365772433399828597662586 254057959420006776575046104480279539067 +100245925008730237050671134987594848308169345183310656690160154645245942241248 0 375950782405668918317371559504373899546 16046462567449396240191147277777771289 +10646908943433990942023973332754302884629815533731958102349973386479579029167 0 254576667978949556928421957844750313357 130094179138678853803864965597631880479 +103587304434638432495116769991849042633391884154267399888847343227656731796230 0 188636289974654588804230034660890169670 42835283162122713714204269814294488832 +88354376167066575993348277160366047478718348008589317514260427740583590451767 0 333977769362414711117489325103889529860 3039337832526263682474120172101096144 +59024305636736502771300237642590340767743143372457469824671155661780118717445 0 312405637581568286052804871278773694398 160192164323873130549045293350751787533 +94120295846078920748954808534779025685722969743180247429061130027478550319817 0 415471766457515594715891531916663958370 72670682074673920288480111642618209111 +8159873138309217669080563866862964228785616840459204112884786799068353284236 0 264991790935708430339066599317919617361 22186738361411121555499974304801333848 +52719321052610279439415421995994876571564786532533683635082504687258579273492 0 391346845607595753595173305494480742811 44754223666222313665665544158661768114 +23755533938295840513568664280226440163233215948299092080129184330360754953103 0 126719629975820571690779749155191070733 6389752298441695314896424171518775881 +96041133724811678016498890729613160116243727246941689453686995264339094021783 0 425822421677633660508611736482147594514 133047033613216157013477363845438358287 +111550678004130900090137027475230635587623042441677972782459359411079562306194 0 439245439173329448889757715348494611404 170547327020930585965723247980207024812 +85597295507862849961520626818755736043255108881084765891998148676628130912592 0 314326011873035433055974318642554714459 184775567756723335978182565985407909717 +63480653689230567298356544478524785649072832967243691165871284141601742632144 0 408722207132465102620636657062028496599 133160836742033690019711025263407999322 +44128672467597561191063169412197191961681390379875897788090765542488600527723 0 388656933322648960404871060071110162021 27441283370309064724330502099862559979 +8508168076264790501177931721057377688711926058288101824626891346613540505202 0 404286789837665582775443868595396044126 122346768973913406811218054026773702701 +106138572241320038775844273503860569608390121556314749223216383807422451655797 0 142237978982088505094108225079735033134 196690530501022213144755982978643732447 +43242866282711402637861502494953456358491333350480131990239757677029074861028 0 164528650577110257446447365373467264375 69529445183422487554587394669912649153 +112757530902040184842315569368145208175378565850819401172996496297225879953673 0 431753026705079743168820087910862804978 171446391194555038630041205692488894695 +105370198210309305921020745804531137540622814092452001704306473766507128918211 0 96743192445993597195497518326029957073 128431084865230553348905670748476837325 +104696139954594227678267100930747649019126739746322431324669657411673392405729 0 371114422415470899973510609151617775668 123824986235892760467532351579450350107 +93887601427911491343622362187827573957195345757605647775636857588041850102494 0 337802397297045196235328163014820584993 199124546356947105361199869113271186440 +107566187693841972947110349437562490379403282900606391209936526732763903904522 0 280736928421498656548959168024476183206 190049362461427786821423962153775906685 +31136751478910238154400899764515824065197387432736527368249369326460064947118 0 107399523982468840494612649863871260742 56410518457592487419420889965632838804 +56085124019652481219857414373429854459680925869409274562445683838554277494699 0 84348085779658897693801402118758543257 266040289206282920073203689492605138400 +30581042392824183602384576500718663933419623974779695766817030251757303169378 0 394133738549190533731105542895445560195 2813545682008475161500710815072240873 +36220236068556817262838964064076042342893357704707404324970547132228521525254 0 178188709195959854724236317232609063559 255707234378619851501256260421725779502 +112434276493587271472876075314059077287514641175730815492471783212962304168963 0 387495785974986086219064678923071259674 316907330728237894331689604690945877659 +11568073584361057247705195448394696933530703750840333081588998384508055500850 0 255866225708373412565255901699280098841 224086658880774463604250658186796700768 +109127419757921833288978970948289662700822926624037530496607146146689132040710 0 285151843891186109392507652727112597226 92480153893169040901975788746955864017 +14651521271637868938198478150536313531898240759147126493139865661725396730639 0 156327089176794614758406687860249017970 191624082951416090097328713786075341935 +109610374189911464765469224410154821510183303978592254019157577206396010287534 0 203585910497860057711674500265693895639 285249710760659889328501524875525663944 +51527676522803236930081596798334337350633361708843276761212592065670384674905 0 268622388889864814655586371595714830878 271595526673508040605382666744039859277 +1171561817458399942248327408211263040193791518965458842212747217520781261863 0 49642267426232879385892903148237315772 25710226016428389584834573811218212767 +45238871166405028891296625815246887199664448746683992497957496697058194488294 0 388255770540696813057349031611046593739 151109306525479818288354427038520625102 +92538574916351891311609746747904116961427084979531477349179305741514235924205 0 191715153152090426513276595524662764262 87883962510531162921264330071977338752 +82796983592157291269526822075699992955026773050894333679430340939279131767342 0 278013205622022726598518699889707522076 297207735574200586318221814490845202389 +9980358829727683725219447364329120067324289170793360443286836576787847289891 0 91883116418050897368964424182464003179 182026432494666805687713489327319528886 +32899223288309294997321603186616975396638809127021105430584146095357594607011 0 391627127641067825359673840469633154819 105277937933364037132497221798542775681 +53276321418805536619402405060113547508177908554209184046566553261164403995594 0 437538891606232624612412966993451908818 253182703670448114292465674285391467310 +109349736141463266351262031011368750981220399964923963494692927726279569699119 0 306277880622527683383403994562080967546 61022036133647203457338007139905050550 +20615273748313120861371820958997675993396413501334326648438991518155953991832 0 217301439330647643057286625711368300958 159327456733627018843857026283919464437 +33389450328090883869556738038494628837495618257244161038057046079198919708244 0 108008891101912251717133883167808184794 203436851827263450989042116492822588268 +114154061361610110381464776869990282533268457219949406763958219121322885313848 0 157106555012329336571264748701781970019 348801244243997075098575442744234903173 +27774975674653116608292685356278567206603207845642983586277988130489418816536 1 253850978211435384390617370288024385775 19180144465940813841545399078697924283 +28787896695223368398740698847136455312760760370654783934819917521763993627013 0 226704885046832951022780177660004379655 164336558145751931136349085388232541508 +77797617712116032494837477064436159977131414225458798982872485561960009325230 0 113468220815808998860630478125707277612 297404524110586511539415554962278178629 +95734172177327343073093690859093848055221936922529463305465280873526099635024 0 390761854138908223483044245193810273196 299896964997534231260188185913508392202 +22473462270801729576657558660397300694288462490552860851003142933286545112702 0 308041764812962657074768537293251251223 166759699720181689103121236648854763833 +27075403632520717661445934873318854816899714309606236288900451976696999780499 0 410675962916521829041397387665369631680 89118054148437237189951219291465281083 +69179945454776190017629484727375755450632756082374692251488963934581562570575 0 296890011864318086634110305485257750604 217028254752595552648214809539943852512 +80672737875447305255225367393465898238665100228915358964973691220803983440817 1 410187632064935788357771551705418770379 15399599527953549684467376434829745196 +90267054773102523225314257045166698192729025397846775924790517541330649065473 0 112291240474550678866704576973303209313 199108203116889412438719620880152571891 +68832774023071456473637730076207717297551492236360217083019234618613898535155 0 189716908750150013705158152022957514032 284407128938961874551846376703083392257 +100617437689910315233444683671782492140838626250652434665017789578560786652025 0 271652800942490239036690328842494102964 271166537949720206644521887095638989351 +4704374881025350133822559153369843785313635704114606558131754380306129043576 1 319167195191720551856092201868931817199 48473673198928130061786748137867092410 +92856117454743736297214360959540958275297835771176054738565658885795071735751 0 117528869031500495598253941478072164509 299970525644086871574807718679590948838 +24391918712144420531111620200927801930543116475868508950814262607138193431947 0 366223830270768982344606294088232694827 191299002864363316251113487331137399715 +75564716910762322240328061134743607609549062446828819450247110181628288730858 0 316735449175174805705951733619757221310 274793457516266512640323670230090782016 +17069738965035422871878972525473314350932732642012356673452809359409666126394 0 96862300860567391931567018084252386180 194433301241845512241428751412928658172 +97244290271257456723705546897188280253927149069569972116881403303761734275409 0 415524337650474535684649931317057784859 235974258839048944180017313629326090415 +68279559178717885765572373556932571905373046310306071354863872692489278400364 0 254141011574931048298817999293844820288 132213086613461580579188339617230313392 +87537703441315585749623637015475318648379787139837364690523067500578205556212 0 340992231944883893794785039659663662962 67683862710956318989936328024545289251 +27806406326384355365854222422840692260580644133687326261741254980514158721133 0 83003908393309256341038799007395584421 84715445610494674338764148842260501999 +53473609963631164134571789889626998126296004647738624664118960093233400273126 1 361794401556898802885917574361279878116 11387145600983334436849624643319028285 +97853767302100788186976280625365651514045145484903172443840987271612524518421 0 463706292565990545336667677507393767130 223495595862146138011893871102183131531 +53545667113681551615084037715229869153114230243976427420523744603507905547576 0 179570911601491813094499312364405649517 83147465731817059043608248167165294694 +15992876104139990804712946633841357279003207106514593275657500171814228156948 0 130550567458574063290551791350560840266 260038898510224185420294064673990120433 +57891170934756576285960149062588086617155826692560868728697620669992665747218 0 354988344829305432663405179029191420939 79883983179415007189936958647305334447 +108507016381054457692788215618375336404311919861012250846327012240253285546506 0 142005969044858707066140370944447852902 258139457767245621593068393747778895359 +34054347371554773373316818139758914762484654455017095377167885054770920401297 0 375634625106018473736138025994705678825 247291419939099559153217697039287295928 +59597629896714670379572532145184643039705475644413319419129167341318603216882 0 176108689029100630868857627599050548440 68107086413453068272790430668000974162 +42315030619816620809852301724683326915644126187220499709568878515089662315379 0 144903314194145041834218440879322076708 189116136178919054900050272715586706282 +56834411481304415843522130950086922421619362050592573716679350526832322000965 0 380252389749196881158091075930843200507 236812928260057185841647870707647721060 +11867207783764866737987093105195201296327750856029932701176927416062704741517 0 244284781775889891027956723833212431534 41650495023990117453079585787684460124 +35315539890117624738055760639807858560004132841046765837168897648389233536675 0 164823624511608340311176257061845358376 155961175827524867513133721686007187611 +3344530483563804661884175505002990873580272838449859025682593843381445618749 0 339859211689612342056950279571010826088 256700023325155898427371831005639332444 +22691371900184277198978827912618527724562609013621485112788235041394516871934 0 283127754258342790431519993137979102828 200737757764073198952380023629923878910 +79166272776749813097399224670455928084825582739455623988159894875782140845837 0 104315741649374408457606816844173588554 156323031756678840620454085108998460538 +56677058757066627144778712395480371260434450240932691397322756917028586525307 0 129929659047550361053089998578592019994 66833904592763194883320468396366148861 +90687971780719617056564314366438961434540371836327216986843015134277084620801 0 335713453426586521528312996125362985221 266252023815787543502342141606387312565 +36751784448339212037213029825997446417147646985336574701632283573722517343857 0 333049532604297401020589849245996781843 179751292726401592437449330046256160602 +111065674725828913383252595260151362301104598454368531025314618798811650009211 0 187138549475004608977836188729650750148 223492885439666524887198056200454812102 +107613826009996711881626698602320589157343872850812328108281310638551732647433 0 174196840374911263355665130107700501709 216812060883092074159737292394717247874 +97520717981582986610224983010595972807045403077566498673988211011818439321030 0 112536730720610162525110474273809258217 60544940081127947114448792136348067287 +26767016443386334144068893869107110443678162458964981860625418541819643649623 0 433707108703394757683854518218065592329 253389158346706890987543715759017722925 +19745577366471177348802795218172366381313919336946160581461958308390099210157 0 127572574276687689891908216657394293260 68683048905492198910801919864009724231 +87030630902514749320579030412538277230773625548975384275447568862658504078428 0 60513015556377249753968673222436277756 57520918186392943868561216803559313232 +67403064376974762083843218995221256072647274866220739731113668300403733162095 0 250240341477039879062823463745817420236 109880742872719503024945580513140210814 +88604783189623484649937365868308441267582371786558144474682694276017297324050 0 245115663586574502316334000160610484225 64867186214115567745124734928831143066 +107587952779282975972779691364078055729609634197262528133469048378034890474945 0 305311571731845649405521306988089829138 47797138626619611328298088064547990447 +81007479598469834788738917285048235764879585010594516381183259077126512265851 0 178568901987452689221856466437671296859 290870889952503925029339827014516531524 +103942025879762871662597723363570657797302094566714381634901334031627161259626 0 333917617694251558093962249268498474413 186670201686913279417639435123177270645 +97045702472617009864845827245083516948396038564998709616505532898528886907578 0 450392927533291686012863464835127564472 156327574346536786871262371723702051238 +109085446571378964942039988870696908827672567588306321185711107431326221431404 0 273049594425224660146058694196938358141 304966597324214901944267592957457288591 +67448657189661408366311882784607010321214413657223210579844447291238069827517 0 223863768422919417364150052448185822661 305200237504588556987081893470100128125 +61393889256004662054393003338727535429045302421015987061736346023494321138359 0 139642924730896618443633222952905927132 24033198059151249236312549769566980936 +8715806587046228670100976915774116762176066239416043450356253405992727140374 0 167657474215549422489992093597878800494 128026819086591320356377808920257025688 +14060658874146412233268151448104245707350901245208108859691030831287852364423 0 173044282121100270795277278194300694120 101650221583689538683584947425285408493 +35225253115645440466241496916483435932928514791860818212619726873315449869552 0 242551618718528126834239181606507674664 238708035903968304869195046468972430202 +23233697089724457977726507086931313865507512571300354437181990411973185102586 0 153574766049011547512070084365625403679 197644661747207386576810181898765414303 +95854802733295220445401176703129257979904419786304780741487907271434003999042 0 221002538074363918844998528367097103700 217364558192770902162595356050779805386 +20236914476494018322390790508611193496705670789092850990279762648257634492978 0 139781861532401280337441786419235818713 55769131816665875911034863627494930753 +31144727217393170677109142556098410147096548725643930810750442677367778673504 0 360397055341832956780593321214377035117 229271358233860232947307451894621896646 +52848817354371605521125169394037981136943251189338590336091525627014496041866 0 299409694808639423169004770945503479275 124051199146467131804689593741332887096 +19741999897234716176027921070391126105729673520674483248917647325405678617186 0 195917543098688075931678258959917870957 148983678340481785007133011141451535075 +65186794784873365720168005611085300394188228730188268238653132245602307223050 0 312516362827147471575752182981559920600 223486706573969708309902137077296117581 +72651221410476954877641403699478237101078768481444610275242048578024433953683 0 446643755841579740093164783154756897757 190542215644287544721219888315486297593 +81452621420321559163530915654058069229553508999787466488719982111390050085688 0 91723257236904013224277156068114739464 97986541700674684470058469923563380594 +98114794561930846401585184299110122698043355389677478453731525242013566461291 0 119620097629672448507834226576223222879 98257441974619149958732884924349692241 +65092365420388180353983031354569613905635378956369503636200907939627874893585 0 195572609084996478102781590835720345218 293361034443044462521402627570900595153 +110148237012030542248977227522026312678430723828092199672004130758096241708475 0 162395984242112140417773676434814790529 279246569702567301096107735348611991252 +104933770737046776371710229929443919017256094313130555957749040457626034902652 0 351847248085991617517387859168004833714 153696619721981517166435756112501440904 +83575258934090314911410833136424360540451023551251819116340800037009918013064 0 199298866261109788468905294886024754229 141264777838271494607398318903400275981 +21982970512024105983093366420465810710211548215695715678124204877504320134202 0 90158547548423856420351417213779843896 63721308293894342579810761854730129493 +26808519663604875623045178783955672034103358206123550799135443660101079143318 0 195621826964255769778371165610053400559 289803777136831383581941096298703780296 +67239563196063681058696748279784515075571858257601106514282947421860966692404 0 161654811196005087795084300190146930915 117084264602809389981198277192855483039 +75663214383075831846998228879121847639478615434448352303046991409253284417795 0 148470932993440643902855529678985075043 98485018938818847347119031550307015125 +10621550517447982702503384602629762526995711943121643672033092918244519339685 0 365468073904445110844019672350047053875 201152634849584446496735222072693723046 +39229767314758376122402156414374562096163917674603761813062937460939307894124 0 348908297119684232234903092252388275132 239934985920735636040783494659502037354 +45495865392833497071754303494759832282081722446593528739399291470273486234552 0 312110388795101002877346899093726176169 192825679983450652236203772354949123371 +79670892905531085419913521935270631700250613651663770195728323499024530660873 0 210162370697217234421215703273070531188 306661763725046281286237434294494453369 +57056541014408286191704504965255228231251710571951332051794335629460906071087 0 229052872606918140739769355192410595038 104295116597138801438511495605935453418 +66489689895324104511841368968615553664063611682577226781108448613160958924201 0 238714773885308457296436990778600061265 125662083510384262149277702909846353581 +19349960800007918960284071220267576512063472875403953805581369560340702225823 0 257577893292649278672615754861042172977 279391650566064181342968875768574150392 +3770428481696521695629637228764834585351588681161806266945220713042533081372 0 258450694855479302866984563703827585137 90313277051068148510335860580316427090 +16655972652212658524463703360167721559918595782166223099282033915750450634747 0 81431785526151253337716301700025230736 211899811907795372513723606620533662682 +15281261424015088373447545160415443806667695200490919712558645069526538251642 0 148585836256956469096173074348490272839 113424748040469605075531348358738139495 +21183029428838089598253690175301223224817605936088691207691660255016665943268 0 367060752524512353549718413498687946998 56038973271675173053409043913844406554 +44115837842825448434549949698971153727817154009211720076302692326226875201072 0 105535877913609561555778969605702677070 300844973979534346807051177071161853634 +78626327798504642984962645891937758066838862558506382338972235645638971174856 0 246165090119111545612927156646896385235 288677534587881032681246041105690796961 +32526769424985383942363243247107228518936860340074454290958474458143472747346 0 375988943561208757689727145153563547758 10647241992119623222307833070046655469 +26596047925884207160144942573264084262121866760597878600686419125712929074955 0 391194367272001125511506972928061681173 185156507180242014924612668541966890820 +10949458318698722084252765648689769937608906280707526590838295900667580749872 0 127898397058044503652873494754606540308 46676927492687614494429007683603752090 +75184508958478729844952482971037946616826084228175436560488375335772573876751 0 175309963118677505049511762573350288585 275054624955694171155929459886735937868 +97600460072181753064839054791721345443851631118606954916263583270693056643053 0 173966795124746722274255686047893934613 303492989541788391733397826948913520251 +58115611490193754297947555010642570114117580581063748055913447216152881084357 0 258268569924055209490839969739329420300 77043583040863117548193574554572392591 +85124180393272905674915895558753296355842801342314284641824664301011394328803 0 320835982456193763627823729044206973704 270782301780965196307389272723517897257 +102595957646687955122836421386805497109907817596423189310814391456427203919651 0 265943019063605750786866684403729444001 232403226746343395823331947852924002898 +26954293366450377880871890468501758986583997109251630007288149160104473772815 0 357527395391405219209861949305162610172 69273280564708665908132122495504797431 +107673762321871862689059836281272445958378427849739910268210366204063642153125 0 381333688453210199235170123177305488914 253566769985225334229081551742023353388 +2570827043287894223445313410527861125027345783339384554262531031550039089624 0 102276432201981604739955738927783151785 194459983447765234230981257872572776019 +64453762035452472328485255457946983070304288166410353704568097425667239697112 0 75282156502701009381083553219898820748 221110973823590274568584000031069581065 +53014028355904453624047429228965378200584519197563621350051599011411329466072 0 101717764127038845316154659074359409224 74140189965437417028291885941704737827 +93649369363969857206746345484923818053818402172900245534166978995388028644990 0 379141370903111375224920202253396861619 114666578072558715000687143417006338052 +46718290806381695498803458419997739539647250783079629691308147261958398634111 0 118030142133865350956764805221650615211 85523947396671194209442422976080736768 +50102811083770537176486468930592548716563783547187309146472688586744875983992 0 355916093106751144903087503777221970719 161881081744259601269266863676470165403 +102126936896750865753682634175161279974642264535237798516064599252467295788903 0 225487820838050029442282561131157857170 121029265991836280506932224877269267235 +73336428268460229968389815320071684211629119689484427210985793274986198963017 0 163234570233663145266544410965654166106 302015411525368225865768337360671325832 +52223815472254225185035199066604959031863310541526425429840237864106277253783 0 386408598333924184119072238368372098044 146144518520017711132131583837589962675 +65717103451629307801017952964793798981525522282159046218949060849254759711352 0 75415877314585113274886280591648887691 106184462095920188580280485498688220535 +20965627569147404438871545189520920538630257067785837611959919126629266840986 0 254374814982339379953677291193842281811 197402344816223577300681183117286787855 +38614027734800104453053543208698935335451220618619530903573321856072097084841 0 231317981656504437841554829369736295978 283216907503636797036029728982374938917 +115177264500695842216266088290866194903201106452459915024795908326694645594165 0 443895921491118835030184876644808146107 282561279654298762021618786702522392582 +76432165085513422033999283852341910293539361404939993937579377734828329948682 0 351911701215885359178312570999694010794 183104601290172555418214284121554975863 +91898805803407156978511089451294491218938267573054915025623562102191747231440 0 204720505723755238155100441734779542748 64488561835186737724838310286426097626 +60167047172623434786932240761881609761323879307553009306758784701499423813753 0 191071213235230912916909526857908979571 25431402129725525388787218508992699716 +99008851708131347166474228806886444392979327756180663239597130492070063950191 0 188186868556594691893314444832966042671 91413166356694945391683782994700922343 +37162392069291919774367435762908927629673117170839583803395903518964084632829 0 305192194391793702065848450681584266886 63791378121801402865198612282033180199 +90761653875218596870771925495100503285267160789899754390060712335425617233165 0 213968446878519878383695697547837858005 44242735973518711841913680713481115497 +106508517836487849202297598649896352474024856280345186373673774458679869816886 0 110233599740521035272124801672929637979 240185726969900384630099114396711616057 +86767363576052643973574784810585838172057379484701103810520227865587077440719 0 134133927555660412462248765942698580859 225263802015785030367033025556685628254 +86288787795019852809558738385542665195774423438956224531504072877088598640519 0 359051606353910557570442014535402106620 158794399369137264814225399108720671753 +23325318434750646932583037475350318468527745122097168835363089791450133964208 0 56783801070032041852455496726417738642 216640028534859642285663550149199382669 +8882993177171056292881197151120616949156998889255643332108552754407862491613 0 64664670041320660742759718433065761842 207793673970147999331050208045211921709 +56784551382436173319670882307183698551497681646724137356540528783082507561570 1 340892521698081650483241020763078220547 4376091791147313809274764932422927033 +103655921530802330921736126527297404297312133515071015082662759096968700483219 0 395600540180587795207905049220813255226 112449101348775375228038057868833592886 +108834121572330844660460785977135461125979244380633483038746348580214572493901 0 359759298402859010036267971625689054008 316592607460051296404092364302217766197 +34367752295176310918849708037452320330865770228308018069119488172058512006522 0 170722273810370559705134521189597077691 290286600369346569711684789834921233189 +106323973657583189988676842093914127511005107664496858821451873284655474824724 0 432254175722259006518728444176490289180 298667442826272252363241382700237490124 +77047279031648853248039444323422732910534388560283245485779420058103728647060 0 87091238395411236863479952403877359332 227554549653532519207613668897293012005 +29164966927635254730462914585221686560437994051060066064782357539280963430281 0 125214793497117814694133665139140656808 18191755178671066657296974570787587159 +100871325706559405808750766798584109699311653300053048286892990766993526112676 0 261402323670616826621516795517292195510 291457235237668242384611700369159805191 +25341764543975020045484922060278498627443818472196863697872592264745968486088 0 304697595354882817395232053588095237352 122382318555347061704443376325281787439 +93122348016998855258683148577513390276710973590756380055098877001403425060853 0 395482620264009489906707850350242571217 14479282257231624711583363325003034963 +33133343550129014814380530406447981458815179807125451181297453997511954438529 0 276134812657542808927548602024536555897 80668155120192242413395190965648358882 +89625209938935029032397592704302183090443412036595538596161717613487454056674 0 213301313158948342376343914240823556871 147207374826196912135640063700880365096 +56152072043954799410212582661348949272421816777123574354875604116161057930596 0 428296874216973005737375623891111270670 105366445481902223570020786193741365868 +49508546006256596608782086604979245508055904190917842657563205907600118564063 0 355159816943398752269668389288569968736 22202916433113948895343068026368153715 +32924962443801255988005807801243686938787054750257294376605603357433413352780 1 222703777814167956871248237281047413838 7903875573541315669235593039689134286 +74864036252412673320720691104472709483695220925662955333251554199480540358196 0 62520755952116417853291233891379303544 112348845691504252809857891108656187385 +44555000793169076652023294937256241010064572173303379968906513874141919265006 0 356657091052998327674557614397399272991 213583091115644069877218576366600930420 +55020272950497323892153468326136909818697821493271141559567768392770706164958 0 152603318714789091245903245929310847115 160638614513486283924666618038421023809 +42801148554637377306432994885371676807714756396721687353467891354871628720825 0 87036494798591887097533900866188318294 169085512516472840448734275080054840165 +15961640531482230402985184996205104621106705938515189707268469572950484347412 0 121270084695350616928284378685429962774 27866526319634631496867636651717974721 +40323387382237525548667872586840553323332771592782639446555505901645954607131 0 229621504432506313171947905141174535711 136341963267689811074957222770565109172 +91636426773415205178703712836493118069562775834604946065775455441516945028105 0 192779283444198354917307811729537263748 102609021898887121286837104787311569022 +23952253728092363526302449601461467480728906066765520931029631158343979390352 0 184497025751719664620493359678973489127 209740247184245572683525359691574647321 +84385833182369313991650137685328182071794024611335168152611495792032273756803 0 438554849212697966269652187808382992945 158363992383668149756742685012061768779 +49930895804218851133192981583975288308721482851596279898309465976790873544608 0 157429214579696008859192128376654241135 97471858925503070238403324249368863241 +5014451648835867989289243887980638089111519057834078477725381874192412262422 1 262052636132062185233877894590668206675 38619602836076605869022943235214163617 +81644396119528266082585643299841965032272010962589398227685592362758977096371 0 264710246796889215486805782733470318298 39562067146174296591721641734749883439 +6844240981972212373633473706945454773183091067491888888056063303790113524839 0 76503923908868235319175219431276533887 73324868930953322340927627903152810488 +113483801185522361607572341114147933826060120434115562418652334719852055239493 0 239106492814113346987033672767307139418 225627640566715477031144920233950201785 +81405335245930405058358852324145855725688040981240440792362736820823311152129 0 214688725142424259957718391133547490546 56809735000289657154045179864107608774 +20642490804878140308818971512760451133484990257518513677882457897908816961868 0 185268268201761755328927498713347423838 125414360766042813299936944572595608368 +44557163535792683564777736441487996038561674684882365578665875863856112197606 0 205630139898143916098464630675899389908 193750920604675830202484002901683363858 +115278353912757645328890155606912213333094722032668590266128760539333292930992 0 338594576331151144193843032648316358479 117953680074683378381622619747598508476 +51209499219244761382775822933065726837670000230677476802997008495655477297142 0 404386680014662011607714121166934267573 165498309141408806192944486421978695442 +9567965049196464596612584779631578047961932694866862058976575676468200416077 1 208113747360217067421446161214919357638 21811166641162858620890089362538470107 +47501488374181683826554902914693414244472273686766627851069882645385510906844 0 295394859582268287365680388517587135899 259589452637284502528659877868040399068 +115727772950750806331497074997046625963230863558536653302128035507477206595536 0 348396452466659528450001469692290083082 127789879253433728194222428682848791639 +51027368772174211512668718809044413396271280912726563615788715161859782410533 0 354362376759939442621588711100516483783 21611253834236862554952408811660549829 +25105878725042329609299329436350834616134417648174490684236182258786537539157 0 246152323280759151857278893775495381493 260641107100361248041892764272352672339 +85898853059450862092636163577155349614430804084119292648302715943400786388725 0 176618971440936648812018857872907270123 308223900231312961297370100928299023959 +84122485180295307795134547401946757664268665105795214024273206749624045166034 0 114356497472911625633020500171282625959 302716711127860419385471309616837717265 +7879689483444819349552076588079959739472312490953665956519160835120759994962 0 145807179719617056975662532883908114925 227007828503585525126886698075379896997 +36453550928374327683383610762479851341313174506781760870336389199846369070098 0 346131018282738152232585165432981969571 255644333849677733070070189532639492552 +71727706037840101844190931945305912544213288619315870793450913552282715150691 0 362200932311303941753536381684595511282 147667317453444191459809979785902320176 +74380323058428798898714957384418986757484788616195293653049391036025593375874 0 252371476905161635656338914080144374530 18863853000792395540130186099909732298 +40656260367348285111541826776373606575352690215253121543778508979033811577626 1 339116163891667166196911610029734524065 13445782746241833186287597150652059912 +41038169348874997562729726189699304129159030993705315910774415088768050603331 0 166286850737512129403317485621076511732 231551055015109340103130071440275990598 +35453996121288003579067361729766867564550248854127344511569296604013437115872 0 352880810979135225420919651607085956522 141817075546195874862001194961469844917 +59307692411778000109438805346380281933067080712752805391606417558806747406113 0 87916431017539545835327619216401375467 133157274287499120763575215430253316815 +1719874047982963587912996899426193506200401116547797632908281925036770571356 0 306352223637219349491203878091814820824 163699730971862873996463387321198192908 +113769319475258097198604794372521838545464847209028555938513424334462922110743 0 269279825668491238190491421250392418253 291527923921481633291715142259202709288 +99690494789517180557157894527007716859821978357972001969795079848661464095055 0 173394825216935602171649853463992503767 324056741816239857416928405738420879162 +100262570929243863575743455429664707952932948180684175364562392810842002330899 0 285462592154447153221855078620235403610 191154038773441489260626243490611264857 +55338905403404148425566884294180213074467327966796631243795135319846771288138 0 245376693844791641689743743095263972420 75382408104192905969582133490626841900 +44629600213957694410524664832214523966479800022656941929191730151627884796366 1 395930816788569562069791172641177847136 9005568385118306713552632284282013672 +31738448221138075212519824575971956162724305551546609480359617523492446601297 0 224931024019221378968219344163942649449 264110601641078970191376310859096054530 +107791940995456777797436364445372941158188985812524136036192316550195787843859 0 128027600738870863405909810559507074993 215995082102652841307609503583879134660 +26140936920699054369877009445850291897186807778418314382589865064374177448361 0 164988420752281807926568311360879703983 105966961875736976805722785866836769547 +75531295543497868067114205027559358983254141035410487295582035979223457768146 0 148400442982066307608721072701345681970 182736759942413011253540093923476380514 +97234034660732674985842629144468950395861662666180223813440437680562073216369 0 289494070196529110790074916599162968691 18907718973690620412654339448460739009 +27621475956222638170230750207543476233608733925167330051543407426007294887365 0 355568908773213958207898506771055577267 246151396518516832735924166642474565065 +30749286524315100331705045403623098841231039186663782347623818181538081994408 0 385833098554812891799774225573730654999 203811281870602054635814011641165762592 +22599504861630977042137304681022012220432367005990022013727718677053680390501 0 298536991807845529843125530923503901290 5002705537714231965370242063293815888 +9644285581499559007385492647807405905468401136129174872424139725648550388011 0 41379260300332768477450440632143240381 149757849909351817002723983326890813701 +79784575380065916796336674651106662409589255580410606860192042657781070215099 0 210805563554233631363020258390196398259 153479311438203322145231619886782242851 +109834105808285333715772579996445952200730791225540277994728689375436156492534 0 191412505890247669890582981140222131595 331424981110373640814488894871060943906 +39182629844391906537221329941437074232446514168357190320810438837086934879478 0 78943366716835314900927887985748435540 193241100487752545170890153073012601895 +63547704406928919599057086922039280316244978103291718398382539715504446446450 0 321449013088412991188080860867780677294 149893652848837665217938050660592856648 +78722467367080824485171519214462740340898666017713858637432722788620947272853 0 95563703734265791844331278053162962618 253588856652056338450182347761163437346 +5322707845739853746910672768681011093247176616835822531189233937314438577272 0 159458635720413664903281651718642563281 252327711982909790327247463560794995044 +60126673911492257457036851265242771755753107147934000134861715177507091926741 0 378821743046106382087442320447779464283 272776134557454564113774590583178442907 +93154976017644256420088969384064641549293701302930781273250361171766697631038 0 231826226580083142648600762593944346657 210814685911251089404442213916216036237 +21356618620851939784401439525854921513524128522520019276288253589846720851515 0 270940194558707207914461038002537041978 262036204453116316580271530149041758779 +56297686180616227993826723603571740356864215706803296653440220188328411271784 0 148220342246216938378041721101709342204 234442906150326303124104279840848841262 +94935685469764623325290044211964105634025713779154922689132815992661571807053 0 378607314884068198625138632506759275579 155252455567985522155313310075456397502 +91329203251471192055457792733492809881915503375411845889129888172348167227513 0 284017399406523130621420721063793371029 250998388573010673106489204041281541354 +11207496406972806856442570559820555577081336352595596876918775178773023241278 0 230156497339319594265686723285622912471 117157470483899769696195985348001517010 +83175870895796706191199265326171740461931881505745954057428782929889292461693 0 160342581538550327444404105299752887002 150738001195954517788737609842260671965 +97476656096907894268079393919386499124008904887457205563331674803117628008045 0 62005759719808570184941917642926058421 67972248227269500064589990157800960981 +88011175900007877102283744659116300610050194686122126786244397492058495618123 0 83723701382753793273581334068913519993 143535995058731834354949181265824439074 +68511120902451265046606661652110236756970487570540924463551735261329291456731 0 80624787906411209560963362195523331013 119802525893839176918367390638310023071 +49338809410014019930709919528457362587351916979551042455795308692778761852955 0 355786534348377680722335799794846261900 165943692288053164755664734668051032268 +13860762726997297462079775497008723659556298082947106804634672762466342846330 0 206349661057361467509823013107472496122 69663284159562761334353823855474606638 +89072153540891670886763249665404124707564881025455289892332019096682886167121 0 350624455123260732961213556521903929836 45506953939106465369002516184214158495 +96365215392718189993065796130961112928741561723584501939857728104126899493612 0 390558648756689458750852900188605175710 48053864815320171133249562404021221559 +3378163908284305214516617456703914296085675966725297690525995225669719823524 0 244144949134294306946605804084137004196 120672310931645743872686604544069075503 +15119847777337401487863409094394855147816797483491774698861340029070934091065 0 101486503158745920342464304111330743035 166597344509127962426536660503527268667 +28750229545304109256719450091973100039174852155583148105283310740793933847601 0 313964879999050690898365084770755635409 17388501199629747212265902492448654177 +54316386516879954955143602983713047494423057372833903183801650661915217751937 0 390684650378197476623740337039959457592 130515502026059635809494301258817572688 +113325163019662746430357569533362431611217578674838380118214542409188394686561 0 337004403245714389765573285229339776302 268920747518322513250045769665563023706 +109790267739285060198456758238161152260014442777436413392751338094520263382458 0 410420539000701774986927520962145803397 161902591648095608810407307928784491507 +17323700323856648100589269944005779297004596813964871206126621947343226677420 0 343999917849124256268242615333285753499 104626795763962583704062133367264067812 +99625161184518603425286848992428498031935487157670087511976504999264748258562 0 447091715465281946404919143057625805819 147233496201419630907143412696672827663 +19767217821257916435819256389938276894668020029365959975851301773808477639765 0 276317197176808790107904813523192826176 20293314203553827021349219813031543851 +33288306255189154748355610827198716697714256681539990095654965427210336360825 0 411353885215647178267308664689140456599 72694595118213582336700026903886603764 +4825307244185329682300154304518342726890201503647695419883006017192047396947 0 192854390745491167218179334467760723607 52876907112410305059670131107289062239 +15059135847794636108922587178069497236588072875426787466528215741854135422441 0 161550459447706461009767962082843869415 272698956712257632810309242569797145150 +100350119253238111848930011718842214268516453707364771735380826624609149631957 0 332001726528692831346778571051011095870 28459486378077711489526613312482001504 +93683729367923247529548543563463066947202052949428732819227573428218706611437 0 434940704775381732088982690380125925074 145316724620785135674364360509352460873 +31988955533830065578118048126775667446371586144237182188930190337540406824726 0 350904939293610421403142212140726348128 168988299059209013541319173412713572258 +111658720667881941422499943986545252060149624690661325821653730829615245939090 0 356916906603731329222240958899012598545 108086257776845416328675455639393636876 +6664534735248848901748678068202083816469285425155608687193581443250704204943 0 226202511278956774693771325571788149947 269592790787167163773094391501083088913 +34482295467967172119970148457167396550362083701617023831786130535869442135246 0 181700281651850872088693804117697098919 305113108614595234746277629365607280453 +109731748438053456086073341352408336449756468969385189500811679496747519805057 0 189363180594420756250068520400009823968 47116030835988404095365472217296637686 +4805725073773920565627223189323061730397028701795149845048194394593234208347 0 202693990977694803725425587569960305014 142478206985210105362933128293363320894 +50854711555938338298642042679831145526543224019417985864613750748875277544383 0 317057991373791913817475638670005867425 144890762501261358154803816194148314134 +93402360775194005883449571282624973317668751546651753916285541360430077597558 0 260658386832050362494958271723003073829 291790575041754075343143464195468360842 +110915012940916190892896711713533780701650763638677102900269197299393143643511 0 463953670574065640574098254215769615676 216655554621050423229048504197979816791 +4588802006528376570410714416265629726873705112124517613552812238249349917639 0 85776510463033730978874275350059827430 144982292893618097586200903961488963396 +13342135581600564673308182521064586516727663579595706251320257789163263744720 0 191435384993643380482556708815999462151 128837894638733372034484928982745524114 +31664375973200218301884442627588554345427575526248869372888349361216745266568 0 337087523888571369345728919567182556934 274102765506885030244891309543662293216 +27517330187922365387954535169979960044463196732896186343057675700262552705995 0 294090152048656955672670760880349630566 245481408538868931281631706027742971432 +69195559085311141220586099119437836855345732205889535051344895372156143493679 0 384150996535821894437411180854422956547 38607357231448963462539911393931415973 +64661885612355790683473477264995832759693384773428626750635194213308169397167 0 387961337912653145874332214941163300178 227476114132226191947425472596035666964 +102087512765186308264509014151299537991994691241460508792922865824877809361464 0 177612010980338159397100068519971031551 130473442200268919269235434079422209757 +105316782509275296438880624286793317431421576660362869484979609413421081438042 0 488639085875349333087228008277377291136 296580076488125169467648395512258117707 +62347575040618224022379683412076325322138665645525801051886659477295914433173 0 182692386081009394243753951868527224098 23502803107977267828033679909886854250 +1355729387922619962827298288249526855297198348130536407553863282143079365037 0 31321773689191716596588419573354170657 55805413947938339287833275560672587305 +2493572742746721530961960666930840089337285219095701611866416589167364098942 0 124536367567514105110282119677007611697 293173975574284837907022806893758512269 +109764204455491437309502086855300465228232484889061045920888512591793278944085 0 368073835731265170424757637853992497660 78744788960418752756719748463738600487 +61704336118289686460383183588071469565979262957045526567212398059695465704237 0 420893934630725537310841252325648882099 209904629379106200715410506344244865339 +100017244055424184521738206191431880848333393586174899207100863084561572240871 0 444616051391645874926332488154855265884 201914970284451290138040501434735278871 +75946829954892236434479367855297795036432394139418080946975129603309886373579 0 74841895220960337488755563937704136231 46317010462904574608707166858795555106 +459749964063109813516518306269083580585166401183748043820875282452005399017 0 99272165093983248737654230512511036238 127075595885065953166216194028356009200 +16336072435757528007930469153051357462914215695232596295640295555811956938105 1 277521070635226386662549321063152236532 5829276597247552805840654353501736861 +2991947677479483464221121216361073034264714510000931451738437439578460244318 1 131520562277157366268375959544046811388 12882091562952216315914896664208877557 +28264618999578134491045235062229091611675509505111014353383828913234459339132 0 271806192731236850299252879119617843681 15247504410696281993971358150571052521 +47738075636155378121368665709361816292316607056347699937288801681294171120567 0 176875775603542367561667239496533797451 203936350932614007196253420759753703924 +92093948214472521539248604224823775884163645166633430401890374319439806330521 0 370669153989950674079082243082348875733 277846901946468410635204147878066734300 +70996688707750892957975721551826448178256748357240191619265927898410903919772 0 433041366945333195947629476181233308654 160204506815234492754619410381807169405 +9918910283306704172668561140979813208666979393627720825459067360468433745403 0 90945818380155875689704339709110590984 224935911367335061840719017295225175663 +30527884009727748136052537048161024981352797748179418500303530280326317592119 0 234487769736867575931321267186246678016 26768352602970656473669062291868867213 +44580485104296149069292626388335049997316521092452398561214291432896731385977 0 375446209586781356986399493054004062224 120232365026272644674002298111796325777 +66953060921252975506916941738884981737749497791184370376583965839700643957861 0 255776916181114386515859118967458075105 129126010268513536882955392328404407061 +79615845873844340481449061692143090207527973886053041009384112340499585480929 0 266637865615987209261698178369963849530 264803800022276097659568427782226681795 +96753084884998997037627618701481927961030660720146862744051811197456786485024 0 375420482591755821325330836679224199602 251290230891810449606280513648626819986 +82109497444191113522349688323935053761395758004739728998015759227144487247185 0 285630914794354171983638649373487347628 175265594616096233272280073956344836051 +41360175903619953768296966660239255998122629620981954855809251811741903379107 0 295886550111595569133074880373530082294 138977398502656435077344069575322617810 +104893005334848300475488141609086607733911671881136035192488032759425458748049 0 402818797684029998830063718929199253805 142144716170574726595229225317802121716 +82204896591079174050484222551881529273634052820263479200460617596729942076226 0 150143293872973824605668855682471833131 269344926453083409420126213255347357292 +112319432158757354985947023444809975333941585130344203855362578054435141905797 0 302772041523865504650085912503177218926 305631340373347262972098178447343574195 +76357025804461380479485840026517121982310871834962706900593145346525174604677 0 300994088768453379299583100927759309277 175729205585396435499147194695960922836 +92701368725603321870477550715675103141096602865268133694318628547269593571787 0 127455090903125178886115303418818946348 158600200429914580130875694024466749933 +30366570691769796982876120321511691088583346786487366789681683572885047322416 0 97864486336195757261374795222430117242 157548945296808283403082235784697656218 +82827535489871339105390846239741419695472886767162961465214201098192979324676 0 135515964095688238117782863237758876159 77986666142713762986485825217720139225 +88201568407787582468633282284290894891262928424364369572410269670502154524059 0 231643220301371175810318981984598045462 101038547484748728329627647279276397219 +89890231174678100470861032021512297371068994737038622501343728899111503205368 0 163657499519499529738087234643643053027 334867309182802483821659479821267369588 +99143940399542535485926882175711688290467131655787106287176333090132199798361 0 323595042245657184386559393572558161278 90896228615594768695709623256958861679 +68652457726470810797054041683538825810459893183861348389112229455272085403220 0 385276219601046184931060986054979943732 176534606246756265460726718977031208396 +113019181577238971206061976769723137889011667503179029986089064097478224818439 0 228061753272126180169954071876821609661 151068210531336838906791636084900495746 +99961552203080381421963872924035714904078318350676344127263639343532606232484 0 127490123986424026229510520151256605526 98069383460288897058819973462695432334 +44096864616939528108157377585899304128258791801829746866432815031336295899825 0 389150996375995637699913045875344505675 80833917397102770121007991410012885873 +35664583367629513423634398043596461918365363763258554696841607525412220379271 0 414026484209468716852064338937873863718 118142288470327059235520095225234420422 +92096324096205374859858623233994377375534109550306030792609114836085665144436 0 290163208464608869810796024523470431458 111104603095616085969435688138740006025 +107606922167558841029985625987774137156563930178830677833604573522055564981132 0 191464891356049351264615676229021211869 124427885465991183283616415778258546452 +105510904845819680158503837767413750629999954387819132257431443625212021028824 0 86786642102173649819544809871554759390 117790109367021635805526187648161165947 +5398585960352522728378574070927682328415753575205389566230423346770420703314 0 39637059636871892371815774737711355703 128924533649149842350999040593173907385 +66041797859056160994552888396449998923447150626888623558165696328564772702111 0 128471908157115025282489468281448230420 168895753020183874952825659370860005632 +29030681398382064687053997403555639359499940938501513104867132703719525297836 0 126516290521697857497924558679626426461 241834344266193259666812223360066486000 +101210265692440511175776893682878407182849248083537725762992485088244276243393 0 317947873438645236264500221984442830631 314243165271306717394627999330142977717 +60777453606065916585172193602644551752747670096699784840249049283380207362280 0 205786569858492786628725953610938188330 122665030839531186031602677379014864365 +7831561338643043897186129384679068770905311821359874272993960143299919848023 0 283628191597583851257389282037740758903 1388168388747006517890713445012168457 +99460161590693020327165745545640702054494382319550225936764570100407625900159 0 371557484498267512156621553111362482878 276627812343284976102910361525028122188 +95294149675948540131232277121591987735687837930211194306745729363392655291724 0 403632299294837968549679470779455563201 84663610069996136592876718232264299749 +54591824719805369762006767475661998377959391653248896414170843491332900784436 0 204243986230697768354598323159362622424 264045149207039798457444793792838782101 +58669266342349309623227276291085718940622836695668402850132211636716249505058 0 356227619239859734199279135905703448294 176114844039539097880245207281439323879 +45791520394318792265185973845473005203308474345186045575148760580387205816712 0 290613758286262058390341082124563425114 190070708086372775234165242152115415793 +6140152154482115974992132192169048256917183784811897187890196297654820303342 0 45032738987432359348721776751137449108 164843215373171149273393542995934292383 +78698045666200483654952978966866005287864603318633340468291781008294141138777 0 324127962403987158227569958242559200045 86496278591532442136596806795767705693 +12056194848471113762007341171230198683501851805387562543935496285874604285929 0 143672048950984076728047680991634573991 256449886264700068979593436531109754571 +56381737799364298751700887057521089740137936353711930816208243632048627251281 0 269398453158762442505165475487560658463 173325066198857405501808985634532463021 +103130475613504946652182329656645774924873591731544239085947045024897491327553 0 380156535565480773497885168783453669647 228570364072038864497115213009521912571 +27902085695334907428799703674633951142730656473424703073346197154332439349448 0 344704217856955730234422138044604020732 145700488479355450125255141168283845247 +16135636924573011751643704097424547692178875191440927020623722441189944018061 1 196942905380271763837716958871912287371 19029788967191373672926579338201910014 +66075322469376679768901578718596836239362466403415490155841260805467114101692 0 161594168895270243506521943286796455562 158078615882870342408874445435584259841 +57022047053781076563463155651519251603508009652669524144016493530877281078085 0 309001557801096534081419607221947811036 45344393972446008897404514545464722812 +50796168741464189605666384066380929776192092045233464308685330881437032244498 0 68722178303617733321838947738083101455 95835088132611489795123421384040938586 +17598527837484307856817548238134448914802708685356112143155071602225858508692 0 304478213612589432229736130168319993836 31302229470981611824629273251179524619 +86718557405134110246729448706577056471395077434656749472678377341587128639207 0 200826123578921082846686780106939510859 195792670257685579693444070336103325572 +43743332333040801164138604378355929925867438656913962175103999863584985883706 0 147757333823301115442934983885930185248 13819620241655022262490162523171409494 +68194228258318122992280377369686145609651246243399673223493090392101631090793 0 330712859378733748653517369519679675547 121402925029605332401434700324261219268 +71505516810374947083709397449720862323528995471220226846675545235935223968340 0 259831184801153346577924113037258583678 229485533872527939451310348548808222760 +101303958796886260834292279866151019800540009767677010337504991892944692825254 0 377340770077239404480490688865139140578 180315115923450904700123316721695225589 +91925311182152028946939545443869971479886807452617039542385108028429077560793 0 101225331091215524224331639781996120647 42731704605444039215989232893236156332 +18943811171533268379045499024248548508389892902824309457007990736616158884520 0 242280948424852596525942793832865938922 226277285172429567847255153266398394457 +55178196116111238630237200094942869266838050572740095198495498594808345525443 0 363489291303669805186073615060486052361 93192668558919463564288080874822620030 +50237499124723031362474304798219495567735510441240632258782740761732058848557 0 362342996548046694871672989702763650550 106891417784922056656175713377569886491 +99397494671491197784810661569750657908848924761527724871300830437497952614984 0 198474248901893667543176891550628412047 66236381645845317989184659482643376295 +72691786566856705892171680324993397517494414172734655964670394431740422232357 0 246301740756889526731883383037933918941 125813603802680153644864691722404928564 +86378076272727588836014001002970290871766286594647378985965065764533670071062 0 365937739812248148536883556388440946370 100223372851123112042623412855533989152 +59728386893814294562210965373310665555061290634021845590678216705226305778232 0 169307513630088670606173987225219053557 116504761778312784178095212098058284502 +9325120897557892202114844264231528322413916576997678857913183862408522250075 0 282089943998229341515870368487332319419 251943640539880048996859085801801098829 +47249104223605214367942618421904154782425193375091701925157173216308684489672 0 129315836039178656492356705848932264652 78497932815695639146584151284843424805 +97108794853350977799623456264139342808167055538825748243341377953580083597049 0 333150787617359717266785333190277356286 110792954702785442909118164019539701831 +44196848447624709884455550565170945784526107780691932277445576710293179397370 0 265468586735833741128063257808142923551 12187210571525825110700130092089938090 +55613875049520117795169013029269928220625934959452153825034379234678672393381 0 449928736368741019094477763393271051663 221461831357293183794304597552124918881 +86283337995711931825280412486268661283095429909230262696190201394596239330421 0 248003976917075220914202962968692564955 25488320581740967697339587943741218135 +78206229071551277989656436006516043758727808973623259310388412170619057999958 0 356761354171995671176421056965368365982 22437153988102747964727476587415865796 +3537534252135498895878659681106539374719330478573289764671206613223143901927 1 338303374454461998860732834670326281345 13127119464645935655934373024150151792 +38594155566061472101611440766611361908089583663045352992496381163410770257322 0 166974231465525019216689647666848265176 222614564178057168999940966689179222073 +13555036123393484750912589388924856161721815349756512548767394415532553732924 0 235368875680428756603496662519485436862 252509703218402651967764646501178215992 +14799914772537120584773130202948749902432607140632260415080806155223089474334 0 167889582051601968412785477800549591034 280257847109832268567178528844527703704 +14009458104930518771645693700324602025728674342840342399445998539398790535689 0 102955643769091953077251206466241634783 73610128182624115435584077103776211692 +48881104197357167625505311491301026815514808423406720694770620803854394988866 0 80880417112116112298446770545201707033 61614430678446780586211875817108539161 +93896626323855541683519550061275472757994874341301799902539430365457366511920 0 84348626531567851462863476211843772702 106518183048480891058160478061151494453 +25008442207128059649033685897582377985987786735992595044703675681441369388795 0 384953149772527368018103355726470953396 160509468033657678825397061944799942052 +4147577668406408103664523230004261974800983399250650719325864856645546982751 0 300477052071190186617946337192346861241 144531158690314730215929351524035431538 +86149689369700488121484094581877256229047142467062227866470139798644728176788 0 476538687921444640611325636796374521674 277478240826400828017643243159666685852 +9517911495207339657723060865229113044563052320883592837746574409123633741220 0 181465702062042860027945132503369633999 25200587029858759061685657948964702791 +39747766644731866787679521372686786927996487787392826880938033511650172198178 0 239645781930494284412145055547285378798 170081568353992213748313060783621827612 +102362842011167433283302118613928647450524177451764718395526847828298109448727 0 109566711224115823608840901607723560052 75597236948626764875042024583618393371 +59500306682240907701437681774982748330305767033736051318196148861216028607296 0 223057623308850897607694537729948232328 150176426874848330365790693907638489298 +100201560157205314025965374556898625046107360501445138700992532223824362602417 0 341265024006865736854330927637475099224 216975673540100455221069431363730436366 +65681774504318094564498618894178951908119458930736000244035621776315567522899 0 264034867392452998852672088695467064590 282180963354092784653758255225591498956 +87309179064271470719481658696257458107737546027786534346287858315068632295596 0 124981066617937058114407597864989798879 54581154051886176277791516615972366682 +1776176530594653553958127019133845098893499561924516369157197219761485537660 0 222658518891480379036125088818554114854 101679553310940070843798260097235187272 +54337798901368702675533089056595311264880959913324600742919065926446000676649 0 437983988910502852823180767874974720693 189144644807700326684069217964613425608 +24424698445192353050453896884231068532038804036261513965899781138111481157479 0 229181298092006890914268287049541979359 54638288020485402270989253469415069875 +114243347268273075089111137800690143413175978587577975372650760470482778409669 0 278110660080029406964667363074242330697 335278116185561974345465602797469703861 +88224738381078036163164096668100908003065812471157287915819867887216256415156 0 225188005202274725303309085389790285872 186487200902004628453625593385852094846 +16817059675278602477179883426533386716301738657186326761231116111494911448012 0 109839833575239125767317699927415498042 191515990761044450344744804925231820300 +71909929297780137116033813992175322012322475236028263704088572426629474751990 0 81480910899150858692217390117877971270 190289157817162653835687928039743343944 +26328193708332695145889944807805565150336387984962399880526719726023256697270 0 134339020431120841043467718725086743333 207133762335250094713450812767300484673 +25408409477591338272814844681257731301730204705112569578131055946784976660113 0 407122038075289451069731001231560106577 79060280821517982645107103173539158963 +55410243529636787176596225731012145135430534562663858754864297210695236448567 0 322123338735550160426757950457948793446 118912082328126001764300013944482331434 +91380965174416027837313181380227598667574357782567786019877653116310687258464 0 168127434350998627092975407829376852386 310932428079562834251233970200649147497 +64166964642273259635080339843015402477608670685604653466583285374808271093494 0 141398202619975485550768547415629649784 97570137941674333737482597698392757547 +76966141101145567072385927755768005845352148690856228722511610510751086576716 0 287309039071847939347226932310853473845 168958298682521800538527799428571169549 +98568265914508174999896304689082280768446068917342819793180464640851297171318 0 232580124543424579314409361039265584199 49722764222740520973071546985504933510 +23502511001983048474552700672136275471146442915161289907100236972502787516562 0 271733257680388500687786776532451074649 94536122366079014798489980963915579484 +111447300979657585620871577629521832701610203701537330818530547614570995260806 0 366017970285388591698324114895186952800 92544681693971767903018090439662941939 +76367464158097450429574283526189920968593363679286267310051712125362302182458 0 183670025726852430524719767578700118249 58430109390448344507882656092560309783 +109886362476478094266373594262038718276650561235618375703229937596791227370184 0 137881605731540703095739213813674863686 128193184291917143803389963463084027312 +85444079062576012123051512421298901146368902476961816269477450430266868662599 0 214401201600361643990936863282361794348 279375456261393668107818697946400492354 +112187036989065392147457888205837777417432009501839290012710327376503032380396 0 150834861757846852759991693385890459793 215613210786935090026606284026882991040 +4297395442790342502480130821306111769674190192384677975087808666751924294160 1 66313158483275745117238639169152209990 2712623184680199258551547258422651922 +50107745145777419460794591038065001148464039071099655473969798681364663610765 0 246156234891746857231394380206614696173 15766308816101177581049279394052665498 +110584438202876442134728408013858883535000313716352291814451550495753728033897 0 194734681853947969663733708893427278200 235914068910493873208405205260751665001 +79774053708652854025708748763907215928421410593396074241536292569545435450476 0 128863821184260900697296774817475863371 115479618589216025725728378454952441742 +23508031315566212518864128403908274634864810799989283661833358600592302375961 1 132325220348619946650986428470260852347 3854417264559997869510694754445974765 +20823971995427935063826552140114232037888091073678105814897114037705119523202 0 127394155376441186311437997988873891922 103046409682402113536889651232093632229 +73182944641903295303155905644995826213734337306847047801733317245610613524715 0 403014356495481840184685422811118356811 224919660630779795191585039327871104926 +72045221287328638352240897253368757919326416648008649020315229561126953606170 0 337899591771009162135679905427628044594 278435260031440196024688908975489761558 +812332291316628048340952340400473022992521483473041394576757608106798598072 1 331531565091496889060523919044259900885 43228695951802270352996130113607463311 +19567730435044825448670751060602160804923896981345115101301856046871268806574 0 141906502330830374861460371585176688845 132923110974258078701264632790431079697 +101080006342886361346737325723055238440572260627196247662507490302189786617131 0 132539407129934581417321025757348032589 216959487401066407800336538835180351901 +24123865599802110467042655409550189618843658248996112001914845202417775524348 0 189865666861129551494374349059249162123 83162131453821237050459323144066115810 +71827941954769159652210308976050841072751631227039910009942637085668370882339 0 220389419717923928057622027181560814070 206832997831245332693187053571705646319 +28770685224694581260963039727849646093886116433664077772150238819344893220211 0 98615983618257427345383377527801635312 116490173749735042464543222059531166925 +30207401842194314203445309211398819694200140497569772910711610649030495534638 0 100251959849458698931939809999801516840 114222438470115288217303911470715595027 +69248191784814809788866677436667944751581739584749237492917154802215662473345 0 169321607360098448410370863697165777028 74381968054122326736171878094268386610 +67325655771150374597516422524527502619270872536959707681109162670085113843114 0 57364970871936065724782719231183041969 123382656362353646559422771427618863995 +49548039690222482473180240474416500958858218904408478288911095290955453483717 0 421812494583855167959598606037923500654 199928128743829636596493164259867392533 +65858783301168160253899007651302867559889949037996529761868327252994962399450 0 101500924298805373613068303284569907607 81418848037533937744608233362630317835 +36013442553122965536070217742698873074324702081057769797160661612950489365299 0 384292932919382244413070846448887114444 244732844194896384338159212135987764332 +21150635357872194831609814645564949375597466787295589928883300915834143724387 0 146778414851604234798487799289601640152 152036572567712401714140406419080532474 +50694726682313018116750449534285218763439495179216367302599589226884040543012 0 103212808463689816999762432919899243490 19349761097269183348932402215443525911 +3885502946740578594146851634928202121691482441068105233880496109282768526724 0 278891364690162055915844752818083343983 69502249506180413098403210068492791652 +66839094055757810884617354634651045613648704755658550945269724750447651717392 0 217197338299891170197719394303939567005 168519445080408348336437930003471726420 +70324618174496674883486544204162121922292782343118780082412865308770395521569 0 392874224827544512634107941106913624159 209291763848185010497843091333736197246 +7682667475215288328135519199861733003023336944236567754210403407260343290448 0 167070412432683110502165231066760969871 290231998536255587679273978731821299397 +59570320365661280552623915315153332726697124036042554399743053319696267720670 0 340326302213525173232353939968296375260 76019911137205949615781499183448485936 +28553968454404261510457401253392467350803319486569476808331501838005386037137 0 252464753317732720443895126836915521838 66370851270462261518441180355656216768 +80849619059445685825156692253322680143369763137123456925694957986577007968135 0 314081460590161579105665992153925663364 80738461991961132803197747402065181790 +90980158894075014113713456366920450776994648894808696574685437143543587137964 0 289118344657137430025336637491307586992 205950018601780352097696871152964674117 +14090391523715668949892048672255815746608656739854809332947242429053269196222 0 155408419193065367540214760802284805509 281708199235176651895437353982366779090 +18310529492412753761493068890642875711390046571029534937312325290576218321776 0 377040710385925897694056777776585125064 211029757801728780943807376298605535931 +94621245962774778216112400569396944255616712906511712028168149228862291454935 0 403775467043132924657769302109994641092 47379298771035040598601847274791343678 +23461157919588375821594566632580563601067398909149047120299230704437410950758 0 104832909359430315038188475177652230467 161186546628694091798450293080404569918 +27639333372781366387449421149642391641843911239185371157945368224339376212394 0 386717594309322920546756136596675346706 209383848702493283829120188508839990304 +88318527389036219560015512236930484119862805721953617977158723060273805520885 0 424721550799428185208235295001567692938 40812002072394156647825080334717686968 +102674520119721936614177238794461376842631939627315701334170541368366569778638 0 469900639208936817647851466746216554210 285056330443631620221595654745660742321 +58250328356461824615410744207340887353249213194441100488664508850778166606026 0 254645317026876918910561943311780969159 42796882837266473430649511398978468895 +86471203138744821122393895485731179098242854384647379292131403460503327248545 0 391127254517068983551496891376033599366 96002290414248464193523405440430066383 +112638179992518841626506191670073459854843307489207890679077229959841902442681 0 96172138002126714484828768554138604461 92812797359549556867153387010126382874 +21939325582071430014625527251713394098345950378060920051914107027662807371717 0 366210047293069963394107128921062547042 205713673187365310452072249759066404120 +74508119341225157200407933179224272321441783660508219067617424275805019707779 0 225114927365015153502587241872907162633 151589293108058335033772682556408697069 +15348310375170218689970383155596583504935808203691298400674305456870261749939 0 174543572810967823929852108668556465651 183563935171926623066651388618495272443 +11279277196179967704824595265123996397248345871677295378998120428344896448617 0 367806030756726113034134931774701191646 152018874866310372562758460823782718519 +16941536347697385954279434224237978905694184976110732138805059136769667134292 0 74263431761143796329446534023379253120 36982977370447855780803989672069227078 +100154593848286086250815406099342773491003942614168461251293789050494026188527 0 97114829259531324402024901200037892111 196159059816485162753853922690537642019 +107477265976015581961602104167687766720321875813032283246213630102111083735279 0 209848022571575679113227047278984230845 73234640703422497548011109234459002614 +12973196860047661071796553904887029960800349265978379823059273671067240718242 0 232641047928970686657398351750080389906 162676545294383019982782446596046995713 +97650312072463682355284221039603200148053254762403262843541152626917973072752 0 429396041144419417005758309839405336183 278712248116847115703401743980893769909 +77711858475028801212999646978547648651721096534666077392856609512752036499706 0 386037315565330426599158286795431640507 240913052819049208111721336639375786307 +53451679758225551206564479385403055490646259444233783559000067077365730759346 0 87970507171364682856394310509890277403 141118870847496248239690585809547715950 +28701400514183884533908442807053270288370329831951364792245399544567551612280 0 338074587408805776702852279218519029890 106426913017728453856103422997877968238 +55319823865976025463155988336541561838224751371757302813494608978298010813029 0 422817719177274664261953540309339750087 212745774052331416640794776788705155066 +73643678869349854879469332195289435944370345519143664200847243277687954808898 0 114392301660398310504820358560630531571 31224568780210599707834629691877662979 +100167644269637618397479251097692111717594412420096304874659765396922133824112 0 385645339468412935612173493466310323948 14160460012640216219442496569391006180 +21974717529865382130440561538673625926884657445805314993011011741592370245140 0 306448263481668048703713743542754836482 159125096870149421323040189461534562828 +7410028173011832003865863544984776897445953187568708583738230634578160696254 0 263845010734416239182377788100569726898 152099643831574833871967540155008605336 +3640274783374585632282756593129957910885579782832994205929593722538690562434 1 350308207530513784258876847090894628792 36797830043518769747890859525201279523 +98631858405027183977032534221315952691924397146404152845426101309992758387245 0 313638743362907184955936907526968650062 282427597529406337143245081284783270648 +51933338488937027580310148576133792193662951275388064159057709815978179354855 0 91185968703014400544186992905908912907 149988151821817171562384776198058983526 +8581832570657185220567156699696042488630947552978981183413532429710567428367 0 84214329170815414745640569172903364628 291089197525675055940546776087203546740 +11424823446268602560146214960926420825682298247903554634974856478595497419687 0 321492236083476939652082904662881887321 109064773252684787947824411590783652261 +99109924018248998416132362194931586306976767551384145955578408535688830443990 0 125172622949407418038833650860790681131 108837116444043262349584408069971645758 +110730767445750159515974727696147210130041629759758173848629088699648911963266 0 331211266562451646803987321909167430353 119554374002282703275428098784580248921 +74916931393802791940145875920520869826147941009600977643574132830746795747455 0 225394560414171963228326868297517711325 48585353911581721903284716816058558705 +12355006545423041620147136104074979782728735051691020222917477202748281361214 0 173267149133011072994865539695998803314 252685887384249589187696545406927106754 +25765138965348049705241388282475967758815664990574485951800838895434940588463 0 133975526067569218892439994225431860158 246861746951492107448306022703033616475 +12051341169856696102846028899433165588932739919311175440041568035646389953821 0 125724950374770419599254114966779165786 69852789110431623082299116619704767193 +101983355393737812749600438366309075831006439063790405434819944174067219127429 0 317184272209169691288003908786459557118 305145352000096632534349758009188869716 +87563203867741542724188001963023387274145142676314615563597610417835100205362 0 303750273412939672418349263114400551488 124104591661188764086021432815671952694 +29617492587427611217912148339766542383664280398379389808902958642701407017915 0 290565056532287106071890679074927915165 228846512174229169734869361402164523948 +37759669087027552453265301300196764771397260752205682788243173111823945873726 0 405199957706485135297606183075778178644 89094478538767697125429585922642047094 +103012564424087750444360849716532844735645411403789984485411207163427479024408 0 269129397613334079515932388723783228269 280051290846259761672659834989175319402 +75729303840399213446566650932875641977173714266512048226624894760677697634559 0 216463109481840006028771171581054511573 163984580682789089213861554067365433930 +25729810370315808584163165956870170061181211477839954187588770219046655371334 0 269276699528853231612170681392402880579 105350051471187917098339624472648028360 +99869723536778280047928394876914306643976363620116612374939510781160458953711 0 245097521678993754629135694536772718492 46199393882119520220689909475120689319 +84999849378403636835375686448281502317226991024052947991096876627515027629174 0 420977442673151095152008676996322188356 44375308823416959584766312649380215006 +88542230579858391200602806920605674067659027744260182089433356399174776842210 0 211285898328948772420404877379590305476 122557482670625008933414917125167410190 +19623641759243111734107831580250608959874238452190557810178371389585878408146 0 376867807414104903590319288002215204568 103088495313807848579981308454610166016 +90246264558603552062452615009299931915869008454340643469546620600793149063998 0 174528479843671289890982020490136787409 258844087118740671100135493373344644495 +34348419612938131099955974927853167375527338608416744110189930666519458569415 0 173965197434060171610333916562950000289 23771263213724920846516829514863928786 +3774193998088866880354620445642700145690911386511174898256690390407372116201 0 227795238194769162411492306624755079899 217154769492734248282589894355098832562 +83982075444891366433148451703690606865902622171147627973205413259248982728466 0 237324726908475737289266383990812625831 120104454591125559048996914949726818492 +66633208600057588235988682012712117420379412847155991080031395530406505349412 0 418616943617570304233495436476273189189 77614298656098609091634881970662819842 +97816835545896964843769127729141113968196779050216256957269301543170305140054 0 220170928629916562425166677115398274379 49423799545919666132137649009868060869 +50324056339579956860064965973472062287140400301458816053231629769408565412587 0 178004029570495904952627700328374277967 163411512573079409675911175826379271302 +114251264981945499314760280635795571848604780226536275794122963186504421931242 0 116847214116641631830318428729309589239 72353229886217356925001752068119260696 +4095922377655047322923663981179139457079537583233254016288439821179851274287 0 380910631455772280928347565620796327736 37948295202464634248706623690912295367 +52913529319658156212455985231686108542374370515800359595001084836696723079452 0 396342796193044356557266347739308131600 188602564617353519168192750300425253484 +17381811203058040619882996938159967940081365598949888475389061188591548137006 0 188932500933712247324503084771799566177 286690364026086261685406687627757563118 +93378024370454409285231146795641299293517725936001171330227313903097647606398 0 299562667832020754314963301664129724920 50408158120505731713597494991330998732 +38007595007682666689525991579815379739539349346364972463036834239255259976533 0 281025844037831930275357092822684257096 198895943655760203211976190648780128872 +5053799255699206297832452485921015363145081341123123403733250901554014400577 0 68421271335227423586897696433564986736 162255062998868383734993095558901452281 +75230689796265460558971523488104678014477622312500973341383177271681595457038 0 231153168679703280750251567464285084582 56322408738148612918529934812074200734 +16815340598448119589339532619214151667763217473384669452134025537791325365259 0 363125862020223974414319780044666396102 13000098040228386042814465585395638489 +102777176970931823204637605367239319925106475814325712039222026980910362071240 0 301412552625562582608651108064284297709 16259837467017794851653040157821965187 +113790292218158940072521078818959505816307163403286210665549496747071266579272 0 483508823459991514129308418789433905214 270850167542260016183306058458723277432 +102136359029077474825332399989421466625797875195442901888337833456385017194002 0 346195789316221694890288744832778330012 116751393338105282955134540338007916312 +67047331509698126590525352680305524003427577493957129352562944971081491842741 0 368105589072862457456365237001052516451 171250574248463614028387615569841490416 +88429015436031619019213009735437497395022675485561547302779650693851291323041 0 309213841236429212680420294120622960520 260048918826113599090387946663594218929 +27668120726240872019461813749957142471290617428493995216848556366637398758794 0 381183880398969056966663160136818933947 88376279795598207069979804812256660797 +4913641083226758890110947362152277823311660317205251684510591481262570952691 0 93522183110886676105561442639169363240 127990896795284814098748862686808901938 +8590853446468246394799643233311083495120708025086620403067513641904753469986 0 248980276743154940943174180960563176633 44080734943633126644864923938110232187 +98339664907965757549966452418160061140835855198201780679140899771850367128442 0 306183943066818652740654771773317152113 245906751015124382190738243360220106103 +33802208049952414598384232466670330503585698463021251135042119861097720022508 1 248307724168330786575526325172380961875 7638294125592454005017054746267126314 +55980323797395458656117971086868466460924340219510613027682873343577752177316 0 400359084552986301362273798493476862739 196530643687699985445700286922814828589 +57584542752766370122664595314996462222116062699417771013751590678583154586904 0 113994672264300891393315969712801547464 285561955949326022084065261319858565243 +114940051637288169180252737538057335339019746827214305685395095946737417231253 0 107305077542510781188751802551428412307 228314796617634959108420131382968261342 +111061301274179009460899852987390138085154032332807248097976874314421872039340 0 278664611092730228928864151408456037462 176343572654805702915459950209922402633 +62006033034297128480598264631423193903994984908308549203430774140663101514016 0 103419658389300283636379767482347468843 45943120464535087985779988172948104859 +11886726869362588406116498294665776430125443004545795604874908974966213175657 0 17788417310393617970565461101673058730 26579389295765997098038219224914752502 +39837909584117468994515082648996640296647698127144907419331582922284576857746 0 139948228632881329197790879083965868589 198962128283993906552682677112419158641 +110406229312233517187558312453560306813003539812252125026529742496668577355427 0 316013578051280022028732268621317113588 184669349506200843340058030671544135216 +92833014802295844690550408463260646371050330408510384694494977281886249323717 0 383443348457631681090609066087458586661 231593935974356515782021543144093196749 +90053170674203264199572594146349966928165257823169947665692707560356155250384 0 319707212644984513470107741136000455858 103144815245296808748457957960340718505 +37544302656645254650508135723079209596088112041484922814444267507096360721013 0 221664348967721174775960077274864234604 164553322674704800124137421169946981356 +40642688734474511208782378308471518520368606251005956884161330242110707170826 0 94082818187993022774058748075208786072 118256748155556232861506157493067214629 +15452994110037531339315787070441234012989023168538166002159233948710666912540 0 82682887634209238749059184281663960689 138446352759502137269675980257708548683 +61856678683918642262025578909738931707831021412334987580784609062080608920581 0 164772436346250304848832239977427438449 242313549875024224762139976621387061061 +17216366248238458454263606986259240890138499781670747664893415843870501957545 0 358361750634471079057784199341728445093 76529154342801107919495961369326405016 +970317079322200308104074699165705762777859179902050358532802551479715798945 1 265913809638255052869781448042759865596 14742383331434773952437548135600351695 +50615648027079633481817887811628975486578364133810763817478265696233331729875 0 239354881642705224265797915556884784540 195522123870549595746606384599767116001 +110696864900836160600533298128663064914650020130146848376102216132963976706794 0 222447030879414082177876650264071077323 190360186601393209517794342171228913918 +22341068297026024733249858687511911788450364332930721487846116185162309671401 0 178106611997434343078059156461514865172 151983502986376854980382950891427927254 +71045901644051380085784705507546409244661099108301208480217835532784768538049 0 410263837956719177357349744303919834852 163247684960647842711104129020296466344 +48683529946796413492959528919396333137505315339197874640269451885809210688556 0 184361604728401684031902959749218267003 304973206003594242459543357158117175210 +80888476553098450830522306745466805565784722357713834303223141197387723579881 0 78239857790838434441155131552686533268 128959132599881975238415151866358260384 +51851379734185017708754726992782810046464532024605454823628730113239772107200 0 76096521444951134494721982873837785911 171597182063103180344505146613904228390 +85681048438320998870625922658543384771606360832350484447431119365383659958383 0 220904828369012993466642414644432897152 19918107937484570282382287984892615142 +104425422497966673919060052484984244517344118900365389243765745283143182376190 0 388624472848898764724196715816565420240 146270020407810333121916409757229888657 +102616663267874457510631597630552755801055557302492990123067061311536466518381 0 243011260509222169934549268637126151043 171255529548195422707562024191945954163 +58570354407008440266823022781483917089010296771524936422348583476682869362393 0 148275060338393325646678855605158726735 279881770779749404488646783495054748522 +110188627086589139227024152804268341017916359760088325873092453486128121889657 0 417213708392565822126297532762876418678 137714842836520489740396208977213698332 +29701466179580565761862787588596357995974028996536663213148023528345816634832 0 369799770229771358858241804911386340037 88377182243265131732503796921604471108 +85829746163406514813602125757983123658749418935998739825889203157855968750286 0 213054444313211401989251561926703035458 136553374597693332037939923500610101245 +19116592590274040629048276053176779333345837105436986782889360144583539944829 0 177939323297073798350771206627022600503 104258270569147093832605971003612831080 +16088592043410392527398596241470511260866513716225160774691697301654582177689 0 62773438551394797186381509244296160302 136242309192578689055653255563127968838 +81087488329367567297149304907061109495632159497324613244315165192312985471300 0 261585593678970802095048069504958206150 18344781372753877599678129983348053125 +44370875350115935921709053008503374975727997269061799563237732569661276189966 0 87696417151694094103647630912019352561 87610379788192352056370559038116729703 +30545518247445277676175409409466459640486529813960467683153183465697451146324 0 136550045878787259175929273761207365300 26489438258397791726350254738149844003 +84692873782749862909843896057551917651791150937670311782287253806695267577867 0 201303658799104594455447348409071538149 114553852795752087477155206580858944562 +78160438773034816104485757517341876464053480423699420551043545158946125647330 0 212762386090142532991222549998244618231 187402156741302036129653199627411614152 +92237013736085486534630388279779220448414268075851652655646601178457372048645 0 138761954534192288130959026880638025383 171696113422438085980887336412414789822 +32543567835183441375187578988155628114602050011293910320195077358776847127267 0 431451549531244926796923188849532859327 255368553379199224326871679382941377135 +7870110480014336914827330123310162007173412779618252060238824664020396808395 0 291103478523168637636340156857349121623 211496450359683802865504135751410545567 +19879146747956185044853462900505427565887698594524838280949876276345237062325 1 212423488542144528396280658312022064999 1913517102279168215888102254788136153 +35072791174821106010960597663258919310680424763514823423893332156095092153430 0 140733176965746301645316634783103035048 276435374692207652816789316657969201900 +3636799662228051987357709819964885719876577205569415424767607443658441937520 0 45372634913228575794149989981778785304 21349480024399857003194671048173675127 +83617231155762008101002683358750292586392071144691787225901404111162623834235 0 170605285239278013377291962482936592342 51989206135489279821622090854218266222 +51352634709079615615519041938315860124487239443978690429072499647367819662378 0 281177528222539675176479822700137977301 178482411775103591423708940682757919147 +27188155743178101341475108977885509460827178452176989536393808829064720341786 1 377239208772149471013532978594169891107 22245027447522982665916094325882727434 +67374275679475821161224159185882922754218647224422117292048603699639279639881 0 293340639144579123838308385781473375013 138953890250240415163113679675065453886 +68610398976535205506660486339389582954363887044949067061741147260059983082774 0 412590969977799154655769019135015900396 225486476084191112002772813301303329344 +112102899606794978086928911534171013658628475474517685480241595632177744452281 0 251516712374755576836748153513995484312 205185246530018618506317286572432875796 +32343399207302182072247801289507640581166381323478563301133722745447271498091 0 168934919088611730410053958049624850408 189803626426678839401715906303317380612 +19619107322433222037003861376151409274788861223610661572418013570637755161464 0 363856613654290124422436537684887928591 78371701067939654748498346270974058971 +86210053712008681551840771974850181959283054749982916851811999731401463945066 0 287916280158321046501430427643403221365 126103119625227149700674121467174269933 +46082272645640685591794229729747308008510051678100121060098144805546977816195 0 326153801202885954978173961040935414527 195472358632339191553744511472278095781 +81628534913168814015222166933710740305886103857541053559673494140318102103128 0 376191036117642974761668478388969093170 119536095802056888340390954484525407565 +27648369390782481158660209612073732361572108340499175703283702250558399529449 0 293268741703162359828863701617715216830 50931819782712106764488245401373041981 +93657477748199222906213478703691728882893558859067753481897128184938925624844 0 402705512633992001720041320634727245691 165956193131012618838026778562539429279 +6525929643034117291034989337319482155151543350596334371075318091431458859362 0 277883373797686485338383048422818897708 126926459121607879599359383315954144833 +49990272916766332351621491194787485284891401467190167288081471929308847831174 0 374946734269150587480584221130149924936 165903213025793986170041929377420761698 +42159303116311726795345082901677733191678536062270140159445797701057274789282 0 428855176222482198384658915061039996179 170891238149990582885875883264324508421 +65618552636831661785872516242835617184370774177084104095809031062249527355675 0 171277069033299808037840321887702728005 280907617202719344632982502271229026549 +65291543430153768133864589916740008581237512001522762705135573907675918499617 0 356990419053478704414167599946630127491 237344686643890724212453374779826380675 +10010112367304986085473813054361650414090959558171834464030003164002515562104 0 151016554771586988481156132370910101543 256434714753692528097590844241598835520 +87402447737038005617716073919006505225367860323340151901337980272527485338657 0 374388464457083639345511210447081202268 37163617808277663784429364509541037462 +61254818088142240050558028088177611620669505214424764791924749741408787493841 0 245267320057483780180585351468167271324 215946789431143233312534243420847102176 +46608346812889398678537840647776506147949577420901277002253883589153480092386 0 409391177257865745337129140139395808842 129978308137680797552297552236963233150 +40529947393410893117225192819147583904449369477975450328632789009267563629341 0 143446476255270625569913647655315049047 211224915135649297760941317919552216723 +65577693561217952404953086161646651928460203697645556541840666215133855382198 0 293935903776005798883892679042272269834 203194341818629539980973091822224544007 +39816499629620479981308743349435818196736057294584616727492790110768973675752 0 352868208599887261855071677770806759176 88007412258199062242068727712841756279 +52497759989726918235828589914315545739381017440070757523339519532490775026470 0 120340354670950578273928442872213264958 42912402037538348895541031135951825942 +80160415097579566941346293825004970498416004153106840457711030545400727284035 0 232897147197410743530259491499312007548 159258091495630515806492505610705287896 +74591724894183426040713911199302011081088242858299464795687402403418812985013 0 187607606238303807859391651886224536820 22759524811240025832210647274343167783 +60634046001346032486285104458410155199148893551027455047155385995751198188833 0 346634028981719696464471762055213638178 84688380410581614070547910835194261736 +64416141553608911215182367027237180989871307823616842722183731497583921669386 0 128770923846100167923750095099503751179 258096627147624253600078140951681137203 +53701408549982703334348453822524579497586282870022009155882208170354518051429 0 94026096934499371479624973831012281239 318972871837885246242771493857138350779 +62266760348614871928053785552665560978798630189695377842457806462568904579388 0 108171909667698530756495447109516442055 70068600468697070234238502625161461127 +75570489346674803744824490198814758730155214257328179272174544411718074754486 0 194125421664951853292706760398041852070 160531198899739490332457604714501392277 +73539256582497473279864919859300082086959455173903079688797062452710488701045 0 197619681309554385960898777091350944826 113437966557044699849711500573507990584 +54835617485483570156824425407357656987319850397750755313930841650535183423376 0 193411487353150122851222366750490050473 146288370511850951237586856461292376486 +50316396146902912806455391608804944829356468492162643499444559825450469703422 0 441705537532349615470412593889353986498 201668713479140546876571608393552731667 +32508867278808409984319331457578725109182605765119274840200687284774578177381 0 215518228331853763115869048072899342362 239188113165339563950926165346561213294 +43816750570703907399988912062428196845932304412145214822714547618359529288411 0 442695212073531973821612497619395677933 209313606208772748141158749901549148121 +16107041463552930527083848118957090323931663149850881963849525010097745183091 0 104509116825839514008676761728554226998 37881262892700920059620528162639922941 +99883899501559850098685012455455033473213413114892111555219378485842071963767 0 420097551060185795912774052184514796024 248073526751475526887029386817118370050 +47866013519694521824950737982738200827332907343329386708153744895621299699112 0 91562682939504840438832432994816163064 198308108586594292769146484494509799697 +67252020601087137502700374223562250533943411727966346615591114686471270864872 0 345812246595120190169267251526649345235 19874870098287992771281523486071036762 +106102439876993475446033113865665086757661326454375115939052798009109259239705 0 302647554152950449637036505998389166349 146937675842288100045473672769479987523 +82604900490103051825176809378288626785350209370261084519886656021651310139567 0 119538227321891366386640139183339130613 161164175747157404069223094127035591066 +35059513782705869436487299084142452308098577670317862281528109843125588272697 0 344789700412841914224609735076293612297 34039391971731815963589939221884923297 +1532162964761207298493232097019726688300545292157004952147128643075092344277 0 102403204329319711965570717991225841274 115449871703149483724995841405367720520 +112901583233770290354935763246364939238659434357381238688197967722299425687599 0 292600433889017057931577545225260446897 200117943154986541640536981980394286431 +60074307812406739264973895109610475833338858366081189846659987007850489828141 0 104131523166357428660562848765671859976 43540308453253937000928265743732281912 +89051900200463115261567779245944070691196061184705599468214701432813016409539 0 417227661650759091450248142203777430708 108082704480082673342232333418809853380 +20547109510500121319098763136248883092267057909889855091215904233971812996603 0 232267672946499079279188377447371807311 23765277551502455299324384936539514213 +14594440503033367518707342646662924990035372611611749321026468188310998585547 0 249804227655770791931708451399696795761 184196661892054029987566184282322338833 +52466982668641938958207299098652403490029429633110026639024675295772445398002 0 407253389764968377574121281617540477125 250315620450113045056813156730841669940 +41668109937418530633760439529536653031965686412374614718908102909468490652050 0 176994268352802357655180912771844200834 234717576122359361449081941634127371943 +97948290061134181054960977828286444308120149591678834601293270046911898368065 0 194572859056595966820856104541103236839 98884203102255456244012958048416472896 +988686837846819224261620281339359468606887235561022312693486658190920245999 0 315743806425751456324439118453490560151 141259661648775445596937935769240490626 +12441503558170542276688535999723120435885658333397361267253942073706630839113 1 382336387513322030095707529611028589580 12281898100654059670465284537305963860 +94478470948344197365436080629460578316589964553070240180227735296233895011595 0 391130729792048802827870618736666617935 212840505023369592315299456515647710515 +49905912202605306998104647335914997900695713471578912544599327035652113342110 0 359972162351284088317931313665030720151 87965839670828797614380295841849108268 +61115267680117314297621770979624980775693264599292240148745734786798164280734 0 60030230495184011496205680007539223538 110960206930007615014187826950666426042 +79689488211389205551110887886314868356698614515118487664845716160111266114674 0 64721976899157021039149110158707008964 49643426831553217548556228059344586342 +110061893058307727424811964244705915331673130626496360738537133083759191718776 0 447593647063360822921670549433347172362 194989323677707644412491767398447636989 +109105273288760976286017284896529742376192825069882027251163541609878365140550 0 237077488457283916401115390882926322749 49437212660409697625787570690762317829 +106555527050667451037014509760960379272674969770752257057730810189248263887241 0 176625882997152335561882563727741143095 272187955283769467159655739569397073090 +29777473512160770468439590378242408545763159980426600785800541320032876564475 0 129662269173894839471373906593073825155 170710890659541699558198376172411671282 +7765376197227882802520905391694187862814094611394079210209589423192575311987 1 177402154934067546431620736692584833663 20185375803432370647665594714952789745 +44773069266242470981015692241236094202947894822527569449772604329682689336376 0 85391370035719927758312958769343750533 75673248097541893709593318875502349885 +72581543171469187650461984698033121499244572145578332080300231705957160849225 0 246179923045001011906566975901586246435 96626268377826505356999747031272398966 +94243076669252379163237103476094437627782009326877734019682139654128196327634 0 253401522599758410559467593334355409083 127299597946823896676028364491932503648 +16668294379240429959032294798977200647806249846074219403846034693805295714154 0 62456958940095654626581212328245602472 28596899590868678321091612471432264970 +103126752106242195199020000559165856424561733672057204865923127962069007486172 0 260981515574682867643391505969164763635 243006967824179347252632830780138869679 +106490124936750463164819685139671838440325560344030953131039263857021488217782 0 283761788871889434155286186983579003876 190698160486897034616728600049426595772 +10498135352132582786611801883273998067865274112115023368818813091832019986270 0 348524947962863364981370024319447642945 73288730709116263788571369485283332707 +39099319033777932088076131662757777746017133938635905245824520712637043616476 0 374593955892191706158881479505168106011 178582083282329638071070459583590605247 +80681029975796264065049743467903933633596328136790459796921220008680265949239 0 187486862440379639562901190065244217726 193327244359023990154006514788761269078 +114853851157818242093405428079587790951982761259070221300707133110628609162213 0 277457561861173715876968136051567352896 73104208098886130002508136533591177927 +11822184929442915408866255675998102986785069041487181471013501454863660776156 0 355049673587080330272116392915164008273 134377115638177903502066592541008882109 +39350140911073416337161583799045280357220426933288949579462122345991953456564 0 67100400439514393651792348888210926398 225931692712456111409360088888791890418 +94909673324756914361122416786697317158104553465119957913988115314160855671104 0 175458549739071978836498768372483234124 219900386149227454356047629792132150126 +81971061996093304865842378893625235212733678672851012650304665548469653355548 0 137081861416212358196953335985664199640 86263670574762664194122088966404909490 +3126572534395041627723576569825324273045794491923896695001497824936230437081 1 353910429294342922323394791393916044058 51454963170751671132097515840610630711 +72081207952783977077221023602806918406040186481121226596239547904710113416599 0 378903725944370134270550881374919286778 262445336640169671183919534551913154785 +93440292291178608179493262251559787571748719669179293506316391511109757736021 0 313561311967250896411354135457116551983 215033438903580834325163312749551876279 +111318362681674673153593481653134769531394082726090136355455525289146362552527 0 239698118127048476630297078125304117146 290075225998841813123425883210204158049 +16101888204943696638584672263586138834668198024549028230186841297885510985266 0 300668748362363178322606756706072791290 222892946606737902794349829725297423025 +80832596609752631521065593139374817100068380247408429404638791708528074700000 0 133506040778754135664736148383531994386 138242257205167372709323875041821213854 +98679094494153869811309794868401782182972931624747393101416830377625811507845 0 289088060552003132310563343220419883135 133012234916457582305340261211007513675 +50169262301390073953478011712142996128144453611896800768965242858432265625574 0 312371011149408777010231948994387542305 189162361493724151470119304062233849265 +94514454519325969469564705441293574760697113883216131791847352094020209640390 0 100519364146184107351779567909955124446 66357615459118287576006691091004172330 +67531951008340822553702463552424012381837846693916870606799078562697707788342 0 188572998261223564794949073375980422548 191121784770908979674537277774936620626 +13575902607946535970544257563204027075197215458782827754451934106044151580429 0 161457055015425775790307722099862778898 173981097243323772801076881865920205962 +29876094332527750519765177741580421602246098955819943600143019311581728880558 0 279589863013479225481481082016037256641 84905300259414745399852597324901796692 +84277802069989432692030613003515748983914073244645030694906803918065792777667 0 109180722392579432341766236761439575925 40366363780847943086742894191531238948 +39866361440412998359187589600229227640136087692519447439859013360860914964540 0 131919559772432446693586894345995207238 277829303199534515573150722173492823859 +64292620359806772458115923014780068902630750027207137974266666023757008050729 0 349496868167737461342961655656965407480 202859024444354712610923970561240646902 +30187838780028003157091695656870021824722380734740512761671923888483131473920 0 207301943557104800130695406699357800561 99136993766428598251252972280263030227 +2070595462734792723160604318127633817383993793929011395313372672504638879844 0 356596299588891828893554517103238438200 19265730252754533054786922587025143045 +49407227241188195171302179156824725361365001129610779099593108768995260333396 0 449709501359412277075892548259802066402 220655333901309183113715412512615639388 +11166525943377157506679219795292333162626473965133941849553231030023266786671 0 82103486178236231870502803952854557361 106016334807599699379085588312463128805 +85298305074545468727241233978926932453255528071274145193023442649433627675692 0 170251098930900220364950139061595431321 263039774453348105560647259251009340359 +75901225957608420489523679498906123416939874941722927932584140148568754853359 0 367775846664407067708409350808304983963 228250975488890622436769909679966897148 +19701411891869799528408919978261798635440380425317134317618775431253599221526 1 312606752680597586275388611948674601182 39034286976800766761195482309044592589 +54375510044233786964970524494381276583023144915774702848872137160199977916188 0 127193521201046990742860224113611374899 18306377472368142874631660065400841101 +58000612156893808155655089975929426146946496418925077926195850497504311723155 0 314825157710548743020563704844014702569 55300762332076609571236856030428700240 +19999324404036989468923312117690860525922773709242396535894614417483125743932 1 238080734463373089553738240239740555412 28461305981634003461640932991669784831 +51516159853838479277067167427588382532186087722784325313078513146797570167553 0 314801344839641919185533592980199020890 212256328608465067850361423357984506102 +57183546068135758522012103251726895336173043505336182078335754000574575467231 0 162338299609484508696078948647900992181 206425845635638305154233093391010893385 +91443461352497755829798526852930736786191070176623519271687784916250805908159 0 230687591401916901223664551300264299180 60701841790460552668876096333436364428 +98120812083107266861581780940611277011249508069711243562061659660398530190903 0 449591422077285883171898483927613790767 248822671127277985581056053156997756793 +46012966042078752026391723620185637105419040632809142369847686600235823397654 0 99096720962092064622788412010547144382 240696141361157163312134006658045356916 +4222562747910054060891600910701700304169888983205830397053718026879320387640 0 144205960184849623630726732768689376787 26377786274362308865113169266944794765 +52720494189195359225604397967278093197221165273317911810037857788532899999459 0 210291573378819564039776501532254596118 174702705543935354635553864804652879984 +24021465484619219469678455112207606958109855212752630071888061124387477204515 0 98460158805609870122050694819667353934 13574921525761739694695810678378447418 +34307676923414811002200539438128678525379935384479512610245626584334679463975 1 185308023325696852730728554153031309694 1097056076947150902881711890635323475 +21175871374237291017842274799621644603797203428126330560083532496389533173941 0 207286853357863593014167266982684077047 40813869432361353376869785000884491702 +13459999585086340722657553918862545350225088269519454710585749849446548112889 0 149481442423534024789167589213312537108 66681978331625266648147229429592498750 +98006113938452729535359575092554869922787397602358529268929184262546183624873 0 428728444736865355769913943318622712748 148869492186806888273857387326783739433 +47528856072620219702069253811123800357801560632194545884263935004681251059631 0 119059597212521599625270418332028284211 163490004413422121013315496207426856856 +108714832896683290701609486530127858117520560276032414412282906129601499260332 0 451202222790195604680462837648570550516 270019053322804025439233973189684469973 +79462372809786893134273677916525688295792480453177997771808619854363171627253 0 192825310727752590823678760216586954563 63350538063051437603473680107335954453 +28192999054305076467060702457324030093010792036314592040866192932190459196216 0 174228590609283584775657670521400143261 138000768655897741221773630208065568151 +91148189861059806132514650560332522791401895244020435536928627840903656937255 0 155752212091079617148563724251384126852 85348766128541144920846420369007111139 +12952106583287842368088550034659761329359593779840943855816132539265305886623 0 52536536308798930313091704379814117051 121598312728571729946784972555390974669 +99599125650825923596058736747218920211058505183409773137233044023071897859270 0 190313580021192799260391189679424971907 137034301677381599341398154549693060119 +54213229483704198151221891885659693044685337658120053270444531237650234430432 0 191318554188893564781121079315763145114 181165081772371883135514505208599382288 +85675065415901899768267092934296204355876954120595491766981138243471370348810 0 196781905567813754770820417205785797897 187098099964525599902673675272821246683 +76935086666426761610199020369573278156412200077183669197796529926717549110209 0 174246838661143436064831585954260685255 216025882377919386571502567055820910298 +79531192454898790432991137468573372293099138037367951337084588400061869105492 0 188782984432849478324835924003638873162 78588919249834990924984026356461909286 +50165926718913292752528289062030273738603589968406797862496165868833566166557 0 352276556899844231488934431422026112096 44838358788766100404412448305910336569 +44882526218729411513131816976423490169506273360215353662623940745174242840594 0 231842397262664816309278502166248904948 270523336660463114343833348639404077850 +100035679597028236835707127478011314041864663837121686780753683420775109272388 0 393143455892708549401065206656491483858 276696655690566981551543738710185621777 +73959966612699029235394391078193100198062525620332109277514833768045045403486 0 273803617198046646958274699952404518165 210795411962966529556902221887629735639 +5238422081436175454438877826765359851430630374181928241682138451417494184598 0 122007510245196893390537159914315585666 22169762500703093314030692642228642637 +56530256724553507843215897797247227752655268500451299030028972298199931818956 0 185623717303514651391293258893005592732 169095748555346172245088611687498952223 +71670697989874334693670094360099860029936995621036928038251317307253173319739 0 104342388520531920277874300055004145973 152360404138564318491549215758686490726 +111155921163392480431197276120439379314527815471416783992551184578062017104258 0 314503124305375588296930165064199968761 179387056406010468451139212587391650869 +20941777879531103392060212235088460061125893789140820773135800999238499575283 0 144716578139176287037699304717876197004 210637621097578522478180062399196354135 +113163045195117652826752847094645502621628399310186372304803466230439623583587 0 159017347359273297768280048280284134115 228826588028074272686972481059266022438 +27073207894178456242023452703942997494047716271596560348839169008235460338032 0 52211736874969123235775604143752822061 146421510972146283555572366217063160662 +20628184188586586924974657831112379702692250475394562228318756141314108736594 0 398342927139347972519056339728301869134 45024864261689159362396025373412172277 +4233850411253284637340378062795718277073970922159339283261453619912304012224 0 68487243553654198785781765602885102605 97901104338378330807848145700156942107 +54579321478108303102239379314743438295118356646494580865151945278726682736466 0 101359437549318377077251417908574134525 269041120051888261835920401402570213671 +42760672319971083360312308921249219099760780525278312880150241715918255569583 0 260131687385158692510775732612549929782 132280593386475439203869116786625944271 +30666221860142161716919402524724047716102151127690742502559963778309431674587 0 101739542058854388973465286505617531911 306446411723193461369695946212657335804 +2306250407875774185496590601413689851973021057384672688225239929829946030139 0 225598275572220839394555901254149777482 109088788933544115160054694054838214497 +67959249690605520921762484362717153209628568738287602088050070912944188281262 0 268376933758506314673037169295618724006 253681280627923142613472739033629427231 +50685433406780069268509238523925898092578703377310130493665095445219214935331 0 412425996186734700536065050137165278795 259909153275143442213010998482606665916 +57628064551068461196126043803225931714167369589748561338468425789484678170169 0 237403719360418672241294965986528146811 189451304114328954021292681437217612278 +97628785638714191736841792687673441096436035905124032228399202245170925583531 0 123244278848877628321313568863032993880 169315482298053447347078754904929763561 +108739969970833462514225898908657858031190116347433816706073475570201270198749 0 313962871644069216794780145794689373932 111541183731885563273695772009039161570 +90124302614594258183904851529059472999720774189085430509426488764372063937433 0 345532195400054055848060639544900607923 174348624289410666060615618816125997485 +62548566734740955657426827017094425891918419763478857243303770743060017976872 0 51531779281034126650468629897880780259 46520395855116137868110371618743279103 +56804102339298976947225605198162021041914664320931477721735791062926383819727 0 53769135077508761110025027722988928768 114899103846736908085945156872710400047 +36344677466237804180332731813427081591548461874377325715629636029542643635600 0 78675129315223385559982447423586728659 149996002188809691333900403511611808669 +74192427450011839922065136869810514995455297294473009922802398357772484357116 0 231075558226846948790838356728789803218 247788366733093007191633218920156128676 +10814607672194567708476955895911343647106655511963324128736517074523432296839 0 339804128683543310990293936155072305939 84560082006366279303397770296163374502 +14670247243417959748430522331710394486060945019663229292652381944686873932949 0 216816132467768706293094573634318230766 104398878419944410625233659620082612605 +56107121155374357535945152164294125642042757527061510117228535407976438121497 0 386892987353912128991329976336503087663 177056120338547206064090741914307427623 +77188073545141919153253032079852740640070319670556722220393219363109374723169 0 160023758393181220743981990294458592550 176101366278784987644445468565960054734 +80438386114623064927081270874114948353006511217400804502969162121456798770972 0 237644167675163130732252669886630134053 165845044977546381122944494488511437219 +21750645098900908912678883667612133796043775549396301332468588133882973948423 1 352818499509769438196965569266618433477 46097658249573195873115401561342364971 +26862423298115945424714035040705559189101666757714539997310305287247826384072 0 399410086271561552098443586429538266727 133833127179670490138292782897063540502 +84080526562328653229664227914484504486674483582449941747220480411763289195625 0 401664657227536740437249455683812959613 100032113100646377756626749845861775733 +62111157053950200884658018451481945090142434132677284556025153670367313526083 0 370411415301340194750740515404640345867 228428336354806362252125128311087217418 +20424636474284922549582098406694102113352361920085060259599092888980976860405 0 59901448108733896277327576901204637639 168974316108173167722431119890394086304 +4160741530737146563091753966954883675428769918419651542270453218465396411039 0 269768321090245135860744703491135204176 161918775753946834640965015495806500243 +104425436525312353906936282248490165432844814232751969526593455875068190422401 0 116842637655429969687192063405922383016 282963147268125192126386127265341408880 +45930251344865671062516217930438777182729125375578126035939365948425751025341 0 107758920282811432284432421635820750119 123967199290914156627921605572173478047 +72166184723593643001804792566029224788650578804787833776316904348974427149973 0 440446365303761438160849560777344991059 183872861468422199367074858983042235115 +2135453739441151053057995441603527253842483912513195498931921015333762698074 1 192707615268490441859403928586904730230 27443246753789444977871683730713716939 +104721606071924032616289509731059945977525632443948667457224299259862780652331 0 99444080945278110891417445470834440351 234394336920344357446088626074774513777 +64649953962548670309290426361806178400660868603739559301989610775384906017495 0 119437588015372638358653065955929696067 207301307614384551817741015416335873135 +60417201892110932276808545275887130933760205660759426438971415293151990784104 0 357563908700485063630659780719065773984 87137760942547591151360966114147098276 +1664536496415348284764196001551027954475847974659102907819354668847923381573 0 182045570726329972739526684296461028116 241640667881073598698849741884010686015 +50334896246466881619652816643741010809861401542950841215066206542516528102462 0 76982247423725866446244451658781690462 256225685541611358347372834773391879412 +111671568565463631161878244794179262953758976156489233845234863174777065225302 0 209674305635206206295267498468839168211 334836052697409696720943411830443927749 +84068227844428618510752367273992331772053774742356243151842411999371420637390 0 213856510655212747028968004757416813425 222215284506449987049877285938495622312 +110461813543747915084659553707740550284295066838275703224501816139984718372811 0 182635661414763611436852512202959298454 246374114488987432989195223519784003714 +29122247838865942836137321790378969069427684636926264527790956825568474242589 1 299062050762546799893302911446117025242 21385481687783856753974327664184076975 +16139263766274456177120355717390955306945847926594640452192538079975737408402 0 71648532241236883109215415334010002606 297979341628169690399484262103437310377 +20874239397852157795669141416178151087606380113974852609761536881486493307392 0 150553430298391297479361605513233569661 192471917098583014371143253644106423847 +104756145063898825208314983368638941374063531139745857591331545086102872055886 0 285030268787929265222013005900329452927 36246807619398950222393088279865542261 +16755210969954310765772278703348360905394375120217980143154390499093667986706 0 182375653669222507590299539329223664205 212429785785345358252219258317618075269 +70783109175121756862845343456351761250951390552005811879507805412945108769003 0 173364062218023105248771803378923309564 254499306310855828324275461595044322206 +58222758908908801229188263699948193412155251602750877235727824399408958823180 1 260364144991476085540847676394715163006 6216379023146083833797664783851119428 +64353453107305707254506444564522097019246741144999070996419662360688616425200 0 149196767399047737387187523417183578929 218984233813941470956365754335022558948 +23354141892373264776056653866323711150159533531152826524271373486896062079789 0 105379255404110152858013130094867167983 95808563785963366788123337399978175961 +57611942899484060300220171354902447078566605980011822292979366602702697093733 0 262371395567136959236798414413756731062 79454808214675985290091166154330561735 +91808587882179410241698697432443233124181819236062853091400802770675587123453 0 95262282643029874522451691973020293809 73639088399079781493053381290465217598 +60407678448328100376700120908974227594315698177575142685012440141825902479686 0 86350058477511789825269604245611020595 201453575936887761731596689172812610649 +55607964307589679565627221312050555924965301035201614143863626108426976586943 0 149806402551016139381632929153498089910 322190303137941309147101690410399742149 +15413513245722317145573937263298588315338520550444589637013196058603861533929 0 311413133960420882134668940412547732657 41752856447193350215821364955138846387 +45288307403562577908073300826138633502447496019441638492651413492215307502001 0 334612629039861254519449932805080904299 249134695833083167552432498040264330773 +10114287750251240077003521495780052652710906794931970767516471307021478042372 0 171855821176733600196644704610006371031 224264889055473063135963301318521913722 +75265869302360323966037358405959354112912133407276386969305344351127211985499 0 187629032489822873024840163305080532404 67946726721917054161674881154812311851 +82152140935567462125942530669394987761484709680471652422176220016190772323683 0 341711612671273408796680780277092788852 28162315999875702735428022505126898785 +115493258573648939360332440464654352426418451414031017462467410557521316482296 0 473368058800169480420489020974833089245 203037730079224839345182038021209908632 +76479466949996444789078719436743917952923292996685467124258003672588325212268 0 444905791058695952979320814797488933794 273796041384197302803781177529748091699 +25125996619364855763011720349183645509988835722218814152536734845877645629459 0 278343053289420744465930265972001641765 98058311008454617563825515575074589524 +1248078784790969430960737943140846067104070478921585937660777704984684765315 0 38416534064979764071134997784606951428 127546077762334049779419306099136370631 +11686198105044859792342682897153468115390574663358027760130867916223539595820 0 287146318209289667583902269984784231016 10765504804724987353392419097343402007 +141137463265921021412870884736445100035811670446600694335396867672508171650 0 274538521361560663499576744085724627784 43759352819540090137575600740942368090 +144477822419594343023623868012350352326412608245328586229841950645922033380 0 385071211877541479197628698548269248570 216530764296105542633490555168418706765 +40992570010755300113787619080842635223227219430122838726605437431997137071983 0 381986273085074957208534020565292972885 97249896313959696898263511907805366012 +12371219560722909729801092779566636720666568841000197545482418738774620333404 0 49031433498638207394111313092777421885 98213948980661473321966633660879562330 +35364145411313833723155005654157370127366685927320245081060340566566358872091 0 48244574149369049428136591890000866807 130929816671018105885243625154836387471 +66526060365153106090097562728372879608145845946552240188072421392613749055871 0 408064451481820347594923372630675737973 158337362317029691406884575296530908875 +91593038880149586289478060392810945286291314167005798449092379142091243240221 0 107426298672020696194031065661831880111 98102523989228437563023482987037203113 +36953158611679062860779341792683933272314093244384643873893957899852659939766 0 268220495268502573153302303502516989447 36706988253247939955429300467301260877 +102806076389248676169723386456845517019583891903155373282638030766050822573442 0 369357301144600826179236296942881198398 140238574182124346900541398498633260517 +46044626959422514914355589659345347006403766372290743284392160192247861050437 0 276798709786093669651474831434363707422 176469346034907716557981322668565373378 +6643241370247813390213189856923622457243138508554526415012501652095862948578 0 362809350268703833655982210520577611333 248234889002626536539296445178050883467 +3003611162645085184724773306628921343238426566743403083806568135912317221711 0 49430335564908082919354003134664006408 54277786036874037199333792285725984168 +96876230011928012643319349144809249486236091044415402214656652123498404816465 0 371865972612933313125490018137335013361 181035470744186787316188165294300429057 +70539399787146524119537253782689181217782531842640890859557885068192318808675 0 219895234157785192115371062823093258139 40452077911054828451716952279309183079 +12792182983527004520975218513340517459942353335063150574519623735081074174423 0 63571412164010818098072521617700329316 85329396661156419390362628137874964107 +110152695794993170753923644931120234258349968997078249043716663735048422814374 0 248188446182126041552204184851733813445 93139156084824522664353241489935855613 +65840731854429843283458734433514477644484906558272863261249182288564870527907 0 99315315012308028197996798680515898864 302468868741384936781149965403691875226 +52853388003490222462322884440094929618336542635888093820348783014557592715136 0 111109581968930903523994230157574979504 250247047983837250463484617920066150742 +50734701374467073106172641985592145155755728377753483865748069366241851996301 0 113285192555933656337001594394721208316 114188194674249494201342828060443412702 +75220177207339571486214284671208360514752674018651974660672077642757335257964 0 208753672347302431403085463318802646825 38079291622561352853876760845752233823 +70850502259261963281077733183853116012527658146003489440651781484509736090747 0 383849891090772776349800233543906736636 115116677521309445920019447198820467607 +108384758494731974499059475488930734994270173848227614231105472374331385075039 0 305813145983335213224833465217614586002 304027241590675546375125414312879526460 +94373599114502355455545199133910091218317366506629156537406087577194291244011 0 351136016950756632286804075936343958770 95067682142608432546549827729743476368 +7521550967913586903228360363216764835777144780933977995170441904489123087211 0 253982028825150512281146329690969617083 252559256526854359480025933012230928390 +61666829540629680448849586522092115591540432827016616588771017710458696954399 0 425770473896701467405333075315188289751 154626125321862001610086983916421881406 +83264226567052362967889718567642041892799793060194341234382713980215885515458 0 426148169185249288043610821740620753492 279418344796949182873075834028478072383 +19019347415732266071991527865888663738788152541408584446039325290901778680968 0 297037177318530079737599553142051249429 258324427867533886709683197241010959165 +66805591623561814519371162167023575943173675845202764176053458626426415285279 0 431874248864119623385007564239662402383 113747308401473376404062565459480246442 +41097576912135798324449247483462337595237158960632722269680734379550641282360 0 310239710353665125964247442453361783538 36211255729138935810809388737560179792 +106531828752929097973099333323873511155015138577164953772095982974264754025720 0 160144335573662551680491632669654960400 286533908321602540916663884444348469192 +112562653182041598411223262346013009051779519559885256720772447121240284928278 0 347700247713861601575782430734816386205 300058978671715632622934387591547039372 +52427044257732773839332556348154585646167248203478162452755267634742677706970 0 390966194973676476419828646774635297917 216136611829696600693343021137419448733 +15212092589841742999753871635822286815018075280258225985733671459221655374161 0 327646356774615541816703175226381732647 218725297244237214830407422552722548155 +97553216855710973986861809091613125625844553296640212060217960231100388695310 0 313769086164482510335746925200056718599 221534517434445658237164082100581035083 +83355570070624458994065855852548410753932476200906527167345973187957887904211 0 363444147578926634179702666939073411987 145198050680939081864852219892822209712 +41343444822532763737703969955283041579029742717270388437745094693668395478522 0 159544845391552409583305047359448088977 306329369601186353748116846691267622034 +69418381162326895416670147347702351280013772780284338417099683798886749335000 0 325042069151948582473511009040309533189 156532359584716063601302777577287443307 +86364868946101942551622516498127240262704182804837681238926408244440326124422 0 336974675322679468203539269571439868209 56664323533183020834598474906965745140 +99641177107819237055940644169179810357708227101112995191006535542547881424189 0 176948700035946127572922837533619941208 116086607304093585021818521383072452187 +57786446020611608012812377796223714878123336362492829538615405329313910757275 0 384800491534406380059844565570981772222 52266986398002287433031888862791991912 +3997314366767485810536545892291469074475439063264066213159323908499425387885 0 163725879096128676036352682652791025333 226192662661660937487283376658070867993 +67615980243338399738933073436777111073055676323119013192447912286335308137561 1 374683121854576184123882418807855041884 16460998928311049120615301499425467046 +95056188134586701650731142730682390276180906522232386805776875227233931192257 0 182406152787653624920535597648223675130 321349256573358626023271328220600865885 +29303255512942236056559307424251563011898396583247137160713909543128925925427 0 394355598494679314906318546129282608566 21331184493823760431200200045312958317 +106095072816802880785446250517258359371356549964859346325751724793738001008422 0 227220012957687545864426229191667293600 246770254904721260287425808259102883326 +36663409390673276751483064413776972596349494023383074980924833849240715914985 0 230187743392050124167268335285107339099 113059593568162008643646418169617685921 +82771506453137383921805646090184316226373521296248698278472145852657812654973 0 448680297043662761128367615186030747453 262213197051601850186326211611243750630 +63961727889177597433433858730561143931667179944225767027103327736231065696099 0 383046246971869687678886265006291417773 176507028690258621063089573000178683886 +78958182741591209735566019555046167360622345655285025222267245540000693237686 0 307912291337105562139537119019669321441 205848874805968588248301686005915074358 +29992541544181020685567466715431424759364607099134658865493940770134575976913 0 313333525502439697557329124795940920692 119522579582707356541133016141177173429 +55996337402705502978149476294220833964476263135957787076213671287801218036407 0 248653658166754470467954009769673979237 276357143630952130131740319087086825142 +97388654595383098119323361968512505408880391182023251416914044507619796828952 0 129607295120933934361947354739649396253 348702844153420909874274280858560595705 +103684421200131084868193986299612363056150648084335897887918260497897369235090 0 137988002724012727477191872780034713743 281923115583615970905143589103674297341 +51903898411245971311002978993768156073831050322488428489494694776389241427257 0 262623709047670399028123774298754795224 31207003832941590720457219524494852428 +58425740812556224150592989975848513144000880020796014870836565112334653910969 0 68158486446232977754554838151530747723 103012829212283472090535513492564260332 +37796766811528874602749994484146268556661763818946409615191822134163010896195 0 156162579596819770844738799408305517568 216013367627807166468937156952120714774 +104673787754110794527069099437830027691927980594776028872356770838068203806260 0 180507395316923827970381624873078371023 178646113611377959474138110551958431743 +45049215919159377471044030910333276930460202953886997427951830354892876391750 0 289076163967625382925306621033964902295 275857644218806224885962811288944950047 +63380093078641019264279595299599687418919536736701706690586662050919186274038 0 399343751961570017203410924552463623568 214629550774181167492620640131657432952 +113574786881534700522953391206121360810474649833543795441469078984157833695331 0 337585385764445108631145233545574984716 111193763139435755487945169649411985164 +49790013599705917814124073220601751501094132229628439049642413761065435294261 0 133366839688672057327444255549371842134 154705106335188742425870712888796851785 +64788086314106700834083217375850254865242934978855596692209349780129318378744 0 208048087191243283921553562292224008573 167962365335602158241904574503681973259 +21491352853103006049432930470687105869438626598819647480078412200076780080071 0 332927058661660714688841373072172537860 250843627315496890002575084365967548370 +32545348251896241054810690816139751919948032476162876085055732154364864057131 0 316420089474027539413130854556009781339 270726459303085467721289162826279737461 +31996583464016056235483983406448600381643612958093441920343439838927133445573 0 29765666218398212724673810874306110546 53399747936093672030894874965364064994 +89615799918538749381458729851193528246397510676297075907981857435559071071168 0 295961493720216429081105109644726719164 166422862787022480873353396305163229140 +15099232009191703557558812339937048250095387722489276214956414252433682422949 0 41556871202295228747165988589465572779 18694763296781371373688519867172104072 +57782406756368925624499515233394410310678496338615394643584084829055171163362 0 323173423073911508486605108651109896446 275664965293871722614277067423096860210 +83687148390003254520718432275833361406493655534410485533465629909784485351645 0 141707333321176834248589538053969047926 105093052280280330880420790614797103764 +94092632829203668064644865473590683890347120881122980873059859591305224541350 0 173463607522614220455498884551679162201 110097298199074074423678389142644523679 +80090736820260360487524344579373063292049864365463485605145955787985831409649 0 331405388022832422419399109218815665369 216771901726439591270277310542827853977 +4535777321770763218606323711246921035408569985909271950592300354642976197724 0 186391731486184025423142954209585423742 284613554603792609269913086961998880737 +30349365459394789718698194480160894678149070522952896486375773030035845255292 0 374909122453556624374459507677331897405 180673515562524763308695824558468012619 +72161671505952573796585894492305379034527876152238180794487819996016895314734 0 237579250085222191217791969107501240781 270701579459201000027844273593745256308 +48235355097859320774568350607756012532791518095832519070257073273821652387881 0 118852566568104430033747992761543500660 252476177183388145123352883841457568048 +43458547309016996164900737966450444092259120892532867520800108971738481241089 1 229858288423301440725886092157390239384 9465475033813460502057314905576362790 +87707849061176806397783023083472930256635791245553973448536304191247865297995 0 420158629397090655290956352806019948850 206730737632167395841059566861837480242 +32484531001265918515785349406978341204499124194739421246274581141032317566767 0 195662044073880514719905180740973104082 253767289227219312208752180951529687412 diff --git a/evm/src/cpu/kernel/tests/ecc/mod.rs b/evm/src/cpu/kernel/tests/ecc/mod.rs new file mode 100644 index 00000000..19bfc896 --- /dev/null +++ b/evm/src/cpu/kernel/tests/ecc/mod.rs @@ -0,0 +1,2 @@ +mod curve_ops; +mod ecrecover; diff --git a/evm/src/cpu/kernel/tests/mod.rs b/evm/src/cpu/kernel/tests/mod.rs index 2ae9d2b0..1d522d5c 100644 --- a/evm/src/cpu/kernel/tests/mod.rs +++ b/evm/src/cpu/kernel/tests/mod.rs @@ -1,8 +1,7 @@ mod account_code; mod balance; mod core; -mod curve_ops; -mod ecrecover; +mod ecc; mod exp; mod fields; mod hash; diff --git a/evm/src/memory/segments.rs b/evm/src/memory/segments.rs index 1587d890..a632e40f 100644 --- a/evm/src/memory/segments.rs +++ b/evm/src/memory/segments.rs @@ -39,10 +39,11 @@ pub(crate) enum Segment { /// instructions; initialised by `kernel/asm/shift.asm::init_shift_table()`. ShiftTable = 16, JumpdestBits = 17, + EcdsaTable = 18, } impl Segment { - pub(crate) const COUNT: usize = 18; + pub(crate) const COUNT: usize = 19; pub(crate) fn all() -> [Self; Self::COUNT] { [ @@ -64,6 +65,7 @@ impl Segment { Self::TrieEncodedChildLen, Self::ShiftTable, Self::JumpdestBits, + Self::EcdsaTable, ] } @@ -88,6 +90,7 @@ impl Segment { Segment::TrieEncodedChildLen => "SEGMENT_TRIE_ENCODED_CHILD_LEN", Segment::ShiftTable => "SEGMENT_SHIFT_TABLE", Segment::JumpdestBits => "SEGMENT_JUMPDEST_BITS", + Segment::EcdsaTable => "SEGMENT_KERNEL_ECDSA_TABLE", } } @@ -112,6 +115,7 @@ impl Segment { Segment::TrieEncodedChildLen => 6, Segment::ShiftTable => 256, Segment::JumpdestBits => 1, + Segment::EcdsaTable => 256, } } }