From 68a48fb36cf521097e18220afb78de24ce67187a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= <4142+huitseeker@users.noreply.github.com> Date: Mon, 7 Oct 2024 07:35:56 -0400 Subject: [PATCH] refactor: Remove BLS12381 G2 Add and Double functionalities (#183) * refactor: Remove BLS12381 G2 Add and Double functionalities - Removed the `bls12381-g2-add` and `bls12381-g2-double` functions related to BLS12381 G2Affine points from various files. - Deleted associated tests, files, modules, and references from test suite, codebase, libraries, and syscall map. * chore: bump the circuit version --- .gitignore | 3 +- core/src/lib.rs | 2 +- core/src/runtime/record.rs | 44 +- core/src/runtime/syscall.rs | 20 - .../syscall/precompiles/bls12_381/g2_add.rs | 813 ------------------ .../precompiles/bls12_381/g2_double.rs | 701 --------------- core/src/syscall/precompiles/bls12_381/mod.rs | 2 - core/src/utils/ec/weierstrass/bls12_381.rs | 1 + core/src/utils/programs.rs | 6 - examples/bls12381-pairing/program/Cargo.toml | 1 - prover/src/lib.rs | 111 --- tests/bls12381-g2-add/Cargo.lock | 271 ------ tests/bls12381-g2-add/Cargo.toml | 8 - .../elf/riscv32im-succinct-zkvm-elf | Bin 52308 -> 0 bytes tests/bls12381-g2-add/src/main.rs | 58 -- tests/bls12381-g2-double/Cargo.lock | 271 ------ tests/bls12381-g2-double/Cargo.toml | 8 - .../elf/riscv32im-succinct-zkvm-elf | Bin 51284 -> 0 bytes tests/bls12381-g2-double/src/main.rs | 48 -- zkvm/entrypoint/src/syscalls/bls12_381.rs | 40 - zkvm/entrypoint/src/syscalls/mod.rs | 2 - zkvm/lib/src/lib.rs | 2 - 22 files changed, 5 insertions(+), 2407 deletions(-) delete mode 100644 core/src/syscall/precompiles/bls12_381/g2_add.rs delete mode 100644 core/src/syscall/precompiles/bls12_381/g2_double.rs delete mode 100644 tests/bls12381-g2-add/Cargo.lock delete mode 100644 tests/bls12381-g2-add/Cargo.toml delete mode 100755 tests/bls12381-g2-add/elf/riscv32im-succinct-zkvm-elf delete mode 100644 tests/bls12381-g2-add/src/main.rs delete mode 100644 tests/bls12381-g2-double/Cargo.lock delete mode 100644 tests/bls12381-g2-double/Cargo.toml delete mode 100755 tests/bls12381-g2-double/elf/riscv32im-succinct-zkvm-elf delete mode 100644 tests/bls12381-g2-double/src/main.rs diff --git a/.gitignore b/.gitignore index 42ef47a62..5fd4da336 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,5 @@ prover/build prover/*.tar.gz # IDE Conf -.idea \ No newline at end of file +.idea +prover/data diff --git a/core/src/lib.rs b/core/src/lib.rs index 850b82058..da87b2ec9 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -30,4 +30,4 @@ use stark::StarkGenericConfig; /// This string should be updated whenever any step in verifying an SP1 proof changes, including /// core, recursion, and plonk-bn254. This string is used to download SP1 artifacts and the gnark /// docker image. -pub const SPHINX_CIRCUIT_VERSION: &str = "v1.0.8.2-testnet"; +pub const SPHINX_CIRCUIT_VERSION: &str = "v1.0.8.3-testnet"; diff --git a/core/src/runtime/record.rs b/core/src/runtime/record.rs index 47cd8759a..206a84eda 100644 --- a/core/src/runtime/record.rs +++ b/core/src/runtime/record.rs @@ -28,11 +28,7 @@ use crate::{ operations::field::params::FieldParameters, stark::Indexed, syscall::precompiles::{ - bls12_381::{ - g1_decompress::{Bls12381G1DecompressChip, Bls12381G1DecompressEvent}, - g2_add::{Bls12381G2AffineAddChip, Bls12381G2AffineAddEvent}, - g2_double::{Bls12381G2AffineDoubleChip, Bls12381G2AffineDoubleEvent}, - }, + bls12_381::g1_decompress::{Bls12381G1DecompressChip, Bls12381G1DecompressEvent}, field::{FieldChip, FieldEvent}, quad_field::{QuadFieldChip, QuadFieldEvent}, secp256k1::decompress::{Secp256k1DecompressChip, Secp256k1DecompressEvent}, @@ -131,8 +127,6 @@ pub struct ExecutionRecord { pub bls12381_fp_events: Vec>, pub bls12381_fp2_events: Vec>, pub bls12381_g1_decompress_events: Vec, - pub bls12381_g2_add_events: Vec, - pub bls12381_g2_double_events: Vec, // Blake2s pub blake2s_round_events: Vec, @@ -262,18 +256,6 @@ impl EventLens for ExecutionRecord { } } -impl EventLens for ExecutionRecord { - fn events(&self) -> >::Events { - &self.bls12381_g2_add_events - } -} - -impl EventLens for ExecutionRecord { - fn events(&self) -> >::Events { - &self.bls12381_g2_double_events - } -} - impl EventLens> for ExecutionRecord { fn events(&self) -> as crate::air::WithEvents<'_>>::Events { &self.bls12381_fp_events @@ -495,14 +477,6 @@ impl MachineRecord for ExecutionRecord { "bls12381_fp2_events".to_string(), self.bls12381_fp2_events.len(), ); - stats.insert( - "bls12381_g2_add_events".to_string(), - self.bls12381_g2_add_events.len(), - ); - stats.insert( - "bls12381_g2_double_events".to_string(), - self.bls12381_g2_double_events.len(), - ); stats.insert( "blake2s_round_events".to_string(), @@ -554,10 +528,6 @@ impl MachineRecord for ExecutionRecord { .append(&mut other.bls12381_fp2_events); self.bls12381_g1_decompress_events .append(&mut other.bls12381_g1_decompress_events); - self.bls12381_g2_add_events - .append(&mut other.bls12381_g2_add_events); - self.bls12381_g2_double_events - .append(&mut other.bls12381_g2_double_events); self.blake2s_round_events .append(&mut other.blake2s_round_events); @@ -887,18 +857,6 @@ impl MachineRecord for ExecutionRecord { self.nonce_lookup.insert(event.lookup_id, i as u32); } - // Bls12-381 G2Affine addition events. - first.bls12381_g2_add_events = take(&mut self.bls12381_g2_add_events); - for (i, event) in first.bls12381_g2_add_events.iter().enumerate() { - self.nonce_lookup.insert(event.lookup_id, i as u32); - } - - // Bls12-381 G2Affine doubling events. - first.bls12381_g2_double_events = take(&mut self.bls12381_g2_double_events); - for (i, event) in first.bls12381_g2_double_events.iter().enumerate() { - self.nonce_lookup.insert(event.lookup_id, i as u32); - } - // blake2s_round events first.blake2s_round_events = take(&mut self.blake2s_round_events); for (i, event) in first.blake2s_round_events.iter().enumerate() { diff --git a/core/src/runtime/syscall.rs b/core/src/runtime/syscall.rs index a30a03936..33a9eb33f 100644 --- a/core/src/runtime/syscall.rs +++ b/core/src/runtime/syscall.rs @@ -8,8 +8,6 @@ use crate::runtime::{Register, Runtime}; use crate::stark::Ed25519Parameters; use crate::syscall::precompiles::blake2s::Blake2sRoundChip; use crate::syscall::precompiles::bls12_381::g1_decompress::Bls12381G1DecompressChip; -use crate::syscall::precompiles::bls12_381::g2_add::Bls12381G2AffineAddChip; -use crate::syscall::precompiles::bls12_381::g2_double::Bls12381G2AffineDoubleChip; use crate::syscall::precompiles::edwards::EdAddAssignChip; use crate::syscall::precompiles::edwards::EdDecompressChip; use crate::syscall::precompiles::field::{FieldAddSyscall, FieldMulSyscall, FieldSubSyscall}; @@ -101,8 +99,6 @@ pub enum SyscallCode { BLS12381_FP2_ADD = 0x00_01_01_77, BLS12381_FP2_SUB = 0x00_01_01_78, BLS12381_FP2_MUL = 0x00_01_01_79, - BLS12381_G2_ADD = 0x00_01_01_80, - BLS12381_G2_DOUBLE = 0x00_00_01_81, /// Executes the `SHA512_EXTEND` precompile. SHA512_EXTEND = 0x00_00_01_C1, @@ -160,8 +156,6 @@ impl SyscallCode { 0x00_01_01_71 => SyscallCode::BLS12381_G1_ADD, 0x00_00_01_72 => SyscallCode::BLS12381_G1_DOUBLE, 0x00_01_01_F2 => SyscallCode::BLS12381_G1_DECOMPRESS, - 0x00_01_01_80 => SyscallCode::BLS12381_G2_ADD, - 0x00_00_01_81 => SyscallCode::BLS12381_G2_DOUBLE, 0x00_01_01_ED => SyscallCode::BLAKE_2S_ROUND, 0x00_00_01_C1 => SyscallCode::SHA512_EXTEND, 0x00_00_01_C2 => SyscallCode::SHA512_COMPRESS, @@ -394,14 +388,6 @@ pub fn default_syscall_map() -> HashMap> { SyscallCode::BLS12381_G1_DECOMPRESS, Arc::new(Bls12381G1DecompressChip::new()), ); - syscall_map.insert( - SyscallCode::BLS12381_G2_ADD, - Arc::new(Bls12381G2AffineAddChip::new()), - ); - syscall_map.insert( - SyscallCode::BLS12381_G2_DOUBLE, - Arc::new(Bls12381G2AffineDoubleChip::new()), - ); syscall_map.insert( SyscallCode::SHA512_EXTEND, Arc::new(Sha512ExtendChip::new()), @@ -523,12 +509,6 @@ mod tests { } SyscallCode::HINT_LEN => assert_eq!(code as u32, sphinx_zkvm::syscalls::HINT_LEN), SyscallCode::HINT_READ => assert_eq!(code as u32, sphinx_zkvm::syscalls::HINT_READ), - SyscallCode::BLS12381_G2_ADD => { - assert_eq!(code as u32, sphinx_zkvm::syscalls::BLS12381_G2_ADD) - } - SyscallCode::BLS12381_G2_DOUBLE => { - assert_eq!(code as u32, sphinx_zkvm::syscalls::BLS12381_G2_DOUBLE) - } SyscallCode::BLAKE_2S_ROUND => { assert_eq!(code as u32, sphinx_zkvm::syscalls::BLAKE_2S_ROUND) } diff --git a/core/src/syscall/precompiles/bls12_381/g2_add.rs b/core/src/syscall/precompiles/bls12_381/g2_add.rs deleted file mode 100644 index f9c2dd814..000000000 --- a/core/src/syscall/precompiles/bls12_381/g2_add.rs +++ /dev/null @@ -1,813 +0,0 @@ -use crate::air::{AluAirBuilder, EventLens, MachineAir, MemoryAirBuilder, WithEvents}; -use crate::bytes::event::ByteRecord; -use crate::memory::{MemoryCols, MemoryReadCols, MemoryWriteCols}; -use crate::operations::field::extensions::quadratic::{QuadFieldOpCols, QuadFieldOperation}; -use crate::operations::field::params::{FieldParameters, WORDS_QUAD_EXT_CURVEPOINT}; -use crate::operations::field::params::{Limbs, WORDS_QUAD_EXT_FIELD_ELEMENT}; -use crate::runtime::{ - ExecutionRecord, MemoryReadRecord, MemoryWriteRecord, Syscall, SyscallCode, SyscallContext, -}; -use crate::utils::ec::weierstrass::bls12_381::{bls12381_g2_add, Bls12381BaseField}; -use crate::utils::ec::AffinePoint; -use crate::utils::{limbs_from_access, limbs_from_prev_access, pad_rows}; -use crate::Program; -use core::borrow::{Borrow, BorrowMut}; -use hybrid_array::{typenum::Unsigned, Array}; -use num::{BigUint, Zero}; -use p3_air::{Air, AirBuilder, BaseAir}; -use p3_field::{AbstractField, PrimeField32}; -use p3_matrix::dense::RowMajorMatrix; -use p3_matrix::Matrix; -use serde::Deserialize; -use serde::Serialize; -use sphinx_derive::AlignedBorrow; -use std::mem::size_of; - -/// Chip for adding to BLS12-381 G2Affine points (A and B). -/// -/// The algorithm used for computing the addition inside the circuit doesn't require converting -/// input points to projective representation, however it has following unsupported corner cases: -/// -/// - if A is point on infinity, -/// - if B is point on infinity, -/// - if A equals B, -/// - if A equals -B. -/// -#[derive(Default)] -pub struct Bls12381G2AffineAddChip; - -impl Bls12381G2AffineAddChip { - pub fn new() -> Self { - Self {} - } - - fn populate_cols( - record: &mut impl ByteRecord, - shard: u32, - channel: u32, - cols: &mut Bls12381G2AffineAddCols, - a_x: &[BigUint; 2], - a_y: &[BigUint; 2], - b_x: &[BigUint; 2], - b_y: &[BigUint; 2], - ) { - let slope = { - let slope_numerator = cols.slope_numerator.populate( - record, - shard, - channel, - &[b_y[0].clone(), b_y[1].clone()], - &[a_y[0].clone(), a_y[1].clone()], - QuadFieldOperation::Sub, - ); - - let slope_denominator = cols.slope_denominator.populate( - record, - shard, - channel, - &[b_x[0].clone(), b_x[1].clone()], - &[a_x[0].clone(), a_x[1].clone()], - QuadFieldOperation::Sub, - ); - - cols.slope.populate( - record, - shard, - channel, - &slope_numerator, - &slope_denominator, - QuadFieldOperation::Div, - ) - }; - - let x = { - let slope_squared = cols.slope_squared.populate( - record, - shard, - channel, - &slope, - &slope, - QuadFieldOperation::Mul, - ); - let p_x_plus_q_x = cols.p_x_plus_q_x.populate( - record, - shard, - channel, - &[a_x[0].clone(), a_x[1].clone()], - &[b_x[0].clone(), b_x[1].clone()], - QuadFieldOperation::Add, - ); - cols.x3_ins.populate( - record, - shard, - channel, - &slope_squared, - &p_x_plus_q_x, - QuadFieldOperation::Sub, - ) - }; - - { - let p_x_minus_x = cols.p_x_minus_x.populate( - record, - shard, - channel, - &[a_x[0].clone(), a_x[1].clone()], - &x, - QuadFieldOperation::Sub, - ); - let slope_times_p_x_minus_x = cols.slope_times_p_x_minus_x.populate( - record, - shard, - channel, - &slope, - &p_x_minus_x, - QuadFieldOperation::Mul, - ); - cols.y3_ins.populate( - record, - shard, - channel, - &slope_times_p_x_minus_x, - &[a_y[0].clone(), a_y[1].clone()], - QuadFieldOperation::Sub, - ) - }; - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Bls12381G2AffineAddEvent { - pub lookup_id: usize, - pub clk: u32, - pub shard: u32, - pub channel: u32, - pub a_ptr: u32, - #[serde(with = "crate::utils::array_serde::ArraySerde")] - pub a_x: - Array::NB_LIMBS>>, - #[serde(with = "crate::utils::array_serde::ArraySerde")] - pub a_y: - Array::NB_LIMBS>>, - #[serde(with = "crate::utils::array_serde::ArraySerde")] - pub b_x: - Array::NB_LIMBS>>, - #[serde(with = "crate::utils::array_serde::ArraySerde")] - pub b_y: - Array::NB_LIMBS>>, - - #[serde(with = "crate::utils::array_serde::ArraySerde")] - pub a_memory_records: Array< - MemoryWriteRecord, - WORDS_QUAD_EXT_CURVEPOINT<::NB_LIMBS>, - >, - pub b_ptr: u32, - #[serde(with = "crate::utils::array_serde::ArraySerde")] - pub b_memory_records: Array< - MemoryReadRecord, - WORDS_QUAD_EXT_CURVEPOINT<::NB_LIMBS>, - >, -} - -impl Syscall for Bls12381G2AffineAddChip { - fn execute(&self, ctx: &mut SyscallContext<'_, '_>, a_ptr: u32, b_ptr: u32) -> Option { - let clk = ctx.clk; - let shard = ctx.current_shard(); - let channel = ctx.current_channel(); - let lookup_id = ctx.syscall_lookup_id; - - assert_eq!(a_ptr % 4, 0, "arg1 ptr must be 4-byte aligned"); - assert_eq!(b_ptr % 4, 0, "arg2 ptr must be 4-byte aligned"); - - let words_len = - ::NB_LIMBS>>::USIZE; - - let a_vec = ctx.slice_unsafe(a_ptr, words_len); - let (b_memory_records, b_vec) = ctx.mr_slice(b_ptr, words_len); - - let a_x: Array< - u32, - WORDS_QUAD_EXT_FIELD_ELEMENT<::NB_LIMBS>, - > = (&a_vec[0..words_len / 2]).try_into().unwrap(); - let a_y: Array< - u32, - WORDS_QUAD_EXT_FIELD_ELEMENT<::NB_LIMBS>, - > = (&a_vec[words_len / 2..words_len]).try_into().unwrap(); - let b_x: Array< - u32, - WORDS_QUAD_EXT_FIELD_ELEMENT<::NB_LIMBS>, - > = (&b_vec[0..words_len / 2]).try_into().unwrap(); - let b_y: Array< - u32, - WORDS_QUAD_EXT_FIELD_ELEMENT<::NB_LIMBS>, - > = (&b_vec[words_len / 2..words_len]).try_into().unwrap(); - - let a_x_c0 = BigUint::new(a_x[0..12].to_vec()); - let a_x_c1 = BigUint::new(a_x[12..24].to_vec()); - let a_y_c0 = BigUint::new(a_y[0..12].to_vec()); - let a_y_c1 = BigUint::new(a_y[12..24].to_vec()); - - let b_x_c0 = BigUint::new(b_x[0..12].to_vec()); - let b_x_c1 = BigUint::new(b_x[12..24].to_vec()); - let b_y_c0 = BigUint::new(b_y[0..12].to_vec()); - let b_y_c1 = BigUint::new(b_y[12..24].to_vec()); - - let result = bls12381_g2_add( - &[a_x_c0, a_x_c1, a_y_c0, a_y_c1], - &[b_x_c0, b_x_c1, b_y_c0, b_y_c1], - ); - - fn biguint_to_words(input: &BigUint) -> Vec { - let mut words = input.to_u32_digits(); - // single Fp2 element in BLS12381 occupies 12 u32 words - words.resize(12, 0); - words - } - - let result_words = [ - biguint_to_words(&result[0]), - biguint_to_words(&result[1]), - biguint_to_words(&result[2]), - biguint_to_words(&result[3]), - ] - .concat(); - - // When we write to p, we want the clk to be incremented because p and q could be the same. - ctx.clk += 1; - - let a_memory_records: Array< - MemoryWriteRecord, - ::NB_LIMBS, - > = (&ctx.mw_slice(a_ptr, &result_words)[..]) - .try_into() - .unwrap(); - - ctx.record_mut() - .bls12381_g2_add_events - .push(Bls12381G2AffineAddEvent { - lookup_id, - clk, - shard, - channel, - a_ptr, - a_x, - a_y, - b_x, - b_y, - a_memory_records, - b_ptr, - b_memory_records: (&b_memory_records[..]).try_into().unwrap(), - }); - - None - } - - fn num_extra_cycles(&self) -> u32 { - 1 - } -} - -#[derive(Debug, Clone, AlignedBorrow)] -#[repr(C)] -pub struct Bls12381G2AffineAddCols { - pub clk: T, - pub shard: T, - pub channel: T, - pub nonce: T, - pub is_real: T, - - pub a_ptr: T, - pub a_access: Array, ::NB_LIMBS>, - pub b_ptr: T, - pub b_access: Array, ::NB_LIMBS>, - - pub(crate) slope_denominator: QuadFieldOpCols, - pub(crate) slope_numerator: QuadFieldOpCols, - pub(crate) slope: QuadFieldOpCols, - pub(crate) slope_squared: QuadFieldOpCols, - pub(crate) p_x_plus_q_x: QuadFieldOpCols, - pub(crate) x3_ins: QuadFieldOpCols, - pub(crate) p_x_minus_x: QuadFieldOpCols, - pub(crate) y3_ins: QuadFieldOpCols, - pub(crate) slope_times_p_x_minus_x: QuadFieldOpCols, -} - -impl BaseAir for Bls12381G2AffineAddChip { - fn width(&self) -> usize { - size_of::>() - } -} - -impl<'a> WithEvents<'a> for Bls12381G2AffineAddChip { - type Events = &'a [Bls12381G2AffineAddEvent]; -} - -impl MachineAir for Bls12381G2AffineAddChip { - type Record = ExecutionRecord; - type Program = Program; - - fn name(&self) -> String { - "G2AffineAdd".to_string() - } - - fn generate_trace>( - &self, - input: &EL, - output: &mut Self::Record, - ) -> RowMajorMatrix { - let mut rows = vec![]; - - let mut new_byte_lookup_events = Vec::new(); - - let width = >::width(self); - for event in input.events() { - let mut row = vec![F::zero(); width]; - let cols: &mut Bls12381G2AffineAddCols = - row.as_mut_slice().borrow_mut(); - - cols.clk = F::from_canonical_u32(event.clk); - cols.is_real = F::one(); - cols.shard = F::from_canonical_u32(event.shard); - cols.channel = F::from_canonical_u32(event.channel); - - // Data - cols.a_ptr = F::from_canonical_u32(event.a_ptr); - cols.b_ptr = F::from_canonical_u32(event.b_ptr); - - let a_x = AffinePoint::::from_words_le(&event.a_x); - let a_y = AffinePoint::::from_words_le(&event.a_y); - let b_x = AffinePoint::::from_words_le(&event.b_x); - let b_y = AffinePoint::::from_words_le(&event.b_y); - - let (a_x_c0, a_x_c1) = (a_x.x, a_x.y); - let (a_y_c0, a_y_c1) = (a_y.x, a_y.y); - let (b_x_c0, b_x_c1) = (b_x.x, b_x.y); - let (b_y_c0, b_y_c1) = (b_y.x, b_y.y); - - for i in 0..::NB_LIMBS::USIZE { - cols.a_access[i].populate( - event.channel, - event.a_memory_records[i], - &mut new_byte_lookup_events, - ); - } - - for i in 0..::NB_LIMBS::USIZE { - cols.b_access[i].populate( - event.channel, - event.b_memory_records[i], - &mut new_byte_lookup_events, - ); - } - - Self::populate_cols( - &mut new_byte_lookup_events, - event.shard, - event.channel, - cols, - &[a_x_c0, a_x_c1], - &[a_y_c0, a_y_c1], - &[b_x_c0, b_x_c1], - &[b_y_c0, b_y_c1], - ); - - rows.push(row); - } - - output.add_byte_lookup_events(new_byte_lookup_events); - - pad_rows(&mut rows, || { - let mut row = vec![F::zero(); width]; - let cols: &mut Bls12381G2AffineAddCols = - row.as_mut_slice().borrow_mut(); - - cols.clk = F::zero(); - cols.is_real = F::zero(); - cols.shard = F::zero(); - cols.channel = F::zero(); - - let zero = BigUint::zero(); - Self::populate_cols( - &mut vec![], - 0, - 0, - cols, - &[zero.clone(), zero.clone()], - &[zero.clone(), zero.clone()], - &[zero.clone(), zero.clone()], - &[zero.clone(), zero.clone()], - ); - row - }); - - let mut trace = - RowMajorMatrix::::new(rows.into_iter().flatten().collect::>(), width); - - // Write the nonces to the trace. - for i in 0..trace.height() { - let cols: &mut Bls12381G2AffineAddCols = - trace.values[i * width..(i + 1) * width].borrow_mut(); - cols.nonce = F::from_canonical_usize(i); - } - - trace - } - - fn included(&self, shard: &Self::Record) -> bool { - !shard.bls12381_g2_add_events.is_empty() - } -} - -impl Air for Bls12381G2AffineAddChip -where - AB: MemoryAirBuilder, - AB::F: PrimeField32, -{ - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let local = main.row_slice(0); - let local: &Bls12381G2AffineAddCols = (*local).borrow(); - let next = main.row_slice(1); - let next: &Bls12381G2AffineAddCols = (*next).borrow(); - - // Constrain the incrementing nonce. - builder.when_first_row().assert_zero(local.nonce); - builder - .when_transition() - .assert_eq(local.nonce + AB::Expr::one(), next.nonce); - - let p_x_c0: Limbs<_, ::NB_LIMBS> = - limbs_from_prev_access(&local.a_access[0..12]); - let p_x_c1: Limbs<_, ::NB_LIMBS> = - limbs_from_prev_access(&local.a_access[12..24]); - let p_y_c0: Limbs<_, ::NB_LIMBS> = - limbs_from_prev_access(&local.a_access[24..36]); - let p_y_c1: Limbs<_, ::NB_LIMBS> = - limbs_from_prev_access(&local.a_access[36..48]); - - let q_x_c0: Limbs<_, ::NB_LIMBS> = - limbs_from_access(&local.b_access[0..12]); - let q_x_c1: Limbs<_, ::NB_LIMBS> = - limbs_from_access(&local.b_access[12..24]); - let q_y_c0: Limbs<_, ::NB_LIMBS> = - limbs_from_access(&local.b_access[24..36]); - let q_y_c1: Limbs<_, ::NB_LIMBS> = - limbs_from_access(&local.b_access[36..48]); - - let slope = { - local.slope_numerator.eval( - builder, - &[q_y_c0, q_y_c1], - &[p_y_c0, p_y_c1], - QuadFieldOperation::Sub, - local.shard, - local.channel, - local.is_real, - ); - - local.slope_denominator.eval( - builder, - &[q_x_c0, q_x_c1], - &[p_x_c0, p_x_c1], - QuadFieldOperation::Sub, - local.shard, - local.channel, - local.is_real, - ); - - local.slope.eval( - builder, - &local.slope_numerator.result, - &local.slope_denominator.result, - QuadFieldOperation::Div, - local.shard, - local.channel, - local.is_real, - ); - - local.slope.result - }; - - let x = { - local.slope_squared.eval( - builder, - &slope, - &slope, - QuadFieldOperation::Mul, - local.shard, - local.channel, - local.is_real, - ); - - local.p_x_plus_q_x.eval( - builder, - &[p_x_c0, p_x_c1], - &[q_x_c0, q_x_c1], - QuadFieldOperation::Add, - local.shard, - local.channel, - local.is_real, - ); - - local.x3_ins.eval( - builder, - &local.slope_squared.result, - &local.p_x_plus_q_x.result, - QuadFieldOperation::Sub, - local.shard, - local.channel, - local.is_real, - ); - - local.x3_ins.result - }; - - { - local.p_x_minus_x.eval( - builder, - &[p_x_c0, p_x_c1], - &x, - QuadFieldOperation::Sub, - local.shard, - local.channel, - local.is_real, - ); - - local.slope_times_p_x_minus_x.eval( - builder, - &slope, - &local.p_x_minus_x.result, - QuadFieldOperation::Mul, - local.shard, - local.channel, - local.is_real, - ); - - local.y3_ins.eval( - builder, - &local.slope_times_p_x_minus_x.result, - &[p_y_c0, p_y_c1], - QuadFieldOperation::Sub, - local.shard, - local.channel, - local.is_real, - ); - } - - // Constraint self.p_access.value = [self.x3_ins.result, self.y3_ins.result]. This is to - // ensure that p_access is updated with the new value. - let x3_ins_x = &local.x3_ins.result[0]; - let x3_ins_y = &local.x3_ins.result[1]; - let y3_ins_x = &local.y3_ins.result[0]; - let y3_ins_y = &local.y3_ins.result[1]; - for i in 0..48 { - builder - .when(local.is_real) - .assert_eq(x3_ins_x[i], local.a_access[i / 4].value()[i % 4]); - builder - .when(local.is_real) - .assert_eq(x3_ins_y[i], local.a_access[12 + i / 4].value()[i % 4]); - builder - .when(local.is_real) - .assert_eq(y3_ins_x[i], local.a_access[24 + i / 4].value()[i % 4]); - builder - .when(local.is_real) - .assert_eq(y3_ins_y[i], local.a_access[36 + i / 4].value()[i % 4]); - } - - // Memory constraints - for i in 0..local.a_access.len() { - builder.eval_memory_access( - local.shard, - local.channel, - local.clk + AB::F::from_canonical_u32(1), // We eval 'a' pointer access at clk+1 since 'a', 'b' could be the same - local.a_ptr.into() + AB::F::from_canonical_u32((i as u32) * 4), - &local.a_access[i], - local.is_real, - ); - } - - for i in 0..local.b_access.len() { - builder.eval_memory_access( - local.shard, - local.channel, - local.clk, - local.b_ptr.into() + AB::F::from_canonical_u32((i as u32) * 4), - &local.b_access[i], - local.is_real, - ); - } - - builder.receive_syscall( - local.shard, - local.channel, - local.clk, - local.nonce, - AB::F::from_canonical_u32(SyscallCode::BLS12381_G2_ADD.syscall_id()), - local.a_ptr, - local.b_ptr, - local.is_real, - ) - } -} - -#[allow(unused)] // Disabling for recursion performance -#[cfg(test)] -mod tests { - use crate::runtime::{Instruction, Opcode, SyscallCode}; - use crate::utils::ec::weierstrass::bls12_381::fp_to_biguint; - use crate::utils::tests::BLS12381_G2_ADD_ELF; - use crate::utils::{run_test, run_test_with_memory_inspection, setup_logger}; - use crate::Program; - use bls12_381::G2Projective; - use elliptic_curve::{group::Curve, Group}; - use num::{BigUint, Num}; - use rand::rngs::OsRng; - - fn biguint_str_to_words(input: &str, radix: u32) -> Vec { - let output = BigUint::from_str_radix(input, radix).unwrap(); - biguint_to_words(&output) - } - - fn biguint_to_words(input: &BigUint) -> Vec { - let mut words = input.to_u32_digits(); - words.resize(12, 0); - words - } - - fn risc_v_program(a_ptr: u32, b_ptr: u32, a_words: Vec, b_words: Vec) -> Program { - let mut instructions = vec![]; - for (index, word) in a_words.into_iter().enumerate() { - instructions.push(Instruction::new(Opcode::ADD, 29, 0, word, false, true)); - instructions.push(Instruction::new( - Opcode::ADD, - 30, - 0, - a_ptr + (index * 4) as u32, - false, - true, - )); - instructions.push(Instruction::new(Opcode::SW, 29, 30, 0, false, true)); - } - - for (index, word) in b_words.into_iter().enumerate() { - instructions.push(Instruction::new(Opcode::ADD, 29, 0, word, false, true)); - instructions.push(Instruction::new( - Opcode::ADD, - 30, - 0, - b_ptr + (index * 4) as u32, - false, - true, - )); - instructions.push(Instruction::new(Opcode::SW, 29, 30, 0, false, true)); - } - - instructions.push(Instruction::new( - Opcode::ADD, - 5, - 0, - SyscallCode::BLS12381_G2_ADD as u32, - false, - true, - )); - instructions.push(Instruction::new(Opcode::ADD, 10, 0, a_ptr, false, true)); - instructions.push(Instruction::new(Opcode::ADD, 11, 0, b_ptr, false, true)); - instructions.push(Instruction::new(Opcode::ECALL, 5, 10, 11, false, false)); - Program::new(instructions, 0, 0) - } - - fn execute_risc_v_test(a_words: Vec, b_words: Vec, expected: &[BigUint]) { - let a_ptr = 10000000u32; - let b_ptr = 20000000u32; - - setup_logger(); - let program = risc_v_program(a_ptr, b_ptr, a_words, b_words); - let (_, memory) = run_test_with_memory_inspection(program); - let mut result = vec![]; - // Fp / BigUint is encoded as a 12 u32 words. G2Affine point has 4 Fp elements, so we read 4 * 12 words from the memory - for i in 0..48 { - result.push(memory.get(&(a_ptr + i * 4)).unwrap().value); - } - - let computed_x_c0 = BigUint::new(result[0..12].to_vec()); - let computed_x_c1 = BigUint::new(result[12..24].to_vec()); - let computed_y_c0 = BigUint::new(result[24..36].to_vec()); - let computed_y_c1 = BigUint::new(result[36..48].to_vec()); - - assert_eq!(computed_x_c0, expected[0]); - assert_eq!(computed_x_c1, expected[1]); - assert_eq!(computed_y_c0, expected[2]); - assert_eq!(computed_y_c1, expected[3]); - } - - // Disabled for recursion performance - // #[test] - fn test_bls12381_g2_affine_add_precompile() { - // input data - let a_x_c0 = biguint_str_to_words("3017839990326613039145041105403203768289907560485999954764669466782738776913278597336115197412326608157502898901494", 10); - let a_x_c1 = biguint_str_to_words("1968364904179875953612227826050294324304687258024434156939687758255052288526966247408321096642287030833236074834637", 10); - let a_y_c0 = biguint_str_to_words("1112963802227266471936425299599962264551592268216698728003246008956141517020182272707792452981388955804771234793026", 10); - let a_y_c1 = biguint_str_to_words("3601956566756065634979731486354880834166415754665429377259877200484386122313208466384188001260145428371483966256158", 10); - - let b_x_c0 = biguint_str_to_words("3995345726713524343478694139317244904221986402748125746531220355264073737425831917431067307136350406235257521914720", 10); - let b_x_c1 = biguint_str_to_words("2371713999659141329582895752583386038540725886998376058382441223953471437659156083018472482942487601301212281350719", 10); - let b_y_c0 = biguint_str_to_words("1657736472727646860487013511699214065739000373955759070260564759907290637218762525626953919644264064293125883245513", 10); - let b_y_c1 = biguint_str_to_words("669840849348882079501065523381492957342969119764450012349355587264902894823664213163993856854342667498557678470765", 10); - - let expected_x_c0 = BigUint::from_str_radix("2217453026271814368440203317808683516910566559070156396650784209828414583877914335476042658864001902388991070392394", 10).unwrap(); - let expected_x_c1 = BigUint::from_str_radix("3735586588151792717344356536686990055696764520142017086471655175341858001563444141499357084899636851157467845644056", 10).unwrap(); - let expected_y_c0 = BigUint::from_str_radix("2258336512095698119772266602759054637810622833780250581163613657159437682816906766646529574247756287023363655074151", 10).unwrap(); - let expected_y_c1 = BigUint::from_str_radix("941210928186334692595956191674128264366290431929708551370700737070865409698010117261045198014833047669542376970151", 10).unwrap(); - - let a_words = [a_x_c0, a_x_c1, a_y_c0, a_y_c1].concat(); - let b_words = [b_x_c0, b_x_c1, b_y_c0, b_y_c1].concat(); - - execute_risc_v_test( - a_words, - b_words, - vec![expected_x_c0, expected_x_c1, expected_y_c0, expected_y_c1].as_slice(), - ); - } - - // #[test] - fn test_bls12381_g2_affine_add_precompile_flaky_input() { - // input data - let a_x_c0 = biguint_str_to_words("940678610412633391924225779762290732605526547639243864351304234419401586596082223466014582312599779726285805697475", 10); - let a_x_c1 = biguint_str_to_words("3970533371664127278374320743636293284643681224131866516566888981399830088697294165563145438098385314712450903750583", 10); - let a_y_c0 = biguint_str_to_words("2871772792170856534319532679530995220771426110922375294987607996910186965076421817067724466403137338049516993640951", 10); - let a_y_c1 = biguint_str_to_words("0053793603554162309816446837984978293593915145569675366398752348829921241608048564007856072778551661809103745377287", 10); - - let b_x_c0 = biguint_str_to_words("1331464510641249323839094619361852670403027671905433475300506442976146288503285736268135124866206040312808602176295", 10); - let b_x_c1 = biguint_str_to_words("3027642434952722503753323015041364214878978079475767163845055204071467562888064074234522216329340479780081790725137", 10); - let b_y_c0 = biguint_str_to_words("200696228981224618855716420820649730377778982335265086880186071238717972653859952113546787814946905099483255668391", 10); - let b_y_c1 = biguint_str_to_words("2577651373384445415166436815683162788302596986034982084134306770915573381249081261772662199090886949623499138384248", 10); - - let expected_x_c0 = BigUint::from_str_radix("2860343709557806964027158749871320254572140155920054742718333850477275802846203645466077272289804508903032673035205", 10).unwrap(); - let expected_x_c1 = BigUint::from_str_radix("2104523116857637401022553203989683783163518619859130296649146989961080115867556546075155138043913256307617354725201", 10).unwrap(); - let expected_y_c0 = BigUint::from_str_radix("3285167425898843195224794751434504763710550311489867065524684573545527680992036398300984330533695019506363839092244", 10).unwrap(); - let expected_y_c1 = BigUint::from_str_radix("700752659476098625384975476746701395987863643330498795166428473984414216525778183396070281760298054977309932101839", 10).unwrap(); - - let a_words = [a_x_c0, a_x_c1, a_y_c0, a_y_c1].concat(); - let b_words = [b_x_c0, b_x_c1, b_y_c0, b_y_c1].concat(); - - execute_risc_v_test( - a_words, - b_words, - vec![expected_x_c0, expected_x_c1, expected_y_c0, expected_y_c1].as_slice(), - ); - } - - // #[test] - fn test_bls12381_g2_affine_add_precompile_randomized_input() { - let mut rng = OsRng; - let a = G2Projective::random(&mut rng); - let b = G2Projective::random(&mut rng); - - let expected = (a + b).to_affine(); - let a_affine = a.to_affine(); - let b_affine = b.to_affine(); - - let a_x_c0 = fp_to_biguint(&a_affine.x.c0); - let a_x_c1 = fp_to_biguint(&a_affine.x.c1); - let a_y_c0 = fp_to_biguint(&a_affine.y.c0); - let a_y_c1 = fp_to_biguint(&a_affine.y.c1); - let b_x_c0 = fp_to_biguint(&b_affine.x.c0); - let b_x_c1 = fp_to_biguint(&b_affine.x.c1); - let b_y_c0 = fp_to_biguint(&b_affine.y.c0); - let b_y_c1 = fp_to_biguint(&b_affine.y.c1); - let expected_x_c0 = fp_to_biguint(&expected.x.c0); - let expected_x_c1 = fp_to_biguint(&expected.x.c1); - let expected_y_c0 = fp_to_biguint(&expected.y.c0); - let expected_y_c1 = fp_to_biguint(&expected.y.c1); - - let a_words = [ - biguint_to_words(&a_x_c0), - biguint_to_words(&a_x_c1), - biguint_to_words(&a_y_c0), - biguint_to_words(&a_y_c1), - ] - .concat(); - - let b_words = [ - biguint_to_words(&b_x_c0), - biguint_to_words(&b_x_c1), - biguint_to_words(&b_y_c0), - biguint_to_words(&b_y_c1), - ] - .concat(); - - execute_risc_v_test( - a_words, - b_words, - vec![expected_x_c0, expected_x_c1, expected_y_c0, expected_y_c1].as_slice(), - ); - } - - // #[test] - fn test_bls12381_g2_addition_precompile_elf() { - setup_logger(); - let program = Program::from(BLS12381_G2_ADD_ELF); - run_test(program).unwrap(); - } -} diff --git a/core/src/syscall/precompiles/bls12_381/g2_double.rs b/core/src/syscall/precompiles/bls12_381/g2_double.rs deleted file mode 100644 index f523fcf43..000000000 --- a/core/src/syscall/precompiles/bls12_381/g2_double.rs +++ /dev/null @@ -1,701 +0,0 @@ -use crate::air::{EventLens, MachineAir, WithEvents}; -use crate::bytes::event::ByteRecord; -use crate::memory::{MemoryCols, MemoryWriteCols}; -use crate::operations::field::extensions::quadratic::{QuadFieldOpCols, QuadFieldOperation}; -use crate::operations::field::params::{FieldParameters, Limbs, WORDS_QUAD_EXT_CURVEPOINT}; -use crate::runtime::{ExecutionRecord, MemoryWriteRecord, Syscall, SyscallCode, SyscallContext}; -use crate::stark::SphinxAirBuilder; -use crate::utils::ec::weierstrass::bls12_381::{bls12381_double, Bls12381BaseField}; -use crate::utils::{limbs_from_prev_access, pad_rows}; -use crate::Program; -use core::borrow::{Borrow, BorrowMut}; -use hybrid_array::{typenum::Unsigned, Array}; -use num::{BigUint, Zero}; -use p3_air::{Air, AirBuilder, BaseAir}; -use p3_field::{AbstractField, PrimeField32}; -use p3_matrix::dense::RowMajorMatrix; -use p3_matrix::Matrix; -use serde::Deserialize; -use serde::Serialize; -use sphinx_derive::AlignedBorrow; -use std::mem::size_of; - -/// Chip for doubling a BLS12-381 G2Affine point (P). -/// -/// The algorithm used for computing the doubling inside the circuit doesn't require converting -/// input points to projective representation, however it has following unsupported corner cases: -/// -/// - if P is point on infinity -/// -#[derive(Default)] -pub struct Bls12381G2AffineDoubleChip; - -impl Bls12381G2AffineDoubleChip { - pub fn new() -> Self { - Bls12381G2AffineDoubleChip - } - - fn populate_field_ops( - record: &mut impl ByteRecord, - shard: u32, - channel: u32, - cols: &mut Bls12381G2AffineDoubleCols, - p_x: &[BigUint; 2], - p_y: &[BigUint; 2], - ) { - // This populates necessary field operations to double a point on a Weierstrass curve. - - let a_const = &[BigUint::zero(), BigUint::zero()]; - let b_const = &[BigUint::from(3u32), BigUint::zero()]; - - // slope = slope_numerator / slope_denominator. - let slope = { - // slope_numerator = a + (p.x * p.x) * 3. - let slope_numerator = { - let p_x_squared = cols.p_x_squared.populate( - record, - shard, - channel, - p_x, - p_x, - QuadFieldOperation::Mul, - ); - - let p_x_squared_times_3 = cols.p_x_squared_times_3.populate( - record, - shard, - channel, - &p_x_squared, - b_const, - QuadFieldOperation::Mul, - ); - - cols.slope_numerator.populate( - record, - shard, - channel, - a_const, - &p_x_squared_times_3, - QuadFieldOperation::Add, - ) - }; - - // slope_denominator = 2 * y. - let slope_denominator = cols.slope_denominator.populate( - record, - shard, - channel, - p_y, - p_y, - QuadFieldOperation::Add, - ); - - cols.slope.populate( - record, - shard, - channel, - &slope_numerator, - &slope_denominator, - QuadFieldOperation::Div, - ) - }; - - // x = slope * slope - (p.x + p.x). - let x = { - let slope_squared = cols.slope_squared.populate( - record, - shard, - channel, - &slope, - &slope, - QuadFieldOperation::Mul, - ); - let p_x_plus_p_x = cols.p_x_plus_p_x.populate( - record, - shard, - channel, - p_x, - p_x, - QuadFieldOperation::Add, - ); - cols.x3_ins.populate( - record, - shard, - channel, - &slope_squared, - &p_x_plus_p_x, - QuadFieldOperation::Sub, - ) - }; - - // y = slope * (p.x - x) - p.y. - { - let p_x_minus_x = - cols.p_x_minus_x - .populate(record, shard, channel, p_x, &x, QuadFieldOperation::Sub); - let slope_times_p_x_minus_x = cols.slope_times_p_x_minus_x.populate( - record, - shard, - channel, - &slope, - &p_x_minus_x, - QuadFieldOperation::Mul, - ); - cols.y3_ins.populate( - record, - shard, - channel, - &slope_times_p_x_minus_x, - p_y, - QuadFieldOperation::Sub, - ) - }; - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Bls12381G2AffineDoubleEvent { - pub(crate) lookup_id: usize, - clk: u32, - shard: u32, - channel: u32, - p_ptr: u32, - - #[serde(with = "crate::utils::array_serde::ArraySerde")] - p_memory_records: Array< - MemoryWriteRecord, - WORDS_QUAD_EXT_CURVEPOINT<::NB_LIMBS>, - >, - p_words: Vec, -} - -impl Syscall for Bls12381G2AffineDoubleChip { - fn execute(&self, ctx: &mut SyscallContext<'_, '_>, p_ptr: u32, _unused: u32) -> Option { - let clk = ctx.clk; - let shard = ctx.current_shard(); - let channel = ctx.current_channel(); - let lookup_id = ctx.syscall_lookup_id; - - assert_eq!(p_ptr % 4, 0, "arg1 ptr must be 4-byte aligned"); - - let words_len = - ::NB_LIMBS>>::USIZE; - - let p_words = ctx.slice_unsafe(p_ptr, words_len); - - let p_x_c0 = BigUint::new(p_words[0..12].to_vec()); - let p_x_c1 = BigUint::new(p_words[12..24].to_vec()); - let p_y_c0 = BigUint::new(p_words[24..36].to_vec()); - let p_y_c1 = BigUint::new(p_words[36..48].to_vec()); - - let double = bls12381_double(&[p_x_c0, p_x_c1, p_y_c0, p_y_c1]); - - fn biguint_to_words(input: &BigUint) -> Vec { - let mut result = input.to_u32_digits(); - // single Fp2 element in BLS12381 occupies 12 u32 words - result.resize(12, 0); - result - } - - let double_words = [ - biguint_to_words(&double[0]), - biguint_to_words(&double[1]), - biguint_to_words(&double[2]), - biguint_to_words(&double[3]), - ] - .concat(); - - let p_memory_records: Array< - MemoryWriteRecord, - ::NB_LIMBS, - > = (&ctx.mw_slice(p_ptr, &double_words)[..]) - .try_into() - .unwrap(); - - ctx.record_mut() - .bls12381_g2_double_events - .push(Bls12381G2AffineDoubleEvent { - lookup_id, - clk, - shard, - channel, - p_ptr, - p_memory_records, - p_words, - }); - - None - } -} - -#[derive(Debug, Clone, AlignedBorrow)] -#[repr(C)] -struct Bls12381G2AffineDoubleCols { - pub(crate) clk: T, - pub(crate) shard: T, - pub(crate) channel: T, - pub(crate) nonce: T, - pub(crate) is_real: T, - - pub(crate) p_ptr: T, - pub(crate) p_access: - Array, ::NB_LIMBS>, - - pub(crate) slope_denominator: QuadFieldOpCols, - pub(crate) slope_numerator: QuadFieldOpCols, - pub(crate) slope: QuadFieldOpCols, - pub(crate) p_x_squared: QuadFieldOpCols, - pub(crate) p_x_squared_times_3: QuadFieldOpCols, - pub(crate) slope_squared: QuadFieldOpCols, - pub(crate) p_x_plus_p_x: QuadFieldOpCols, - pub(crate) x3_ins: QuadFieldOpCols, - pub(crate) p_x_minus_x: QuadFieldOpCols, - pub(crate) y3_ins: QuadFieldOpCols, - pub(crate) slope_times_p_x_minus_x: QuadFieldOpCols, -} - -impl BaseAir for Bls12381G2AffineDoubleChip { - fn width(&self) -> usize { - size_of::>() - } -} - -impl<'a> WithEvents<'a> for Bls12381G2AffineDoubleChip { - type Events = &'a [Bls12381G2AffineDoubleEvent]; -} - -impl MachineAir for Bls12381G2AffineDoubleChip { - type Record = ExecutionRecord; - type Program = Program; - - fn name(&self) -> String { - "Bls12381G2AffineDoubleChip".to_string() - } - - fn generate_trace>( - &self, - input: &EL, - output: &mut Self::Record, - ) -> RowMajorMatrix { - let mut rows: Vec> = vec![]; - - let width = >::width(self); - - let mut new_byte_lookup_events = Vec::new(); - - for event in input.events() { - let mut row = vec![F::zero(); width]; - let cols: &mut Bls12381G2AffineDoubleCols = - row.as_mut_slice().borrow_mut(); - - cols.clk = F::from_canonical_u32(event.clk); - cols.is_real = F::one(); - cols.shard = F::from_canonical_u32(event.shard); - cols.channel = F::from_canonical_u32(event.channel); - cols.p_ptr = F::from_canonical_u32(event.p_ptr); - - for index in 0..::NB_LIMBS::USIZE { - cols.p_access[index].populate( - event.channel, - event.p_memory_records[index], - &mut new_byte_lookup_events, - ); - } - - let p = &event.p_words; - let p_x_c0 = BigUint::new(p[0..12].to_vec()); - let p_x_c1 = BigUint::new(p[12..24].to_vec()); - let p_y_c0 = BigUint::new(p[24..36].to_vec()); - let p_y_c1 = BigUint::new(p[36..48].to_vec()); - - Self::populate_field_ops( - &mut new_byte_lookup_events, - event.shard, - event.channel, - cols, - &[p_x_c0, p_x_c1], - &[p_y_c0, p_y_c1], - ); - - rows.push(row); - } - - output.add_byte_lookup_events(new_byte_lookup_events); - - pad_rows(&mut rows, || { - let mut row = vec![F::zero(); width]; - let cols: &mut Bls12381G2AffineDoubleCols = - row.as_mut_slice().borrow_mut(); - - cols.clk = F::zero(); - cols.is_real = F::zero(); - cols.shard = F::zero(); - cols.channel = F::zero(); - cols.p_ptr = F::zero(); - - let zero = BigUint::zero(); - Self::populate_field_ops( - &mut vec![], - 0, - 0, - cols, - &[zero.clone(), zero.clone()], - &[zero.clone(), zero.clone()], - ); - - row - }); - - let mut trace = - RowMajorMatrix::::new(rows.into_iter().flatten().collect::>(), width); - - // Write the nonces to the trace. - for i in 0..trace.height() { - let cols: &mut Bls12381G2AffineDoubleCols = - trace.values[i * width..(i + 1) * width].borrow_mut(); - cols.nonce = F::from_canonical_usize(i); - } - - trace - } - - fn included(&self, shard: &Self::Record) -> bool { - !shard.bls12381_g2_double_events.is_empty() - } -} - -impl Air for Bls12381G2AffineDoubleChip -where - AB::F: PrimeField32, -{ - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let local = main.row_slice(0); - let local: &Bls12381G2AffineDoubleCols = (*local).borrow(); - let next = main.row_slice(1); - let next: &Bls12381G2AffineDoubleCols = (*next).borrow(); - - // Constrain the incrementing nonce. - builder.when_first_row().assert_zero(local.nonce); - builder - .when_transition() - .assert_eq(local.nonce + AB::Expr::one(), next.nonce); - - let a_const_limbs = &[ - Bls12381BaseField::to_limbs_field::(&BigUint::zero()), - Bls12381BaseField::to_limbs_field::(&BigUint::zero()), - ]; - - let three_b_const_limbs = &[ - Bls12381BaseField::to_limbs_field::(&BigUint::from(3u32)), - Bls12381BaseField::to_limbs_field::(&BigUint::zero()), - ]; - - let p_x_c0: Limbs<_, ::NB_LIMBS> = - limbs_from_prev_access(&local.p_access[0..12]); - let p_x_c1: Limbs<_, ::NB_LIMBS> = - limbs_from_prev_access(&local.p_access[12..24]); - let p_x = [p_x_c0, p_x_c1]; - - let p_y_c0: Limbs<_, ::NB_LIMBS> = - limbs_from_prev_access(&local.p_access[24..36]); - let p_y_c1: Limbs<_, ::NB_LIMBS> = - limbs_from_prev_access(&local.p_access[36..48]); - let p_y = [p_y_c0, p_y_c1]; - - // slope = slope_numerator / slope_denominator. - let slope = { - // slope_numerator = a + (p.x * p.x) * 3*b. - { - local.p_x_squared.eval( - builder, - &p_x, - &p_x, - QuadFieldOperation::Mul, - local.shard, - local.channel, - local.is_real, - ); - - local.p_x_squared_times_3.eval( - builder, - &local.p_x_squared.result, - three_b_const_limbs, - QuadFieldOperation::Mul, - local.shard, - local.channel, - local.is_real, - ); - - local.slope_numerator.eval( - builder, - a_const_limbs, - &local.p_x_squared_times_3.result, - QuadFieldOperation::Add, - local.shard, - local.channel, - local.is_real, - ); - }; - - // slope_denominator = 2 * y. - local.slope_denominator.eval( - builder, - &p_y, - &p_y, - QuadFieldOperation::Add, - local.shard, - local.channel, - local.is_real, - ); - - local.slope.eval( - builder, - &local.slope_numerator.result, - &local.slope_denominator.result, - QuadFieldOperation::Div, - local.shard, - local.channel, - local.is_real, - ); - - local.slope.result - }; - - // x = slope * slope - (p.x + p.x). - let x = { - local.slope_squared.eval( - builder, - &slope, - &slope, - QuadFieldOperation::Mul, - local.shard, - local.channel, - local.is_real, - ); - local.p_x_plus_p_x.eval( - builder, - &p_x, - &p_x, - QuadFieldOperation::Add, - local.shard, - local.channel, - local.is_real, - ); - local.x3_ins.eval( - builder, - &local.slope_squared.result, - &local.p_x_plus_p_x.result, - QuadFieldOperation::Sub, - local.shard, - local.channel, - local.is_real, - ); - local.x3_ins.result - }; - - // y = slope * (p.x - x) - p.y. - { - local.p_x_minus_x.eval( - builder, - &p_x, - &x, - QuadFieldOperation::Sub, - local.shard, - local.channel, - local.is_real, - ); - local.slope_times_p_x_minus_x.eval( - builder, - &slope, - &local.p_x_minus_x.result, - QuadFieldOperation::Mul, - local.shard, - local.channel, - local.is_real, - ); - local.y3_ins.eval( - builder, - &local.slope_times_p_x_minus_x.result, - &p_y, - QuadFieldOperation::Sub, - local.shard, - local.channel, - local.is_real, - ); - } - - // Constraint self.p_access.value = [self.x3_ins.result, self.y3_ins.result]. This is to - // ensure that p_access is updated with the new value. - let x3_ins_x = &local.x3_ins.result[0]; - let x3_ins_y = &local.x3_ins.result[1]; - let y3_ins_x = &local.y3_ins.result[0]; - let y3_ins_y = &local.y3_ins.result[1]; - for i in 0..::NB_LIMBS::USIZE { - builder - .when(local.is_real) - .assert_eq(x3_ins_x[i], local.p_access[i / 4].value()[i % 4]); - builder - .when(local.is_real) - .assert_eq(x3_ins_y[i], local.p_access[12 + i / 4].value()[i % 4]); - builder - .when(local.is_real) - .assert_eq(y3_ins_x[i], local.p_access[24 + i / 4].value()[i % 4]); - builder - .when(local.is_real) - .assert_eq(y3_ins_y[i], local.p_access[36 + i / 4].value()[i % 4]); - } - - for index in 0..::NB_LIMBS::USIZE { - builder.eval_memory_access( - local.shard, - local.channel, - local.clk, - local.p_ptr.into() + AB::F::from_canonical_u32((index as u32) * 4), - &local.p_access[index], - local.is_real, - ); - } - - builder.receive_syscall( - local.shard, - local.channel, - local.clk, - local.nonce, - AB::F::from_canonical_u32(SyscallCode::BLS12381_G2_DOUBLE.syscall_id()), - local.p_ptr, - AB::Expr::zero(), - local.is_real, - ) - } -} - -#[allow(unused)] // Disabled for recursion performance -#[cfg(test)] -mod tests { - use crate::runtime::{Instruction, Opcode, SyscallCode}; - use crate::utils::ec::weierstrass::bls12_381::fp_to_biguint; - use crate::utils::tests::BLS12381_G2_DOUBLE_ELF; - use crate::utils::{run_test, run_test_with_memory_inspection, setup_logger}; - use crate::Program; - use bls12_381::G2Projective; - use elliptic_curve::group::Curve; - use elliptic_curve::Group; - use num::{BigUint, Num}; - use rand::rngs::OsRng; - - fn biguint_to_words(input: &BigUint) -> Vec { - let mut result = input.to_u32_digits(); - result.resize(12, 0); - result - } - - fn risc_v_program(p_ptr: u32, p_words: Vec) -> Program { - let mut instructions = vec![]; - for (index, word) in p_words.into_iter().enumerate() { - instructions.push(Instruction::new(Opcode::ADD, 29, 0, word, false, true)); - instructions.push(Instruction::new( - Opcode::ADD, - 30, - 0, - p_ptr + (index * 4) as u32, - false, - true, - )); - instructions.push(Instruction::new(Opcode::SW, 29, 30, 0, false, true)); - } - - instructions.push(Instruction::new( - Opcode::ADD, - 5, - 0, - SyscallCode::BLS12381_G2_DOUBLE as u32, - false, - true, - )); - instructions.push(Instruction::new(Opcode::ADD, 10, 0, p_ptr, false, true)); - instructions.push(Instruction::new(Opcode::ADD, 11, 0, 0, false, true)); - instructions.push(Instruction::new(Opcode::ECALL, 5, 10, 11, false, false)); - Program::new(instructions, 0, 0) - } - - fn execute_test(p_ptr: u32, p_value: &[BigUint; 4], expected: &[BigUint; 4]) { - let words = [ - biguint_to_words(&p_value[0]), - biguint_to_words(&p_value[1]), - biguint_to_words(&p_value[2]), - biguint_to_words(&p_value[3]), - ] - .concat(); - - setup_logger(); - let program = risc_v_program(p_ptr, words); - let (_, memory) = run_test_with_memory_inspection(program); - - let mut result = vec![]; - // Fp / BigUint is encoded as a 12 u32 words. G2Affine point has 4 Fp elements, so we read 4 * 12 words from the memory - for i in 0..48 { - result.push(memory.get(&(p_ptr + i * 4)).unwrap().value); - } - - let computed_x_c0 = BigUint::new(result[0..12].to_vec()); - let computed_x_c1 = BigUint::new(result[12..24].to_vec()); - let computed_y_c0 = BigUint::new(result[24..36].to_vec()); - let computed_y_c1 = BigUint::new(result[36..48].to_vec()); - - assert_eq!(computed_x_c0, expected[0]); - assert_eq!(computed_x_c1, expected[1]); - assert_eq!(computed_y_c0, expected[2]); - assert_eq!(computed_y_c1, expected[3]); - } - - // #[test] - fn test_bls12381_g2_double_precompile() { - let p_ptr = 100u32; - let p = [ - BigUint::from_str_radix("00f0310692ee572076c940e7e486c4b3bcfa12d3aa83ce88ca53aca3d83cc388d10f7ab3dd58bc38b0dfc421a0741012", 16).unwrap(), - BigUint::from_str_radix("05ba6e8828a638bdde01da90912664d74f4b97c526016a6c5ad517b717f0a76787c1576ce5748d7ebd5a052f7435ee9d", 16).unwrap(), - BigUint::from_str_radix("18415e0e74a390dbaf7b1fe2408e0ff1d3a5ebf89d40ce374cfe625ad910da372670e13c4d1bf848cd261c9a17e20c5f", 16).unwrap(), - BigUint::from_str_radix("149bc92f3dd4a1ea9da08fad7e8f97202f09b474eaa91624b27adcf9462f2ab2b6cc6c48b117241e9edb8fe8ed282de7", 16).unwrap(), - ]; - let expected = [ - BigUint::from_str_radix("13e17a4d65e7935687da36118a193207e264f8c504a753da48236962d823902be0f2d3d8b1163b6b236a99b363074598", 16).unwrap(), - BigUint::from_str_radix("af9ffb2d83cfd4c3d8448e5c313e494964f5ddc0165943488cd838a76175ed001fbd539bfd1162f5afbac5ca483faf1", 16).unwrap(), - BigUint::from_str_radix("d62922919c99baf757b6d92cb4d5fe8595a93d79e5d7dfc39af35b2efd906faefb86df5cc5226a2d49b47934ef96070", 16).unwrap(), - BigUint::from_str_radix("2d3a00fba534c8fe37bf850470209cf210f8502685536888e85cc8ad00bd64a29f03bff2a8c5952a87ac30f2ba8f6fa", 16).unwrap(), - ]; - execute_test(p_ptr, &p, &expected); - } - - // #[test] - fn test_bls12381_g2_double_precompile_randomized_input() { - let mut rng = OsRng; - let p = G2Projective::random(&mut rng); - let double_affine = p.double().to_affine(); - let p_affine = p.to_affine(); - - let p_ptr = 100u32; - - let p = [ - fp_to_biguint(&p_affine.x.c0), - fp_to_biguint(&p_affine.x.c1), - fp_to_biguint(&p_affine.y.c0), - fp_to_biguint(&p_affine.y.c1), - ]; - - let expected = [ - fp_to_biguint(&double_affine.x.c0), - fp_to_biguint(&double_affine.x.c1), - fp_to_biguint(&double_affine.y.c0), - fp_to_biguint(&double_affine.y.c1), - ]; - execute_test(p_ptr, &p, &expected); - } - - // #[test] - fn test_bls12381_g2_double_precompile_elf() { - setup_logger(); - let program = Program::from(BLS12381_G2_DOUBLE_ELF); - run_test(program).unwrap(); - } -} diff --git a/core/src/syscall/precompiles/bls12_381/mod.rs b/core/src/syscall/precompiles/bls12_381/mod.rs index 182a5fe11..e440dcb9e 100644 --- a/core/src/syscall/precompiles/bls12_381/mod.rs +++ b/core/src/syscall/precompiles/bls12_381/mod.rs @@ -1,6 +1,4 @@ pub mod g1_decompress; -pub mod g2_add; -pub mod g2_double; use super::{ field::{create_fp_event, FieldAddSyscall, FieldMulSyscall, FieldSubSyscall}, diff --git a/core/src/utils/ec/weierstrass/bls12_381.rs b/core/src/utils/ec/weierstrass/bls12_381.rs index 8adbe0b34..b4bc80579 100644 --- a/core/src/utils/ec/weierstrass/bls12_381.rs +++ b/core/src/utils/ec/weierstrass/bls12_381.rs @@ -80,6 +80,7 @@ pub fn bls12381_fp2_sqrt(a: &[BigUint; 2]) -> [BigUint; 2] { [fp_to_biguint(&a_sqrt.c0), fp_to_biguint(&a_sqrt.c1)] } +#[deprecated(since = "1.0.0-rc.1", note = "material for G2 precompiles removed")] pub fn bls12381_g2_add(a: &[BigUint; 4], b: &[BigUint; 4]) -> [BigUint; 4] { fn is_identity(input: &[BigUint; 4]) -> Choice { if input[0] == BigUint::zero() diff --git a/core/src/utils/programs.rs b/core/src/utils/programs.rs index f5fa7d4d5..9984c2960 100644 --- a/core/src/utils/programs.rs +++ b/core/src/utils/programs.rs @@ -124,12 +124,6 @@ pub mod tests { pub const VERIFY_PROOF_ELF: &[u8] = include_bytes!("../../../tests/verify-proof/elf/riscv32im-succinct-zkvm-elf"); - pub const BLS12381_G2_ADD_ELF: &[u8] = - include_bytes!("../../../tests/bls12381-g2-add/elf/riscv32im-succinct-zkvm-elf"); - - pub const BLS12381_G2_DOUBLE_ELF: &[u8] = - include_bytes!("../../../tests/bls12381-g2-double/elf/riscv32im-succinct-zkvm-elf"); - pub const BLAKE2S_ROUND_ELF: &[u8] = include_bytes!("../../../tests/blake2s-round/elf/riscv32im-succinct-zkvm-elf"); diff --git a/examples/bls12381-pairing/program/Cargo.toml b/examples/bls12381-pairing/program/Cargo.toml index d9821af57..e1da1c8e4 100644 --- a/examples/bls12381-pairing/program/Cargo.toml +++ b/examples/bls12381-pairing/program/Cargo.toml @@ -6,5 +6,4 @@ edition = "2021" [dependencies] sphinx-zkvm = { path = "../../../zkvm/entrypoint" } - bls12_381 = { git = "https://github.com/argumentcomputer/bls12_381", branch = "zkvm" } diff --git a/prover/src/lib.rs b/prover/src/lib.rs index 3f4536867..250fdde5c 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -699,7 +699,6 @@ impl Default for SphinxProver { #[cfg(test)] mod tests { - use std::env; use std::fs::File; use std::io::{Read, Write}; @@ -902,114 +901,4 @@ mod tests { Ok(()) } - - #[test] - #[ignore] // ignore for recursion performance reasons - fn test_deferred_proving_with_bls12381_g2_precompiles() { - fn test_inner( - program_elf: &[u8], - deferred_proofs_num: usize, - program_inputs: Vec<&SphinxStdin>, - ) { - assert_eq!(deferred_proofs_num, program_inputs.len()); - - setup_logger(); - env::set_var("RECONSTRUCT_COMMITMENTS", "false"); - env::set_var("FRI_QUERIES", "1"); - - // verify program which verifies proofs of a vkey and a list of committed inputs - let verify_elf = - include_bytes!("../../tests/verify-proof/elf/riscv32im-succinct-zkvm-elf"); - - tracing::info!("initializing prover"); - let prover = SphinxProver::new(); - let opts = SphinxProverOpts::default(); - - tracing::info!("setup elf"); - let (program_pk, program_vk) = prover.setup(program_elf); - let (verify_pk, verify_vk) = prover.setup(verify_elf); - - // Generate deferred proofs - let mut public_values = vec![]; - let mut deferred_compress_proofs = vec![]; - program_inputs - .into_iter() - .enumerate() - .for_each(|(index, input)| { - tracing::info!("prove subproof {}", index); - let deferred_proof = prover - .prove_core(&program_pk, input, opts, Default::default()) - .unwrap(); - let pv = deferred_proof.public_values.to_vec(); - public_values.push(pv); - let deferred_compress = prover - .compress(&program_vk, deferred_proof, vec![], opts) - .unwrap(); - deferred_compress_proofs.push(deferred_compress.proof); - }); - - // Aggregate deferred proofs - let mut stdin = SphinxStdin::new(); - let vkey_digest = program_vk.hash_babybear(); - let vkey_digest: [u32; 8] = vkey_digest - .iter() - .map(|n| n.as_canonical_u32()) - .collect::>() - .try_into() - .unwrap(); - stdin.write(&vkey_digest); - stdin.write(&public_values); - for drp in deferred_compress_proofs.iter() { - stdin.write_proof(drp.clone(), program_vk.vk.clone()); - } - - // Generate aggregated proof - let verify_proof = prover - .prove_core(&verify_pk, &stdin, opts, Default::default()) - .unwrap(); - let verify_compress = prover - .compress( - &verify_vk, - verify_proof.clone(), - deferred_compress_proofs, - opts, - ) - .unwrap(); - - let compress_pv: &RecursionPublicValues<_> = - verify_compress.proof.public_values.as_slice().borrow(); - println!("deferred_hash: {:?}", compress_pv.deferred_proofs_digest); - println!("complete: {:?}", compress_pv.is_complete); - - tracing::info!("verify verify program"); - prover - .verify_compressed(&verify_compress, &verify_vk) - .unwrap(); - } - - // Programs that we will use to produce deferred proofs while testing - let bls12381_g2_add_elf = - include_bytes!("../../tests/bls12381-g2-add/elf/riscv32im-succinct-zkvm-elf"); - let bls12381_g2_double_elf = - include_bytes!("../../tests/bls12381-g2-double/elf/riscv32im-succinct-zkvm-elf"); - - test_inner( - bls12381_g2_add_elf, - 3, - vec![ - &SphinxStdin::new(), - &SphinxStdin::new(), - &SphinxStdin::new(), - ], - ); - test_inner( - bls12381_g2_double_elf, - 3, - vec![ - &SphinxStdin::new(), - &SphinxStdin::new(), - &SphinxStdin::new(), - ], - ); - } } diff --git a/tests/bls12381-g2-add/Cargo.lock b/tests/bls12381-g2-add/Cargo.lock deleted file mode 100644 index 888e033a1..000000000 --- a/tests/bls12381-g2-add/Cargo.lock +++ /dev/null @@ -1,271 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "anyhow" -version = "1.0.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bls12381-g2-add-test" -version = "0.1.0" -dependencies = [ - "sphinx-zkvm", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cpufeatures" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" -dependencies = [ - "libc", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "hybrid-array" -version = "0.2.0-rc.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53668f5da5a41d9eaf4bf7064be46d1ebe6a4e1ceed817f387587b18f2b51047" -dependencies = [ - "typenum", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "libc" -version = "0.2.154" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" - -[[package]] -name = "libm" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" - -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro2" -version = "1.0.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "serde" -version = "1.0.210" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.210" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sphinx-lib" -version = "1.0.0" -dependencies = [ - "anyhow", - "bincode", - "cfg-if", - "getrandom", - "hybrid-array", - "serde", -] - -[[package]] -name = "sphinx-zkvm" -version = "1.0.0" -dependencies = [ - "bincode", - "cfg-if", - "getrandom", - "lazy_static", - "libm", - "once_cell", - "rand", - "serde", - "sha2", - "sphinx-lib", -] - -[[package]] -name = "syn" -version = "2.0.63" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/tests/bls12381-g2-add/Cargo.toml b/tests/bls12381-g2-add/Cargo.toml deleted file mode 100644 index 71d73208d..000000000 --- a/tests/bls12381-g2-add/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[workspace] -[package] -version = "0.1.0" -name = "bls12381-g2-add-test" -edition = "2021" - -[dependencies] -sphinx-zkvm = { path = "../../zkvm/entrypoint" } diff --git a/tests/bls12381-g2-add/elf/riscv32im-succinct-zkvm-elf b/tests/bls12381-g2-add/elf/riscv32im-succinct-zkvm-elf deleted file mode 100755 index 05f040a666bc32e58ad50516497c8bb5e1389fec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52308 zcmeFadw3Pqz3{(g&z`+^cCIjxgn$qk5+Yi}ju>rEYf`z0o}+jW5N$!-T-3HADtM{Y z#LW)i19`pYPf`5b5Qd=Y5{v zKfkAWp8c#@Gi!a+fF__!kBKMS*`&;9nH@|1kw@!)SL(yC|n$wX%bT5ihP&rd;C`6ksT)sJMy+Lczaj zH_TGz7E8sRX$g3V?#d1wZMUuNs<~UJ&Bl;-hBKu~DY+-5n`o&x^I0q!%5l^><%epO z9Ui2TPpof?-Pn?ojqi%=qP|3O6pV*R>dgZL-H()~>ungxs?oU1% za%#D^hTn_)e$KD&hBl`kzf#&A8>YO2vkW(Mw8D<4fKx`FAB9Z6<|ud{GGvTXaL<*yZvZi4_d}*TDyAT zV(zyVdEq=2d#bf>ynk3ZRY#TAI7=m~z-v#~@Ddj(r#i$qP3>MN4BeDrt{K+u?Ve?L zg|k)s`I>hB;Bni$1YKI0!^UYgM*t0HbpQ=<3FC=rdl;Gd|l=X#YDI@ly^#RX1qy$&9 zY`BV-%~J8cTNwW+{Jn7+elFKfbFYiejaOL^0W z361jQxjge~hH-zm0*~%8mY2xaw9)j|bSUnigMtpm8FWz4!8n5s3OX2P&_O{5;~&!@ zA39V;I@ZPB1D=Uem8_EWXi2X}UK$V9sxWc}9ud|fa0VU$)`NCmTaO6q5&kE5l(IIg z!}O35d&-JvT0UdtX`0Qp0&xqPh6|J#=?{(8gWpjVx5`s|0}Z`d!&I_bXbK&tm!ca4 zR=$cYumZyG9sQ-R9qyl=qpVG;UGrS)q_mOeo!kGt@#2hUM#k##;?@-9JbY9o9}XGu z@IA_juV<`TJYT4iac~T;R?bZDoyl`+gK`pbZwt?Nt7Jmj9#jr8oP;mq;bY2~CHRR< zgpA~@4*Y6V^JCVh9dr19!AbL9qDJGCn)5K#yukWs>Y)jrEPM}rP;FdR@G`p6sJ_WO z+fWPdQ0ib+#o6fIW9aCJjZTQKsyFPTmJ#a~3V5wnfOQI)L!qCQhkl8O-i^>cqres2 zy=jhq76xXhF5+3s;q!`sS2M%1!|3bo3Bw*;ZzOY@+C-Pwp|FZ&SwU|`t7>jre-b%0 zryg4N)jxn?{zDj1@HqnpG97UftrfABRe|#QV#cz{1ZOYAwG~kdiQ5iVsp~rMm-XKu z?P{$4(syC8Jg@qYxt(wZb_qJ?RR^5`PX?W`vn{9e0?X-FX*qqbv7F+YEN9>}ljkPS zO?xJG>cxa%o&)V(7;41NeN)N!o@M3RA;ynFw?qc8GnE%aKlZYs=DC{MZ#Y=}Mv;RzV)Mn+B-efw(D*{gBWo$|vHfe`)&U+7Ab(rxDY|x4n%`7Vn9P}b~ z4ZKn~Rqc!ASl#T59?H%6>Mw=9*=N<-Q$l41v2LbFef2+Ae)!!OSeX zwVR%MbH3FUFWGFEB|{9ebf)1Q9g6IR0`bxY)(e_xI$>+NS|`6wmx`vThxVUHhOaT0 z2lz6Fe$#*xG|XW^`Ujco5av1)T`u!wo_fA@%$KZX?}f<72BH& ztuNvw)d91lG7zt=PvK)lS(A{OdZ^D48(xadW2};y8h>fCLDo6ZU9YpWgSRYv8!Ha4 znCBQ){G7U=IiNZiA5a-I&ozSabL&hKzKNe#X~JhAt%Ev^nS$3C5?Z9jov>fp(6-$U zhx8gVw={nxKGtgy9{P!G;ZKF?w4O`xvAU2xyOHTKr(boM)Az|Tr}*r1oPihgch0Nq z?+m!6zf*Qof2VX>f1dZ}d4K5D-x>IFi5IELbOK#i|LvK{zzX;yFW{KE%u4FAD$5D3 zfUm2vZ0klV$u+)L#IY#D71>GaJ?e*YoRHjKo0*g{cWtI^jZ{vUe!-F4uZkwQrawGM z8Nw%vfPY|P)XBI8K3c1i8I^_?z9Z~J4~z%P^DH zih!M!m*Iq}X?rN*gr!e$rW28JkeM_L%Z}U#U8Gdm+_#ea?8rPTnW682gAt}rl=~*_ zxL$5L8B%TxI8iB8$jLD*M;T!!Q$JUUOgpDI%gL0o0vbxWJm_T5FXIB! z$($B+BGV#{nih4;X<42*FDn_D7ILgH?Fs}kR#))vW@qN*CKYx(`(23xY zrQ2rGk9+fi&_0;7Rz#hg75TQMtYlyXy!JBJ8?%$0+ghIOSSx~F zWO=q1SdPqI!#x#n!t!h_^OtgD$cfS>M@5~i9hT^kn}g`HgP2aA#8Cxc02M~HjM z39Ja&!77zBt3#f;F@k(?e^3VFgp%QDVcROsNM_I`w>ZPL=7B%g)^_An$~y$_kR7=r zDs}<*AunTj)KM2?c%ii!NwovtZ+os|ybSMN6Y)atVl#F`?C`tDOc&bRh|So6F2r{X zjm%~pBTmLNt~a9R<=(t7>qU9Uf`-T^YnFXMh8@adT)Ds9U`^yX`V*cp--{-L?`3+{ zA!P35s1r809vMl7xsII8^}FcHm!qDwHY=Gb(E-%6*8-#1OlAWk*T_ufzQ-JPWP4Uc zR#Ls3W#`^VKH$CVBtI{AkmUppXD0*ld^z~@++2%`my@E82kr%sk@>Tci7t7LaSbxM1KoFH zmKPm~?YpLnV|M8RUmJ{<=U6Z2d75A zp|!!fqSpqY3+S6QtxGcN$u3Uzll0lnn7|0-WhE(-W*6HDiayy6Pa=1r6|_?ttwY%P6ikkXPg3o<;C^;wah3{>aa;Xy{ylzYp=PEceWo~3jwBi{*4%ZD$5$n`p(VMuJhkVdBlQFXD3X<8C1=#d# z)~&$H9vO0SUoLRelZ9S*WH6}?7uwP7%)7FOoe7T^H}xpT$M*tOp>0(v&#KJ2i&z7^ zqR@kTRk`3k}mh)aAvc&U2 zK}W49N*Z-V(4q&wo;LF5m@A5)FT5iqG8)o#@hKT=MUj(pO^;;c8rseau!e=`Hrl~A zx~?dg9L?QevKrQb%;eUyVTfF)%ZoO&hIduPGZ zy?72kub><$8p=zFa$5b@Z#q0{33v$g}NXCtFIb+l%NYxMjmX(x1E|{q%JeHj6pr%Kf$IBPomV zBcY92(HolJ*Kuvgb#VqXrK~~+NcparG^NZ#mrGd@gvN5;qJQtCb-1?^evKW%hHqvU&=%9H+=(gy%9e^%H;;Ii=cfmI7=P>k2d;xqs*Ioc_HXZ`ECYyQr;-@ z?ggw~^ev|3-bjl%QO*l9C(7m66e;uY`@kndKf}M065T=RWbEjX%-+!hz17pn+yTCW zuuU32Swq?#Lf)kuna%ix%$I9)hVWmuTrbD3V}14YNa3sA%$@u5gcilD9oJR(dSn%-Y)T#JkR7g<&7cmECMI4v3o%&6)^$0Sd0trK___@ zgicb1xlg%02RikFw~E1A?&A~bGMD)jLND&E#m<7GzJ51Azar?y{R;FSW9sWW#KsHl zxL=I_&UpIzkOdvNw=o2NP!1B@gl1ecHt>_0Wi7#lcuZwe;PjBFHzY0CJqMs9}E>2q2&{^xTLL2+I@ZI+I zgH`JOSiTj~IS8x%Q}P-8h->>f0}T3BkPo0 z%rTvlsW@RLIzl;-Cb2$w4`R}wVR=Dv31z{P z&@bTh-Ap-1Inm80&7TDgXF!nr7r%ZhPdcTqoQwrByT)G}V#FSc7Md@TS9)P4xu_H5 z_C_mvG`|e?6QWlrZ}Iiyd@>C$vzWY4o-#|LDwab|q!hR%Gf&1#-fWN8Ht4)YEYB)5 zOEXn`*zd{PJW4M8(RQ=s_w9m<6Ww*vDSQ89>=AO0i^(5$AIzT43$!6uMBd$FpNGEf z8qf1qo`~hmFv&TYW=SeXnR@oh2}hpg%rN35SGLFUYD{~6CphGTnLBy5jlh*_@GZGg zof@ljD0Pc`(dH9Q@hc}iYsgE4bd*-7_-Pntqr(YOO!1+#R&U}tZ(e$jDR2d7JE%m^6~Uv9X{!8 zArH+S1AA<$Ie&MXn^i+D(qe5x0r(*W_c8L7(d_|&)!Zv|!j1~h(I!N$@;u<*xy1BZ z6YQs1k@!$6ApGM$_E=@?-`D5Bw|USu&pcOh!SJs|-Zzm6U83Z%qU=MR%B!ADj##e+ z`xtp<^7mZRmb|N7gPaw!4#+F(VzMqK>k?pH0<24bbqTO82J2#=pA7Vqfqs&;2~Oek z5j<@;{e*9oY5s>r`xGut**f5plg@^>)w;kx#%*JPp+`(rOHTV8^7$%1z2@y+ur!+sonk^NPBEGs)G z@KSTmIt5R7^Z$A7!kk5W5ipZmsh{F21HLlgEA}kln{(Lk6vJQts9j1Ae1**kIQ@|4 zzQ}X&*~l-r!jIS-L#Ma&T6UXN~>aWvNkPtFPiDRVhX5HyF|Dn8s% z_C3c;@7OWZyxdiGev@I3VXv=Xi}DsuQSQNm%Dd;d=}tMO+{s&1IqMkcQv%THT?`XG`Yf?aS`Vm=&u~|+t`N)JXIDsX zrm{_y<;{lv@5huEQCYTErpSG%=9aN1yt~Xxee+&r=R}lwevb04Zg2PYKcwslQ&s$& ztCaiMXNLRGXG-APZ++QrPk2bh&!4Njy|*fN>ZSUbx0OD7?^E9XsmlNCOXOmk^4mXC zZf?7w@k{sf_Di33-N()WXK-)MfABbate<-UrM%;B2qQFkeC4>Y+2x*q?4d2VUDWxUS=nK ztEQW0!t2nt;=6b=qv*|R5YFNgd-4Sb5SZ4$@|3n{(bP zz+8DU6$2!)i2=S!jaM=C|06uU{AYN`_+n2|IH)P$@Zau zOThgEaVYz`EAh{R_&GCagU>&Z`=Q`pWh~iCPQ_(a1Bsiurf55~M0_>Dp#z^-A->nS zkKo&7>iE&KQhV-SYm;x~5FZ$VgISV^jg#{CGCt*_*l8)RB+k^&^?dBm%#&ZoLzTvq z6~$kQnk82tL-_fn_3%XSgx>c?Uf35u5ibdzC@+2VRXdcw!5vz!%7@jCwL>)pem!>b zz$&3#&@9KLk$7lZu0M&I9t_ps$G$#1)h#K1Qa4@eno z?<8ZVd<*d(q}?h0I4w(MvJRqOWL=zoB1^$`;{_0x~RJzuEoFA)FvCr!Rv785T z#TL1tMr95oM$4+r^lI`sH`pjK0{aZa!kn9o^{&;inOBo&H~SCys~f(@@Wb_->kDHu zw{U(VqWm|e82;?*3~vkm-m&A#zx)jIU5Blhwcmp`jt_6R=u^t>ZnjXHFpO}G7 zOCByLUNX8cn>Kat`yN4=of@W;OE*z_Ow`8&3=ya9O+g~Iww8g}Jxas)@( z+p)R3nDeQ*BKJD3CMVRkf&QGYyxi%xi(HxH&XDn<&FG9FZ3{zJH@LoB^+B zy>h0Wu&oM_J@iD7=QBImx{Bk#`I5LML|nsusS!r^90$&EY|wFM;I88$pK`VnJCp+q zNhg9C|M)Hx& zQ|7OAetlCKkCd;pN^nbv&6jn+?xVLm=yXcYTiv-2?$B9$firz38PBTU4PI$Iul{%@ zFNj>*V~2{L+}{q}5r|DZ7M0QtPi)^%uwjQcYvJ_r4-|fRUy1H5wJah^8cwN|57Q}8_6`?I#FWEE#ys~A7s-wWRn;C_Ynv(PtU6HA_YRUQ5R@lS9Ys^vOALm1 zvlM?{Vg~W?Q{#TcxL=hwaAqX5pi29vEmuu}&u=;9pXTXtX*ct$^4eYp?a-LZQ-180 zJGm}A)lQE>e@~HmAp3imf{%?t?y>r)2FHR_L>|Be}O9U0#eXFNS8t){@htQmz_TuU#cFTzAmqP@FU+tPoc+GJA zBiLigE4S*_*;eas>6cf2VBLqdHTu`iz!f>=^|2ly`lpZoEyv|kH++F#Jmwhr6?e6F)b+t%Pe0za#K((mpB z&Timmm9N`ZMH_*iRc`(9S=*X^8}PHrTc53?O#$$;%BMg3J}{R6KdXG`?>@tKychUc z<*Vv)x&9;Yxz1v&`d+|~mJj~$8_cT#_|fvj-B;Mwsw;pWEnm0x`?Pru_|fwDU(VqE zxVzLT{WjXmbN^)+O% z`y;>)l}{=H|8*0AA1a@JZuSl;^74%?bv4g6rawWx`)ZU%m^ zd~nVez`PFl!SYqN<|pS$iRUe-HdX zdG}uM&*0w!KTy7I&sETQCGZ2~jZ1D|T~-1=P(FR&Ewn-ZaGiMKZss)*_<{0CtL~-E zMZo9&8#BSJ8u)>7Ys+JdTMT?vZoM`Sn8Y1iPanCG`ECQgioe9(&FW#w{AG>2d!ypN z49Pno@-7HD=pFcf4MzE@FMr3nTnY}vCev8Qk|)3c+sxj2=}oHdw%n~&Ygr~hQ$#VM}qm1;Mi951l?P3x9|g`X__ zhS27`>aNl!1*b{iuwN!PG5%EE2OIdFEjXq6b=SD1U|NRzVFP~A*|y!iqyHGgo8QrY znBl*_3LKv7db_{Rdvt{dE8C0ijKL{DA*4{Y$5H zKiz*!pwsvP|6TeI=+b%oPW=-B_b-BfbzI;m+%Sw&s$N@X?(NO_@@7fjQ?^c z{KuG`#y2HL;q>Di%PIQJ*ZqANxAe&#sd*&uEvyduvp=9|ui@za1353#i6@-%>Wb{f z+gayd8uHw0{GGBFj5qvAymL2{J*~#2hP~iH)lv8B1BTz)X!!7oZ~6EVtAvm8Tx*?j ztpl__pZ=fe{&v%Te<|;IL9-9`Fy<`Y59Bv_zsOlvcUZpR z4~Kp--z~ez`|nZytmDKUMxH-wH+FFg@96dCeYJxcKkvHT%IjKg_#bpN{4MyU*G)0J zuB(Bas{3!W8veZnhW|O`8`lAg>o_sZIm{vBBExRF6&zjwU*PV3m3fa#&3hQ{Q3-Fn zkuJBiDDMx84BNX;`8SVJ{`bac+}#%7P2Q^f;lCs=@Fh4i$M`z@4Ds`mc}GdL};nq7;8$R*K)h%&?z+Ncp$#GW^&7VAytC zxv?Y4e}AI#w{122w*7{`ZJQyv9dZuRFW0r|l^+<5KYYIOdb59V;Fro9zhBwiE;R&> zJG9a8#!ofuZZB!LQunttgD)}17nIxPYFMr{TDdd-m-0qWWQ|rD{sFH4^A%uTYxsX2 z56`Fd>E$!f@kRk-pB4Y!WMDsS*gftB_Fm+Ov7TZt;?Dte?Dgo6x_mbWU9}Bd9%N42 zzH7KS`@p+0*Uj0c+#GmrG`!sV0ON5zzL9eYgTXruYy2c&!^`Qqe-*TOu+ebuque&o z@Dt1>QE#{zpCUuIvR?;pOyPPwywQ7{;eHCOhHpvH_wx1RV~y^Dw|P}9c*7q>@W<_M z(}q~8H*~l?2i|&^~Z|!}<-UWXzg1_IN1HJcijtE>n zgx*6ggx=`l#qf8!?vL1@{4aTaKjl{V09bDDeZ$XuN!g9id)dzncM{k8q4y&25xj>U zNaH<&J!+$e;B8(#IpFPm7TC~RU=JN^*nOVB_UFRie-S<@ax+Sq&uxa={xRiL;`k4N zQ<>xDzJ**d#)N+G^!tW8^+UrRw+lLf%ZJSG>hq~jG`t=wRY%?3`k8X?>j$q;j<^I^ z=No>;OTbx&oM1ottil$dLnf>P=4cJmukXNnJ^AsJT$)$c2JCV34ZGKL<=%Q8a#?Z; zZ~yjB4ENF7!F@*;_pK}0pM;06zM8Q<25&>-?OTko{~TpM3tgsuY}oJYRrV6_`QeZB z9PM{*HS8rXsE)dOC35oDTMhRC$}1lQ7IesdP5D&^u%S1yC%GKFHy|@>q5Vi?^fUCy zfs~(h`B9;_)+grhmVmu~o?$Z|ybl<5!7O~eFf#Fpa-&yqUZAdvJAAs~9(xyi zcMo#(k?8Jh$w~MYV~qU#N>Jo7}W;WbVLZ}hgt8+trD0hk|m!uulj8`5$)ywtScL3b}fHlUGT zkNl_chL3u=u@>cL-UXlEROD`1uH0j+?{I9-tdE5T8h1Cmfi)fgz8^8>Zou*VFwBJEXvQ$X^-naY}e|W)*lF*;=QBg*GL~BY2BW8P}-1JaqVn zgHk$W+$6)xL+_>Q{??)BCY~Rn+}cB>b&B${RvY&2A2IgN)3)HZ%A5E76dY_p3h$%r z|Ds@#}OLVw9>oatC zKk!DMX&t^x*=Jn{oxY=Wwuer!&svENPu2Yq>(JpB8tzXix4w|l;d9`>=y1bZxX|z( z2_lzwr_P)_^9=sV4DCC{%c^aUdfdT{+J6^5+HoU(zR}ZLwv2uM>TdR*;Fr%{;(U|H&~t4|!@% zvUUr#Z|ZM}815Snan|8m(51le_FRMynol_hUrp-YR_^76oGqEG{a|<2MuYEcK$CZj zl#g6j6fdjnsr|-OQ=v{@=6xhTj_gjJJs;)^qmb9nMfyXUSO*zXo_s=<$W_g?>MLGN)Vg+ytHR(9UYnlX{RjK%8H}=TG{s2eEFT3Xh2V0Y_ z`;Wf{4Hl!L4*+K&Yx4=`St_&KZ~;1YH!(%2^6VC34SefSP0H(Yk>NK#1gsg*CmnE%W;<4y#Rl^PFk!Yk-={AcAD-e-0-j7p!|ov z0M2X5&plzp%jz=yC;EUBaxkF;TXul{C$L@U)t=~8Y?=So)zl{eYn8TTQg`2Mz?Kcg zmQiBM{3otX`)V%oR@c?@JkG$7&)9nhG~wPkmEF8Wi_n!uH~XDC(Q~(QR;RkV-Sb)U z+m+qD@85+jdzN#f)%o6%CE!(=Z~y6D<^Sd9MtoqUZU1SY;XQ{9{^S>iIk2vq_k)3k zz4Xt@ek_0={X5aY-Mv5cHN4*N$fr^_3UvM150wA$Z=eTqziq4GU-b?;?g4xZWPDT1 zuy6P`4?+_`UR-`svI%sH_Fbm=k1 z+lx<#J$ZIFdhpvi?(p`mCf{(lTSpAyl%dPUqZfJ{PqiO+G4$MXy8XB{jQ?HMVJERd zZ`u*#3fytur5!P22i$SQlF}|nJ9Z4bMMu)(~|C-Y@F-${UZY_e9ojhaSX!-gsnP{N;4rw|)oybp`%a?-K*P&73|(E*n{w>P$b; z9hjqazQSAjSt^#=w;vw9GzCxMvVBv*2c0DFsQXtna;9!SJWL6l1(rK|v~nMR9UX)n z?us35d7T*WzrYbYObqA_8$uc&WNOi}n59Bg((?2gJt@BbQ@E)-$!NhtPLp z%YgL(y5i6DC5O=a;uNgmW2xT)Ea)t-sQa&j-<{Wi7bP?hSbhM%4jp7SO-BFJruEo* zV)8M-{4}jY7Z8)rK>wub?#!9M!>>Drp1|*OvGvIMX`FWVrT?FGe{bI(@LjMA5-Sr2 zdi!oB27ewprRx4w$BDbalwMy59CYZE z_cf00BO?rV3-}*92u}3DZ$;Llr|7hMApJj0`E0xP5l<2;Pke;7v+$vigT=qZhf3Am z&BWJVfXfNWAwz&Q3x5Qhi2d+y$O$gs%x-m&J@!6qP7C(H$hQj?a&FJ)V!wSqI1M+< z0hI-I(PDC!m0j!`&|SCPCTIEzMgHSXWl!%Nk^i3vfA#X-DKorYA1XUH1KE9m9AjO+ z_s0_C|0CrkqUeO@q#g2&o$rCpzx7qo7e!L{7Q-JO{n~Jc%@8|pxFZG_?iDu}-u0IX zKN;TJgPG%4>XakKQFqZ_*JIb$YaQ=qFo$Rn@klpdeZd*ck7#otyu3^Kx8c`HEcy0I z@-0v1%e7fTOcs4n`H_zddw&n;_9d|d*Kc1!|Gmon=n5V0*l)j}{Qp32X3R1C>{n@9 zXxNL$nMs_s7hSM;5Ay(5iPQG3QBs;EMo+OjzIl-LXVLzvbo=kn{!g9S|AO4=%RSvI z&u1PDtPl31X)3YLOYq8D%;hy@FPfm^H+ThD%YgNeh9!0P@im4$H-fLxrNDh_H)mi! z0}p(L+)sqgQTC953!F1zOS|Yo;}p)~2Nq$+({pgQB4fms?g`2f@FKCL#HfBWM67cg zc~11ur1kJLdT0^!mpqxybE1b9!87T)U&A~b+Le3sP1f%yIX?3LrIk_f6J*Ui$wTFf z4&r*ue&SSg(B!?qYNU=1>Va&h>;AA^_$|!qKFWqt(Lw06dgOhKmUr(d%=|GA@`W!HjFXc8UIJ~xm)v=$a{tx$_FRue-8ce%e0QgFYA3tCmnk$dJ+Ci z>sbGFe1JR0p%*DdFM^Yw15Uw&>~Gq3;~lIYadVt=Rp3UfKwcglP*&Z=`_Fq>yD@yT ztg_I1>M0}Mzpjh7{~lm}jz3>rsQvk}%3k){_u|jvSG-+pc+XfSt_|79M&68;Brjvc^9S%!~-EOXDk{$gk_4A|w+ z^m~*SEA6vb7IQhO{D-@OS9Ov9L_24vOl7}~9ewtQ#=(2XWjyTY579eiMlbP=+$-)k zymz!cEtG5Vb)3HM71`Gp5F;N;^}C^*n0B}B=Rf=uI)y$rtk$Lfu;9>#G2mn7>AiC) zW9(1GzoCoL@L%F=M6Pb{M!>zPEA2mLeSfdKcfLvcKcw1Q^?J_UJJ0jZPC)i#irjyP z2Du6NWg`5tGR-fq5QksOdjDSYhdt(La3$`d?msft5dVd54;2aTxWkaK8Jj7QshT(8 zo9Yzb$ae`F2a@~3P8@iVGuHc*_bmRzN9e(!qtS!6gCqV(y6#``s`5{aCr3c}=KUfU zS|0q&7Un~aK=NQ(AN>ZH!1(MV=EAf6&+43))cu-2fJY1Zhk586*Gt@M5&fRw*TBoj zzc;3Y+!VYy8F`<8oEHt&u!s#f=Q7FgidJd8Ep_*;fjZ~ppP)ov3oMCK{OEf8moxOx zGSNfmsQP+j7(KL1^w3+#;u?Gy=+r z>h5OnlRU|nlta+lxxkm4Vn!3TyE@+^r{fJ>-0daqwG^rEd1Fdzdr! z_Wj3rud1%d`~FYJZ#-*=oO&Y%kT?DaKfSl|M)o(nZU@<8=%&2wTtCD02Zh{6uI-PB z6>R3!fUmxkc(xmU`vc_Zza_khk6Mnez7L-Xxqf30^>5?D??k3s$nE1V&%Oo!ImR0^ z5%z%!iLZ|pc>!X5$xmFj2w3-NSpK8;7~bQ^av8GBGyigY=X-m|eimm_d&=5G{MpBmYk`BU zw6T>M2V|!=wh|mt<4*-AY-PIcZ^Aa-^^kJgDA^nEXM@+=H^`mUg4-l;Tb0Ib9WY0C z!figdO;6z#9i{R0FMk1?x`Wdi;1q#VA-0xe&|y4O9xJu zy{7yl(Ck6VG*0f!H^_B1A+JAVzZCt_(!e_*=$B`1Al~02d_x`m^2|@zOQeo0%RPTs ziQyl8ha5mDIgCHC2Wdp)d!{M8G#>vl1}3pIXzgV3HFx=p?d8cN-N1KjV0 zPmfTtj-sPPzqr}!@lB9r?6>xF#C{iw{RUsrFJiw7#ePH6ImK!H@-y_X*l%Pov=yA{ z!eXzqpCkE#XIk)cenpOnIe8bQaH{VPFQQ+(+7wRI{mXap-VS)&MJYJxcn$rM+hUmK zRTtP}kg38YsrB1)AFvi_SkR!r+lgE+MXsTFyr0p_-gh^8<7wiAt?VD1hi?4~`v(Ki z{pX<5K1BC_-|+s}pQPPEWzP+>f9O%~N3M^!@gVwO81#h~-Ays}L65=b#M{GWARCvY z`R=>yW8OvmLS*tDd|mYB+&753J@!V2^A2GIS`|Vocv#EA#pu92yqnYmS*|1Zb`W^7 zE^EkZ89n4$?*s47W6clg+#>lre6H=fk88$Kd%=5_GkyVaH1aq$->~hY=n8besEdH# z#5`^lokH6w+=I4Try$4QC12T%xqgvqyOX$J=`PydPJX~owcSZ<(D%t+{+m;!pEu(>-@LX z%3gF|_I3Z9g1Pe#tlu|J!`yi@yzsn+so`v@VZZohLtV+XK z$GVR;^ju`lJLfaE>8ZIrdbggNd)Z5ddt?%Dl7<`qq2yk}uDMC?XWKg+`Zw$T_P(8| zJ#5(zy=;?V?~A4Ou+w#S8#cZTozg~$J_iQ-wD@0hW}&xlgAJsd6nPGC`GQj7ZgVGbH#j}M8-IiRL;-T#!u42evW<=fKfmE9b!@UteuBFD1pW*6 z{Nb-E|DLbl=MB)NUHs#a-s`WSzr@|5?__?W@0NWRnoXsx&>WnW{Sum|>h5Lex+AwM z_aRE=Cu`?Eft`7H3}^kTdwOH0gV%hGm)IR|EV64a)UcsLp*?B_b6x=aiO6zgPkYzh z_;1Ma`;RK`h7$bj1KQ8_zTcnR&q1Ak^S)ndcxScnE@Mc?+1{V}kz0QsKWII3n<0Dv z{XFD)Gy55Xp+OC0Irz`Qr??nAz=n zcNU2GB~D4fTEp1HqU;w*J(Y_oBDZk@92j%gw}{2x*8N=bGVoyhT`%hK{NN_+0J8k_ z(R5p6`B`L{G04&DHuf%btKh~t54))wdg=@G$`;lIS$=vK>quSp%ZIVHyOHJMCws}Y zpDpcgCjat0z8TlMIj^wrKgsDpw_z>Rp90pM z|1wo~uL5`Ui+dmCRRfWmE!Y%r%5KvBwa9V`Cv39h8V`2hgiV%QBXvLV1C6K8zG+I9 zDHE;WRA+VM4eT-Kmjd*Qp5H!Tp_2|3<>~G;u zH~xf}8h?4xdid-icx#!4%eoZG9+|yt0kmsJj_Z2b`|l>#iX2bqi4r(XkqkG9D_pab=}9hbe2oy7PPcu%Ubm(Ta4<@}EK zpGn}drjOFT=`7Cbpc7lLSAY19;Vt`@tJLWcKAvhtwQqRWk);RS8r`_OG+Tl}!6Y~_Dnut9X5_{0z+YL_M zrN|2W106WChR^a6Yn-ZUopP-Glui*CK6{mJc0)>jB`2nFLVgR7-%~g}2Tngt;WX!* zG)}WvJ9tgolD*PnMHfD!eQbMt33(&*@a_NGu>VLd zYzeurzdVM#mKxsEti}7R{rAhrt$v{8SYl(pJ^>C3iGeON?D2!)2k5eIki->~7a_ao z#PP^5F%otaT(LE?!#bZVdyme*n|gXFSP z@HRT&3(AHCT6XBdRKXJYYes#mF;m~^WNZ@~T zBly)aN6wp;RtB{$uxio$*xm)$E#dE`UHBUpp5Bjdn#jKX%2WIC{;(dzu*~%yN||4% z3B6aV+^kk|fj=Vlj-7+sPgOZ=o4Dk=rUY zPUD0=ksNzU=CBv|C>>`4bdDV#rK9dQhz{7#zCL9dCtvHh?thz~%)ho1PVjmD-5MvK zGn=WrWUq8yG9MkXO=QW`bJTfBaN?}Ad!QCNfz!e?PD2~1PlZltoT&T5mcawybPr{k z4|FU=zKydbMRwCqfc0#e=Jx|@v7R5cuF&3%TrWkg4}OZDTG`9nhwLswc5mOyKFzt< zSZwdD!W{1cJZwj;ah*Wh-+k*J!sk6*0FHIFnqUi_V)1g_j103_}Je2 zE%1^OS;j|XKZqD)AnPzjgTEc z_(H((XMIZC)0=p`S*NcAot!J&vvSln0WV*+Xuv0QkpJ?b*}z7NOri zJV(y?Yr8La+3aoW^RRQ946g~Qx1rF7Ak#_;UErBjy;AD=|x}O*dmJv4Qk2Nu8;p zy_~7C$p!0kup`h|vfRJkW8sWCH#=-ISb&KThS#iX!-Ycy~=** zIK!W~nDKeGYZ2>wALor0EB7&UFZcW*kE06~k>8uC{NNIag-w6vW^^t7^0nuH$A`MV zef?hY(~R@c6^6fg2zwLgm7*8mlT_WkqLp*HBRD%s$v&h%a|l1|`XepSmi+Xx7on5* zXuk%h>+$&{#%#eiTMSP3lM5z?K69?{HM#B=$SYI7`X!wUHcJgt@41Dfe?M};=#{DO zlM6<#EWR+c->Y-M=#|AQiIX1DxnXw*_x^Bt73->Y-M$j{89#BdW+=S^C$;d0)j z2tS4MBG~cNd6S~MId5`8?bl#*Nbh?`JCX}bnn}E|}wJLrtu`T|ymf?e<4}Qx0o`Y_0b0+gH;ClGg z>@N)nfSOTxoPA( zAK9La9%4^f`&{+Yk>}rYZW5UtIt4%cJFLOv)HxD4k9otlknPFDd+U(l$(&&xDl$}* z+GlRXK4HgA)(^khYeGh!K}ILB7JONY960UBeF+@N zeR+1LcI3U>t7w-){1ll>ekB3EV>nB45oggjgJ{bc#Ab4Ozhxc2k3YkFhTlVd33B%U zwm(((FM~(!qP}Sq_WnNJuc!|AapJdG^xcUb=bW|Qe4jpNZOb|9Tj3ebn#(zBTh3Xh z>zq?&?U!*5;cDcC@`~$$cNxEC_9@Vtk~=Cw2Bv((y1$Ctjni_MI*Ypqxw}uxoj#*+ z#bfaI2+lLW^Pw7L^35SBC8o9|)_l4-eKvwvb1}GzZ0NHQF>pe^rt9uzaQY&SoFGqf zE<(-)2N$qb6M?akcziSYiC388wffv=Dqn%ETZFB9e-7)#m_z0O19_A4sb(BpjDVDG zh;U`P0{+)r6F;9LBnjTgI9_l+u;d{jJ#V`&f&u=dkt{%lFuH>4xCF>m1-f zpARzV2Yrg%6r9V^|Kno7A?{<%T(r{@;`MqN*DZ(4?dBeu@x}OCnD-hDOX3aps%G|M z-r#*3N^-dxMgpATcM{Xbz$ux=$p@!VopAEN>8=zU!DkzCBsrU_J_W}H@Tt_L_z~V8 zN`P}94W|(}gFC^Q1RQA3-24ZLL-gFxS#5$F_&of&{E$oJnt0aky&9g|#=JH#udy1A zj-SRaWM22AXhb~a&U~D>yc@n(H*h<`dpTQx*M^S-U1q-leuggfzS^F?;730Gmfp~s z{KnF6rto{?4eI3Mmp-4ukGi|fVxHjCMk)9KiywS88CmDoyEc_WAHN>_;Myr#0R#ksb`xl@kN{wT5c zE!YY8mb!c7f9ZSv7D|bA$w`|Id06t!GFDvP?VH7S8hCeBuG#-S%b(3zioVr7oU$%b zFM=-I>n<|e!;SokTmtr)cV_*ZHROisxQ8tZjKwFkg2c_e+ywDx>55*aQ`d`nUTo$n zHFe`#o6VAHa<8qNvuMS4tT&v}?!1f9$a^Ff?^TT^hMgp5QHo98p^x{gEH=+EiqTPh z*sJanA5htcvt@na=hpRRzpHoryvp9@H;mrsm9u#F{_ONO9**!1ewNd(I*a|9ET{PF zY~I1obk3{Hr|hQ84Xwp|zrb4Nyl5?T)>unpMIn{?E=uCOg=^*8${WYoeA|Gz zEb|fxW%E6IJEX!3=jrb*e{PoW%^d!3lNYQGm?Nq~y#=)KjvXWCu4tvqk4G5p&ad5*8nbN!C- z577mQeg@yk$h<>(*&f#7a@yiWH?EZDU z<1YB;F(3Z#*G4_RR_=xQmQts_s?lzoWAM%HsNI-h@Evc;Ir2St{^!L7nf7R&@&82Z z(LB>7&w@M)o}DT1Jq=HP=huMdVW;l};lCZX;!|iRI#~7wgl}YjR`%}2f0TEkjSZi( zHXC1GYLBW>8v?+6IB>|lYZSgm4f1mrxg+$3hmF+lUn3Wjk*%HBT4}R0-R4NDjl4(f zx6B~cr_CsAxP~(;4F_HoI0e+FpdY480ZzKj&UBk2sW!Z?N{$CvJV={Sv(s>9r{P?e zg2Vq(bZ@yAf9n?Dq}%LFw>gq(<2^H*{n&f41=1#w#yOFKa|FE)KJsp}e_%R39r8Uo zq5M|vk4oM1@8p{IBIW&YcM`gI(mLKVP=_!of0s(Wlh;Xp zUzKn8+0*$J;oV=Dl4Ew!ul(OxjguDw*VQ$F`09p$oj^AD&Y8;xhveNBXJyEAYFh#+ zJy5YOJ%{H#=qTTTPV14R+;`OcT6D2t{*&+WrszJ@P0_j{1g-f-L>E~D)(QQ@ca&1! zm8sCYk-Ap31^Qb$n_4>B>+uFyOXV9nd^dWj&~?LsjN81#^vO=Yb!x-9DZlX&qd#y; zxo*Av-@HUgxqM%GL+jP6yu_+e&H%1Q=Un3@TKS)ITu-;2^%C{7oq=2*i1+al>lQo3 z>jE1Z-@U<0%-`YkmG4m}pzXY%@Kbu;GgD=i%{q7B{cH!0j{G0tGybP=+UDpvcRaVJ zxAK2+_RD}nz!ZC3`Btg~5Bzf_kc)^qYd2IShd>eqPF z2P=DhcjZAl@?#Y?--&YZf8=bEXYp-H{{Mn}KQH!qeX#r}{8_es2{te&-}Es{hkPY` z!vALc%IVwjzY-I}onm~ZPT%f-p))#$kYokidz2YOXc??; zz;3LU=Q;ZM{Gr4?tC*t|N$U24XkRSt`JV~EAKfU=_#Z*QntwpzuhVTi^>r*6tAf7R zlh#&K<}AGDg=V(t+;PH+xS@+GwZC+8sntPmzCYJ?n&uUw|2vvP!+@4?=&g0G(7ITE z1Mv0~otN=#RCqXqOf9V!{r5L=r{z?CFI41ARvNvmY zg&jWD?y_eu-(d4X2~{3O_V_;rvEwa)@?#=TPt=WiVlt9w@2|IGx6Iq@Y^%++cWUnGx!co2G29#-^}vk#rTl3mwJzc zdH*tuEQB~8iY?E8o{2(ubIsD&n~}TaTV-jT_%it379ToP{*S@~vS$1*RbmnF7oAj_ zc+SH|b^?tmc2($scxeW*na}@BuU}##1NvL{ne=%S_*v-mrO`l|4}Q+KEhmazwfQDz z?B<_q8yp|n_*@JdX!9LUZSj;0pCA8%DNx@r{7NN zW%13_&9s|&^uPUf8hTa!Hv!)%4X4Ly?KD;^W3~Rn@1?b@>WGK_%|jQ)@I!#&n5gnkQH69ukQT)!k)?>4^cDM&DV>rS3{CR#oD_`k*Y@{6=k98?+BKvT%^csm_kTYB z=l^|<&q1a^2c@3Ha>t9vD{FL|JM0|2;BC7LdGwjgK0+^F=gdvjM|9DlvJc@G>@=;b zCepgN`Lmx%(osExymui)HZUNW%?A z8N5jxvSnoWeDd)5vnobV=4~@3(Tj5KtQ9}qIw^Yb&P$ht705ZT!Uxvkqt{{c5>NPDPkDrco)^UV0N&OpVM{YcI}o;)ft z(eqDouMl!S>kL%SXDN4Kq_~BBB<&ZG$#Sl#oD<5^FZK}6oga~B#pz5{!TCP}wAO zY2C1>&~@V7ultN?+5`W82`%5?3be3peM;wq6ig6CUwh>m9W&9V-^w!FuP#a)pb5 z@p7Si-?g4m#=1ywCwERj|4MGCvnIjGnFoisa~fP-_k6M7%ylC1b%m=Zz*V`oNcY{d zW=`K_UMt$s#exGF$Jhm5-oCf2PxpObgGb(_?`j@9j$tb?Z+j9#mj%|@7ok6Xa~6f< z$?mUZ6eFYI2WZ-7K8HUk^qS(00mbLo^Iumywe0Aq!%u5W72$)y24+vr+5ijL%&ChtA&1 zsk4CG-{4raOh<%=k0RtZ;*4$TQB;G_FOnv)P50qx0n}nE3(YmnebX)@f0KHxr;}|rkp$5hBgZRe7C+K=XkrUvq#jNp4m5kL}-wC z#{a_y&~$x=^n-H^U!RZjogZW|oJiZXIl!o)I2Cv+Job zd3P;wf1Jq&XGRkTG#@xP+cSPZ@?`UYbB633-^uZ>_G0VJ`Ht(x}Wa`zy3en-(2OV*v!?6v^hFS~K_QcksYH1sN6ogTwyu4cu?KH#<;~N!8c(HmmPh01^Yi| z-^4GGvB)@7`#!JSrII@@bDduXO;u*o&!E(~bSM))=~#=#$>4*OnMXT9H{yTkxlZgC zT4DSX&=U8?q3=8$&=hl3b%cBWPAj|KwPfgP)*`XPnG4ZL@uQ;CN@PBv19LZ-(6FQee&ci zXl0AbJ3jQGvh%s^V>vf-WVmg%t@9_!bz7%L@d35pN1fuYNGxdt+$s37W6${Zk2B^C zN{{#o6UIUM(I|ZYZ)a{ehVKfUK+kF&#_%Y)f&V?>J1~yjaYWMsXKzc4!ndg6vZk@N zZ-T$e8FALv&|jA=&Uu%4v-XWEyBC=;CUF$xEO+1a7SwJyN~{-uhxtZ7afY?#AvqIU zY$W7`;kte2Wewx5hza^d?96u{fqe;|@AnwKoy=k1$2u;#58P()p2!d3QJ+~s{7ljC zMUiD`nV|Hu8=aDpb&1O&8yvZ|Pt(5%uZ$s6+*d|~7ffOo>AO)9-;l98wDbN|`1{9` zr(|A--i8MqegcQ!Qsj@+Ei_2|4v!iAx=sV!5>HS%d+J-+GGcPWPb_GKcsl;R{o3h~ zp;M<-TzSb~5KF&)l6bH&**5Vf7O?@sq+&Mcjv+ z^i4bqy%F!>Ec-I=bc(Ltkvy(@g_f^Z{OOzcJK_n?a(`LtmBEDDaLN3KWgP*3;PLT7 z-~QGD-+?;TbxtO0>&?7(g+WU$r4NY#&*SBD|9(-^^T0fs2E~3E+(A3(^^bfpb@cG;Jl^6dQg?1JhV1#&dbs5~zk<0ez~;(*k0tU! z;>#(#r{!{bE~n09<4)N?Yv}I~`mGQ@J|Qx%UfrLQiA%?Rd0oSoRF-+~n}cfy{{8Iw zbJ&b&I?-kBc`X;7;cvBOfezEU%cw|d-8Dq)!+k~Dz%3o(Bj8V7u@*1!2o3lK#*`f; zy35B}4>plo;J$&10;?1L5+1qXD0UOPGf#iDu*M|c$xm6|e(qh!T6d(cIu4&f8`4+N z*Mn`theWPeADrTD`qPI7m!R)V@Vd)Wq;z(k^(NwjE$P_Vj?>;r>XO)*(w7Ic&h$+P z&lxSm6Aj;U$ol8`{)Dy#WK2Hy(L<@eru!&keRABL{ax$Ip>eIF25NKaK<(r1(0M)2 ziQZFonR7Re*a_%meTTQ`Nzc%~t9eo7T4M9)V2Ot~cBx@~6E`I_$`&=+BXi{`^@k8k0L_rB3nt2Jb~ijkR&k)CtE9KXsJ; zDu3%egL_cD!qdukC&tEF7qSKYS)gh1-DAXhJ>r{5-ZM9QE~RijSg|^N-)wqbvi#1c zkz1^9=2&l~PuNg$$4S`&aJTy`HmKK4tjRq>`;M+Jkhr+;I(vz*N!!@V(#G7dPCD@@ zKHCuY4=gAcvV8 zGpB@J3kT5)T2Gd;wkG;@q26mU%-J5pZnY*Z_xD*B&K8Oeku~9z9+ow&r+d|Yt;Y{P zb}wtg*cQV7hTO$IP{i%W)RyI8;dAVdr*42>@ z!h6WoUyK*Qa|^E~OFy;$`tY$Egw|wz=Bobd*f{GoPWx|4F2}3Jfw4|ir1mb9UwuA) z3Ho$@5`FsiW20bSr0@=J2_NZwJ@iq_o2f^oU!s$^v+?{@@P9EA=g1RmAsH()wi|j+ z17Ytfb#j@{rnc<0$rcR=hyzriPt~Df05V& zaSQIMU&dYacW_sICI0i$YL~BKHRq0hfqPs7F6P-SXGxS5lZZo09J0*QK%6I$JnTud z)E+&?dN+HZSVyYeN=)vZQ{tm?rzv}0hfcOwZ=b9>T*Ud53-~`Z!#A!TdGsUh)H|0f zE7~u2w|fV_&AtfMiS_pzjWb^@OdfvxKU%DxJ#ek-&d22bVb)(4qHpZg)-RsBi5RrJ zqkCAp;y&-EJ4Ela_-eg>T;Qv~SN3|j59!{tzEOIHyJvmuhw)V`6npE;Wzu&<@Y`RnUwobYCB(HpX?vfr(vV4XczVEN}gHdx?uhI*;~pIl<~CDXU6=* z54^SB`U1HNR&XZk;l$~#9De)n-g_xFanVVQ0`jWU7@f;P; z8u29Fk{FY-{`0c6i}5I57ai{4N5(IBL=P_tBMh-8%A@99+3U0YTllH!xbZ)g zzyD{%EiVl&*?B4D`>_V8)(eRPv(JNms{L_O4YTh9%jfZW-+>{WdD;fbQbalOCAS1J36Sl9J(r>UA_gSBn=n)#6Up6^!i z&r~c?>7lfo&uZuJ(DdxSk4vWtCzbCiF>Gk;)~Ddf1UgFjbp_dR3vj1=>HM;E;49>r z7Xt(rF4==a{7LN{6g^e;8RF7gmoAVPx;3+8!ART3$&phZ7Kr`mnrM}ENar3`_zk}m zI=(#OZ6(f@TGO6--?fAo_)Q+@bNaHTfr@#tGy0=qX0m@$+B);o#7x=30VCDknVUQ_ zYR@C%*EaLOdL0iZW}$58f&24~No22#MdEze7e#7J=NZd?drSlW`b&*T)G zrj|8x$JElsn9{mNbc&i2?+|iU=6`JV9&?Ew;G60-$+lsi+VA@Y`-aIw?1}Y^CCm?N z$9~ycqvRqscY$8(BQ8_PTHhVhz>$jzpGHecPS6J}53>C0vPZE`<&ItDcX@uO+Twl0 z)Al3zHnO1Yhxl2@%2wnxdy5pGw?s0>B>GdvrF}N~!8%)d3{|Q1hoN_YBgaj>rm6P5 z>|{T%eA9Bi%u|OyJa$E`e;4ja`Qya2gcjMuqW!Fz!M4^b65Fx{o?^|c^$PO{9QitL zABbN6*84zq&WmZKa3eA_AAX$p!_Q@{yye$T!;OBB^g;jPkcqVuZOI?fpExr$PSu;xET$Ex7cPZ z9*D-xP&jCBH_b?2+>CTOkj_ZdjCF@BTRwH$;Vtnl)39jkI@;*tc`MIAEM`aJp-9;5 z=(aoKrWFZ?ZA*QK+A%wBhvPD$a3pSaM*70Ru1G9y_JyN%!0HNY?zThWt%2@P(CiG+ zLZHXiowCD0r%T}oJ&Z&H(o?Wwo6*pgu6T#p8K6LLr8z@CN;CRnfgpz0@dv%qhm|Y) z!rP*O-pboMOu85_J3bH2J51W_v*Xb|+vIPVxvbdO?Kcg!cB*-Mp7)ko)A)d71;Wzz zsBQH{W1+2fcfT31zSI|r+GbB*+}=J9*9v1Rdh8ynD+dyE|vsiy$X;Q`+cYgQ?1<^YSf~r<^>3j|wMGDsKVbT4Qm8f%(rnrr>F)wMOXwY7D%^|cMPjkQg+&2|2|>bjb`+Pb>B`nra?#=54u z=6Zj9b$v~JZGByReSJfHV|`P7bA!L3x}m0_wxO<}zM-L^v7xD(_r=?I zMg!fDRsW%e)gxaCzNVH##Crlfa)V-V}vc_fnQ}6TCFS7G5XiWI?zAw+pflK42(LP}ERnyJ;&zeT@Y|lHH zIX#e-D+Arz0{yW~0mv?1dHXU`y$A+GzNctzFKt%yyVxP3bXD8B5N2j?G-BDY7^QTr zRhhM<>UfJfYN^Ai!V2`opq_9fyc|h~6wh7SFwBIh=BzUihxou_Jk(>Sh%mZMNZXWu zs7&PkwrD6$RmH|e%B`Ti)t2;4u~1 z{SDjkZIS3hnM9wFN#Tx6h+XHa9Sj76sFN5QwSx;GGorJ%%=A`LRZGPgld<}!`8xxW7O8CShSmYfIVp-65cX9A4)z(`+DPckgoRi z_QFu|YH#laV~k2n1MCs$pJT0P=@f}~!H!C*_JyrL+};w2hIU8+5wvDyB<8%`p616t zcvahDU*swLC3;hAEwOLpT~e9z``37iT)Lhlc7oV(@-FFGlE5Ud$oeLd*kYnbB(KPC zkpohnd=r@DmpT`dMDB_1k#Cac29ne(ZOFUilXt2ARrJ)0@Z@WxU2!(L9C_Lk2_j7$gn_3mNH~O+4GV|9gyrgJ zfO}h4nwvv$Z5-R%yHE(QiBa^Lsr-ze(9hqbpU1(a(2@8e2C@ZlpH_z}iy0}}b$tYt z5VYe6RAe6(ZUosxWy|vG-Oj%5Zj5mlEMVfaY(~z^)+@?S>}sKJnm3BR%6w(~b`P1x z+xvij!&G#y@;qW1@ty3gN=xWipHe-v6cu5PgCOoo2zprjJ!p@5S8TusiLF4XfuR+m zS$L1dGyX}TIPDQxc6axRNZ7K)s98ompQWA(guv%`rgEng9HM#TaAcFl zeT>3NaawpbI8Kh&CQ53yt8=`B*&3-WooAC&LS5gdA4_TfCDL-zA?EHLo|!gD@0a0$ zLDN__k@igj3O`IhyS7c&i6S=FJ6cG&I%cpBj@u&2C!oxhpdG{gQ7Q-hn?rw1x4dd_NwS|=5%~Qrx zOntZKs88Y6nT$;S6xq0I$}~)#^7kJW0mTZJZO`al$<*#7J{YJsV1qHvcs8HF7<$Mb^CzBNd zY%M8W`$)mA!q!DX;tDP@p$mA2d{{dlzLC%6_a~EU1>Ra>W|!a2=N8~1tFr5tvF|0V z1>TLl3jDqJ>I7le6{H5g?o!L`tvH;cD5&z(y#3L{Cs+!Gzz@JrQj{` zT=o6cHP0;i+OMfvW!yJ2kGuaUnS7pi8Nor$oi91? zJv^!ZzE3l!=g9lL0CT15%sHph_j2fD%G^Ch#dc-F|6y{WHMd2cBTd zL*Q=#4^qBW!{^DMOTc#lU+eT?o$f=LSJ~cRyzs5u?N?CeDhEF5)SpW}n81SMtN5E_ zvP-vPwx`C*re=|-($`(&>3SiVd{XMVuf(6-mqWnc1n$u79DIr_c@=o~-zJl9h{9+s zd7+?dwvlXsDecXWzx03Qmow#F>=@v==HH}z6L1rFk;8kr#`GY0+<%`;){#G(|D}$H zftLe62Q3Q!=Awc9p6b=ICwUZv)@u%yF(c5_}XbKp(x5 zOt$B!N8pvft}z)$3tE$zU2XW?`B&ZONq@V&ryJAKGS zJA27vd?!DB9tS={9nU*;WcTq+;5#W_so`_@MbSU--N4N}@N)P_;LCF0K<29n_}*8O z$+vX*yz|vTzI7*)Nztp>^eO!q0KV$AWHPSXDb42l{lK66URp-Y8^;vzp$WNm)z}EtQle(CfmTbJ2U&HrP$)vQEJ#X?! z{*H{9>e+HoKjpVLWR|#yDlk}G)!1C+mw1t}j)<2{Abj1rRWOZxtqIliR&VO5=|$gH;oQZIs%S()XhxOYwW(8LYerQ} ze~4|4#f&QTj}_^WAQqXW!YYCd(a>g`L*TJ4JS(DOH1TQwge)W>k`cHjiFurmANtSj zJP}bUpTwt*zib-EzlDC2z=rcjG4&^(q&{J-0sNeSm!VIYH|H-^pTwagpOEd7qwPI1TVFk zxU&;L1#|=iQNRrW?L{~vq_#bk2uZ|#4@w~cv;{Q~yi`Fk+{EPjS!;G6(%N&L@ALfr z`900^O$N-GC3~U=IrFZgs*Uu zH~;nbFADsN0{^1GzbNo83jF_=0+yzA*u~w1-LFDeK~0Ml)(Kr+;}qm#D5r>+hz88y z-?bZN2))q|(Pvr%ZoG%E%p)C^(L=Oko9z~cyeph3yfbi!UkclC*8J-Mf6c4%?*h_^sXa)U1%AtYU}QX z3c25w?}oBO^r^PKv2#MgEy>_?A?#G|Gb z3*9H|*arHl=J_I#h=F5hwXiF}x02^Zt+3b?8d1;%xnF zO)R=why&%N@1uK9*aN!-?ei;w_JAjYcK@>tyZA!G?pJ2meMcI0;Vp(eaGK6@o#(n$ ziEe%=uIc9@2QLoQV&}anO6m*I6-KjVW(hZvC9HS~uv3K_L?;v&5&gW)nl9Uw*b5G?0(d6dy)YyMn=coiKRclyDNECxu;jp=S3j(=6k2Y|09u7X`K0 z&|p9>4vJXu5TTz_iCk_LcJVE`UAQ7(hu5J;>(Im7gnj<|=;zPrUqkP%2*~~{!)OE! zx&U1cUOrBxdC?3b+e+yvtkgrP3a^y)4Q?v$rfL3I(~L&-ex#`Z+%-7{EA^`m`A#&L znr5|StFgD_811p5&6-{`MAM5aHTTF+_|*)=iW|_^&`i+@{o36)`Axcrtf`0go=Aj7 zYK#MX8AHEmzzJ&lupsXT8S41`8uVh``aP+CbBrR= zl4pDwE2;?SMP-3lO}&qg5n)bDG4)WN!lnr#N$HY0%EF!iCxGq_o1SoR;LeM+t^eThwl>RAXLH|xT#v0U<1 zz^$5TSRwp}9&ybYU9TlFo7<&rv&@i)rWrwZW}9ef-*6JUp-(-u?CWoVp?@ok2>6@< z0~-@|;%%kT)>S^AnZ}0_XP0qWMp!H&k8q$|9PGqj(nZq3sxl79cNY~Z*(jIy8DEDL zG8Mlw&q-N}|5Eaw8yfl>%fO#9>y)1J-xFW*&IibJ0Ww>F%oZTiXCc#P+5O7SLZ;6` zrq4pAd-1#%&wJVZSM&mQFE=8Dnm!>Buv9lR=#yZNisR>zzuMu`;H_Z){M-rJM!Tsid zE$RZYbWQv-v5j(L#MUVFjS)ArRwT5~xp#NS4#;O&=?O|JJuBS}49c+ek=$DzO6c4R zEf2YYQqvBO3?zcQ6UZ`bgXf|&HDPq4EEKj$X|B~1ChgPK>aHn*c4(V~k(r>h!z%)b z@cRMq&#+TgP~M!DpmZ~CPO~E;sS6ESP+pS;9Vp)pK!<=8ygOj0RRj`g-E_++7tjEF z%0(i*jy72-c7|-bTqIO^C~Rj^*F|O`^Ok@WE=;wBW>7{FvWzSb+FF^B(7FY!$a{>t z%&?5Lfka?M&`ntjoKj)yT&KTnB=mKL9qeYf;kQExO52zgbj@or62`WmW!!Dp=89m# zTxYoHr73o39dHI|iO`CmWe#Fq3pK_YbVXKXB1~V##vo%3x~YToM9MV7O`RXKBeE1m zg3>Y$g&1?tO5K>ANX0Ist>c|6lX<6Hp3azqZrbv6Xp@@YntK}K4!XhRnrqxF=RfEg z__mppq4_C^%+F;#-4^T21pn}eX=}2a&)C3d%!4#%|hr1=r=_gf8!ih#g!( z+d=7xAn!!r8DnIKc@FW+WZb5!A4*A>(L2c_8Q(?V`)Rz`x-GUtcw zj5;%sp@r(GI3XEH8XMiP{EleVN2VmwfoEhf zfAanJ7#H_5yQR3{!ccFjo;b@E)>!w~%)oA`9r&3o~t9c()G&^`3_DX}9NRLi@O<4CTDOFFGRq2=Ge=rCOTSr5Xt!ip%0|>>Axqudj6uxNwr*DX z-Hc;JH#>MsH~6lbZLG^ehO&@z(@I?)u+3Y#S*drYB`EDsSvRG-LMyVY$jzB{xU5?u zJS_{I1OD7gm9~I(k#5~w{Tl4Rv@ARILf#o95~(~lveNA|VC$t?B8_M1-Lewt-MZNs z6|})_WXNaBQ|(M(MoL8@v#gsf-tX?3h3LO+-EHlC%Qf##PiWh^Tj)GHu!6EsV?5oh z%;mz?d9Dw_)=lex{bYw%N_%I+QT$x?>ZNqi&WP&Vuh`IiE4f z^1bwgEcN%$_tUz=cb0-5`MkTUm!~i;;B5@rsde2GsTW#y+P3bAv^sEm8=c2>x@`A$ zs-3~R86&&fnH7xTX7s`~%K7A5wq^4^IhML?-U-_p^-y_6LVKTbdDsrTpPdMN-ow^c z^nk~DSfRpz9pqkc8qaTr$7H)h*zEVS-O$Dq+x$E`VQSf~o`nsYmYoRk+(bu)>nN8q z7Ru1grkx`1qccvOFNa5(8_Y{z{niS*qFu~@%(1p*4I%- zU+OC;*M^xV%D0i96*;!iEtj_FJnoryW25SLHXl9Cv(V?QcNo3xV3oWt1b$Q4Xx~CACT0vPFNl@B>bzEnm%jLcDFmsld(6`Cw_#Ipa z*Y%|DRNLU%(73)CnUZbaPG?+{*O=%%%H`+=;Dz6p_kxT|wqeeAFJ&a}Nu5`gZkZx& z(V>nGV#7Z~^Nnz9K%KDGKlmeePQGy0=Zo06#A+LQ3UBtS!kJMcXtP85!o+gFwg;d~ z@w4soSESi}wG4112I9IiSue)n0#Eoa5>hlyLxB5o-lZYdycDIjhsAa3bJ z+|rATo(u1l`n79|EFaH+x^&}`~u*7BX=6jF*@VZ6IQ(QS{$d{ zSHw#}yC3m;U*h+|vx(O)BwjBgULUF3{cq9j;%UTy6*~ABZje}_fAA!94%mG+Qx3x4 z*?dy}Wl*yR1jz;P-*4qfyZDup(LieV*h@pS=;M(*{Uu`T7b}U&PY_Fu7S?F~Q^+Sq zt`P2$>xq9;HEaMea+c7GBO;nXd{_+JqRNx8qBlEYH4Q4pi)I;ldU2|V4f_)@>|^B8 z9_!GH{?s9Hu_HT9+Wq&PjLsp}TSC0nV=y`07imM>Oy14KCh~5F!t?xrWPx@vLl*RrCSTSz#$*R*oh4{0#{yxZ_$(| z)l~h#UL!4So%}mE@pP$ob6%egvA#HG;7s@&YG&h}uXu z@-1oPoYn@Mw57t5`IRv13CwTw(UgE^exF=(gnS(DuMVAb8;SeLF_L2!Ejc^eowO?Q zG6r*N2H*!D?&IWcB5wyItd;`vghfoO+L+{R&IkTIOLey`PCgyoG}It&%@W?@kC#RN zeM1I(n+0vN^z&q14E{C9fyGmyON7`zLLRy^$5ehc`7$*ZlnK7gk+yPxD+q3i!)(B8+z z?!O-RZ)?=KMUm^{&7<4k!7aCtEc|#Z4YyOKI^si;E>2tR-O*re9%J&a3f2ZsQGJuXg8yUEexh#`% z5U34xBNjjwkOOx%JR3Md-Y!M6Wc~@+lVh-ye8X$dF-gDV;E7n)$KPkT} z>&Gk|oh>$nb(4OtN4Ly8l4*e_YlT6|Ox6;E`fy9chTFor@2Kt`J*w-MJHpCo)~qq) z`g0qFyJ(7V4jd5feaCcX%2DA=ZWJZVXEfLvh?ZmPPA-5BuL|9SJ|S^3I;K_0Q1s~q z!X@7xecbFTZ6JD0iyqsI&OyJ;4fgh+ofS&?R{e%9I?FP@C#>lW9Zpq0%`qpJx-%~( zZ^-+?CZ~yg2*XqT!zZoF^OSD%szX{!&XjiMf%QG|uJ`k9$kIGAtzOkG5u?4khV{TV z{Ps>bkM=v;-BHD&#KboXo8JbhI6}y)aiO$uD@8HR5U0AwEb7w*SeNpm@jaX5f-2G5ZPoSSq>wo~V9Y6;J&;bE-fQAmx zuq_(41-qqeiSFwIbbDSc{o;E||N9;3e*+KOrVn7v^|2h`KK4~dv^H?NiZ3I2Cu z@paZW{AoSu=jnEX)OV+R7CCP8+c(-K*Q(_GQ+|x}O*;K#%RsN=%a;r;#0Cu=W*4t) zcMbg6{;#y#1Gul^llbd)|COxs6cR(MJZTpOX;ay5pZ^LlSDy4^fcSqC1ALuahno8T z5gxDpGdyJf(w_J@h$-OkKdui#3p4g&T#K#5*LZ0QewvY7r#VxRDTQm{+C z@&Ak7giqq=vw9f=oh(@(=@y>~-6YLUo}#0q)A`UbNgvwm2lo@io05k3=Ry3OO4{J_ z59GcX{7UvE*VU3SY6Of~&B{`%uL+T<7+#0Q$h zK`%w(eY7X_H$FS3~ekE%r>bV+^Wmca2CLW?VDvSvJQbaGh0vW>3FRo|I!4ql? z5P4yZ@IX7*MzpK6zHsJPQL{=s-ygN?01@7IhtO^0lJBVq};28k^E(=|Q#L9OGR$(z#m zNb>n6@+1MDe>M&~vt3D(n(H~xjTrc5;sIF(JG#i&Dc?f+53=1U{x~g5AIf=<`bEx* z-A~F=u!DH5UB=h457!tv54lXrQu+7K#N!(>@9T&EbsUY)BiEPlc~p#ISZTgr!#ast zKdUu5+TA&A2WxvoBGzN8mZOK^i&uz)4dVY-;FqDSWlQ{zcgR@PGDawV_USjHLGg0> za#0d5FGrTb*iYuY@+-$!AmsX_WlZX@2KT_%hxf}@!8^f|G5E=;x~R-Ez9-jfPf8u@ z?f(_NV!A-b=0s;cWVpoU?i1)}Vy5Wa**$=HQo@5bSqJuESZ1l@MR48h$GyFc?k!JU zbX@wc(l2Vc>!Gha-@HK}e@4*Q0hWQ;%# zmsps6Q_3GedT zRNcD#Sch{gtjRq=-qGP&bV&$5s7kaPYB-5aP`1!9Yf7CEI1FcPH?mq+IOMV3OMflL z8jeNh)CWCuPOJeOXVrke#IYV6H?j^0z2Y;mY3ws=kALnMz=1X`d8~l0o-0H_#|J=`y`+R1L7X^uX z?7NChzx|zmU^`11kayE;RC`jeD}R?GINZ^J&E3J6PmLA1S8+8tq4rwdXMa`L?ze+n znarIb8AI9^T_baDr479McRIr!@QTtaXX**dD3!8@o(S^1vWu-NJqDcP#5E>y zjR_7Rbk8y19K!}3vj^@tCgoG^?ZOUi1qO19E$>UNX`vC6x}1BbZCPI_-@)T{Kkg~N zpnjIL5!kEa(#A<2z?b*>wIQca8FG;)C zAA484nLOPNaQH|4R;nCcNs-jG2DC}yf~`7*3;ioxy7(Wo-R|Q8jgomv^eXxlnnQc~ zYIozI&OGFP==BYMs8;IT_|T{PyxGmOY!7lBt&&x^mD$qQ1ht+7L;pL~vG-W`ZeJQ|Uu6`I&ln_IihtzI;v zE+}L5(v<%@4ZVOnWn}{V0-J82r#Vyu^ z3p6LQkM2(BqgfL&HS3Xsk9hMY1iZO>x!aqYxcle_G}-WJ-ipRcwNnpB_DEbqsL_wemSIQwfS{04g~*iR|eKzDdJv|L0V z-cSGRjS2BQ)F`Y;25W1p;Y0E~J;>DnuLOAPr)zeM)#z0po=bbLx=|#`+1pl5|H=2= z(A@#m9`06ru%RUD#(Vy*eN9}`R}IC{Xh`mFTJ?ctzW0w}f^X;;%ea?* zJ9&2K2*tnX2g|76CuLjeSE+lXo*@=Mw;>D2xs-W$_Ez*)l4m!rJrk$cDnm>9PhxtO z*vRu!eRt+tq;GVfoUxKm(QE3l>0B%M#Mk!yN%o!v+yALQ+1Z{n*BPxXwG(PW3qidX;0Fll?xAtve&`gFcF<rlRkL&oQPZ1jqu={g8so8HWGeXiSd_bEn~(Vz)ve_d$x`?xxh~=nepsC zU@irITFKBq9>jONANXk{t5~NuCjABYT&K}jeF5+zC4)cu4&!P7exxLR?-iD@>I&dT zO4hHvkv7i(KT@*b_)P9U0sKhG=pQ{}8AUySA1SH7aE)a&O$L6Xq)1=OxE=yNaK^m` zZrF+y;gTNjH87UffFCYdu)2x+*}xB%OnQ?&Jv{~kKU^~ST-IS{U@v6*+H;`Q8sLXZ zih2zMrUm?P$&B;=1AO)ZpEj3_0_Ha0n$+k0yvA5-`_v%OE2k~s!u;lwq!d-yf%i;aW?8PsvEAZPb zAjb==e$%pjSoq2Ey&<$YzoNT*$A{A_;jmsNaiagJd=D~^y%6B!zt=S<}T=bf0*X&TLlhJc0c|8f-djJHRogA?_ZXC`u*lE?>A`P$GksqWRKJD z_XtSZB>N9|pYVP$`w~v~AL!D5z8q3`0&QN)OS$Br-0>*yI zkmgn2!}mf5*uPztg+C``Zr9pbM2>jBdf(j%Uq3Tgli$QR!;g{+p2W8mt5`$v>%4cC z*9dM$+l155NZieTs98938(4FIRyoj$F}Y*@2u{$dbtip6=f>5-8P?4G=lRY9ntiy7 zKCAgYi2uoZrJQwlhUIA9aOfw;+qjdw|1RNGA0zhAvb^e@*u_S^(K(0jRSqcp-0OA< zw|j}^eb`;|8u3f7o1(ehR|DIxdvCO9-u=0n_XXt}*8z*`SR?T&V@SDJvzl)QhZn&Y zxI15E+~fRl592!u$s2DZ%f?pW{&}%xxeo~M)=|Ry@fd}>(+a%FTZA|KH{=D5gEM1{ z?Gaw+Ffw14Dg9KhdMxjsmM}_w= ztmIki4DKl=kLpk@%xu) z*3*v&@2(x1_xhhT%ZdpndRTb-CJJxs7R_tlt9e_uYBIM&&OyGH=@|9G3yj7ezCgIW zS-;r-8{v-ME3E8GH3`QV+N8PTr)pOA%L=Znds|z;mzd*A%B^!1EXNovoXYU>k=49*v@3KrMW2p;zmGnRP0=A?Xv4BgIp9lSAx>+$eL?{S**8MGSS=+pP|4di3B z9ujZ;sv7WyKl0&^yWXJ4Nv4`2GXDGmmKAh}pt> z;0N?YIqy>7)Ma?{-eOJd5#dfi9(y8>tX;X|&?WgL3UBYNOQ}x*Hu4DGvhHnR3=e?o z2}4=MPeDfe(P?1ovrP>dV4vg5Jx1 zsX3Fl-V41KgO9{}=>8<$Gg+h7dP=tvbEW94 zJ6jG4=Yf9k3gw9J0qX+IOL-YM>yZ=eN1s*LB6P@v^}rmhV0!hPcyAy-?#rcqbxpt; zw?MNBW(eo@^O4JsjbB z^%Ko{cek*Xg3nLyRAaQ>y5D&Xvf?SGQ}}^R5+2V z*e_7m%^5yJbB?};y}J)N`dI4jbeWUz4Eh-R9eA2CPWf0^*Z);my?&@UcQDT3!!_&r zpJ`UFUyIJV^X6>fJoq2rPI(1$7#glQ5qM@^o8~r61#k4W!W()#I{}!Vbiw;#)*F&? zIlNf6-bHsWMK++3SC9NB@rI8IoM@}?Qm4b`x8ysG%Y}24`5lh!ss2RLK;iC$YMJ8! z;QKLs{$5x&yd&IR@Z2Xq_2J!c2lDfh=&U2yz=Mh!Abn@7fxz5H25j{ z|CivqPgvK&Iq@t-~fo$kX{R0?Ne5ZQcO z>hJ=`s1eTQKEk;d7(=q*N9iM%rAyo6g!Us-MZ$U-KAN;rSUZ~FmBE@b@o{{vICW^T zbQSRYx-;Y@>`q)b4^a-O#FlZ5ujQqeAmcwn9)F#b@&5wOP3We7Q!?)3y@5SoKHjkr zHM;ee=QL~4FW`@A@ZQI}%;UZkE!MZLmvaz1B*1ydSM>J;CA?W#4&GY2(kUTHn<=_!&rMR;keHEZXc^!=-(E%<|Q z=ilhV!4~*h9Z1HS4*Dg!8krBp;m8h3wPnmum%L#4A_bc%J>N_4ni_eQKohhL;Qzo6XmqOZee z!+(+Cn!9L`=FSNsm-qU6PM&!Ne`Tif9b^3~ESGxB#*NxL9UtwO7P~;}7mz+;Uwwx2=&d z3#`A|n(HD@EeYmsk@8Kw#<1qR@d$ezz7Jh;HFwv=_@FtIgYea4{RhIiJdeF4la(Lr zRBzPy&K{b)tNA{1U4E>8Suf=`hK>md!duX zc)%6--sSk4p&b0yKJY2+CO1NB+PQmqr-iZM`|o^N*u|gslGo0x24Ri41D_n(P(JzZ zh#9U|F@x`mFF~(hLnrT4zPXb+mE8aPme=qH&71iSvBU=Ue!R;bs){tZ7sRUqUNd@p z5qezp&3Ih&_^G~c2bUL5;o=O(79IUac+)9|-$$$fPG+-;W8CIRz**(TFFUbEF7^jF zDt_6y065s1WZiq>HE6H|9lakoiK4TReXXp&- z@OGd36dYOi9$$n{h3rn}-loflD;@$TEzOHrTqn?B;Iyn2Sn$POV*O{qX)3y7Xq)El zos3Nar)1r`nr9uvc|WIo3tMNj@z3}xWAr-_JXR_^+zGFs&+(smPvY)Qz*p{tubiwq zk;^sj>RRDF@+ELy6JF*CE!Mv-)tlD`oREVFMcA_aynh1QgZGscAa8ZuUDss~4Ec=RcS95Iom-aeE?$hT)UvI2??KPq z&R(609#*es$#0kSaBrNBEqj)IqZK*s(xu>4mSg?(e&PM?S6XafnPvTTpyob@4gU1k znm({D+r4R^Wdk;2!Q@`)Y1)c;qu#*K$?;*$;*H$?u^Da=&$p=3VtJ zI_@ES3}k#$RI_gQH{o1$7o~%bPxRKB_O zc!+&sx#-fP^tT(I5PS0MPW0dpRNUe2UQNE?^X$$T#O{wS8;@S-dCYG=?h@#^>va2Z zYv}(+%)@qKh2FFy#+7i#{fKtNjGb`D5lhN;LE5om;4VI#>>uC!+3ly>?M{&InO5L! zq3xp|Xx{AaYVJ>v_1-V3_{trRtoK6J?}8r0e(rc=UHZ$(x@Y_m{OfYPtM(BCy~CJ3 zLoS<`mx@#`-UFDURldS~{-7UA?b!4i1GZoQ_cMTc#iPyxrzArQRH&0 zl=W04>n8ecY=2;Vh_3j6cgZ32zQl($d@S|bfCZf;Eb89t;CIh;;6(`yBrGq0UxyB| znkS=wYLa?v12Op+V1Aa=p$m!0XQF@nx>H#RJp8(&=n4Ej2V0MKJi1xZF!AW986J_8)>%s0N&-fD`&h=~jHtr^hOv z%h&6RfP)U5vQOdY%o(9Mjo^Ru066guek-ybIYp}HQvQD~`K!Qvx4-5Vd?c*Q z6lC`ya*TX4^p_&!|6}3CBj|+ZWIN;=JKqzXfBUOaU*yZWy9ECD_;;E!Y^JpHnloa6 z=3H@u=3alPw4_^2BqVj6vhzACmzWL)|c$j{FpWu!OJ^@ zcL#p0j3wV$NxtRD9C@u55tBt;5?=UY&Dz@&x*aE$;QF2K@&0b%e0+t9cdU0_6yATJ zH&bS7Uiz!F&C{&K<&HybB;hWbKq!VgH+oj5x8^%LX`$54KOEgmi9JtFh2GDfkQi^#$4 zPr|~EE*{(k7It(oetr^`_v3lO`vN`q0Ojj_q4OBNlc^C-dK2sBKVuC7y*Q}`9f@A- zev^VJ*CVYl=*8~8ARqiXaSvn2JgPYjV>D;xe=9zBs@{_Fp5mA};3VHam-q3@l#azO z>;1hhI`($-BK(=uvEJ+W0QZbTFH%aq2u@xGI0fT!ebcgdon`7*&0ykm>^780_ z{uSNapWM&fjp59qvOM>xr?l8Pb=};(_W}D0{P~JJ<?#iOnHe=K8s;6mLtM@v^#iJmBUqvxgN9?z;~CVMl+8 z-s!IuNZ-i0;z7-QSJ~4%c`bb%yYKt?*7do>$VdJ6ZYUw9-KpO59(@X(!aFytR;Bl- z#Gwy;z{iZ!efLuO*z3o?=EX_)FS9oyQ?+-(;NIMw_Fpi+e-iGy-=+PZ{q{z^8ngTE z3w*N^kZUse&c8#0%sBiq5q?>j?t&|D^cC8uK)`5_eJe=8V;(|H3&e`I2{> zVaV9b&6LPg)tm55h0izgT(YKtE z%iL>~`aQ*~f|rqhcT5qvDR^@-@;(7M&mXK{5gV}2Ws>IRuTpwj)}6Nos+^N|f)af# zVaYhfi)_GuIYSRElX?gpRbP(`qlcDBJ@gi`xCS2vIyI1wPnI}0;IRw3(34EDTOlih;SL9e@9zfuFWY`;ISrA2}al z9Q+n!Dcjm{A7jSe?t7f?s_OFH8-GrI<5^A0se8=;^2Q(Ir}q}_HRot<_5s!yvW5FL z*UxbMVIKF9YwHtY1&eVt;Hy7RJe!T*{t$Wk?@QjqM=ilu--FMDT)(l4`Va8ow>ct_XG6XyPU_Cq3P!kugCBa8bg|O-7m>E-O^KD zd)0?I%MBTIkpc>!X5nV-0BF|Zy`u)N3a)7&SJ<^ISr&%Dd=o$v1{*R$B0 z+Dpz&*qe0>xt4ISl@_*A;ehP)##VxZ-~Uu_!d52h-X?70^hbo#PRZJUHw(PxzCrG+ z2HYlr+o~jP>w!7C3vLU*ZHA9qWR$|!yZl9P>H$t`fRhhSWv6l4g?@2A^KsgaANoA{ zr4y&^_@U3EUpjHR>^0#XhGq{_CUJ5q-yql7jJ*Do^-}apYXjeepkJQ3fp~wHE%86=~j)cgX=1lf(EcYmi!4o@eOm7xG zpsb8G*4frO=GWJ{fInsXTN&5}HQol(1ohR)#G@V_T z)Gxn84@>)v44PZOsV*e#mGX0BzTlZw{G8vCV`5D1#Xe5;J>W(3i(BL4MBTf52jA_0 z$8<`GlZw~SFPW{Hett!+H3pf=Tk6l>t_Og%Siyn@x$bu4`g!CUn#cNS1=gN>(Hl<_ zCv0K;;CyuJ-&j8wfbKsRo%RvB|3=OI%Q+K7rC_u=cJH|M@V-0iY9I-G9^!_X=ZTEW9g7A`>t z?&8~|p2%_?xwiwrlk>8Myq4BeUaNKBomtHJA(dMspNG%&wtC0WqyAd(uI2QfOB{_n zj?K|5>j=669Wd%*;5Rdl+oevS?G)}o+bvU&;~$Z)%x0`#`faxp7d*d%ws(;q@cg#h zi4FQbS>U}n1v@(m*i-q&4;!XnzXmT&P~Y|WaF@YL$XH8M$(5U8GKRW*`OLZO7n(DD zs-z7zbP_&RMM&kpjW*Vz`?9Y4fe&;0pP9e!o`$*oR(RnB1yjMD*mh8%s9 zTFted;z(Jk5NmSo-AI*L38xO_<|<8FOBaW^Y_f%p1wXIh z2z6|-MSg<1GY|iTd*1L@g?HcA@N+G+>5%@hsn+_dcwffdQs2q(Nqx8MN6>64Z6(dY zY1wa}xnFlKL)RU?OE`~EGCnzX&OGeQqhr|XU(w4QGXuO9D7>WIamOON)*=NPI^8H)Ebn_vxHlBxXYW^jwtM3_A(QZ1b*tJ_A!S+GITty$ zvX2pu?cg2cnEaPBk#iP^`DL8q!&*b%#G zu9pvEZg(Qfg-;g9YcE~4zm@#U3;1SS?_|HiqMwk{fo{WEsXqm*d&mhb}IXH@HvuwIE~2D zeFxb~LWwR_eov6`kPDVJS?>2yxOLg@qj2l8-^ZDaZzpkTWE}VH2dCBK3qF!DwJCGu za=(r>7WZFIo47?`1y@`fxfW`djT8(iG##nf>9eP3Av5W9G(D$;=sX~gz!$%`GN8zQh$j#T_q#-xpR3+!fR{BM@m-@w8 z>}bwiTj;Y<=@)Bp0{_ylJ6C}_`o(#G@~VNzO(Ql1oYI?>e=TL%#|fJ(bBzZ&al$6c zTqAWaev`t}W8Ku3Wy*LPIMo@Qc>`+<`Xv|rqQ8iuqa8pY<*L>876(Q{yjB+5n$D0&gu-aG94pxkhF!TL|qskmI^u*4}%`wIat; zKOtX@-@Nn_)@kp=?p{Egcu>X4?q7<@5y6+7k?h>D#J9=FvDXS5){Bt=Xg8caWcY!5 ze}GT2pE%`G$;-r0mw@8|>K7s3t*lkGfv*W27s>rWDi??0gJyU-E8+#I9ytGn};O7}X-?;}oL_yscDzq2Eb25>mVa?oo z0eei0%(a*D7IJbs`6B68(|!#0C3l?CpQ`N`>`U$g{`+xag8|c6C*!?gp9*j0Zb=Jd ztx3wSaY}xh;emyc2W0>7z#@1+_OJSe2NuBt$+|ZTU;D@&_<#~Sp>kN6H7c)SjhP5u zE0cJ=0$$gih9hJ5+KwORh1l7+OK@+)&<3Mb?@ z7x_Jf({teT3m>Q1=O%HgX71oMWlMHTpE`INr~csdVG<{NefN?sINb(LYfj_T2=4Nm zzr&Q$PnF}E-9TQ9_?R59C2euef+Qc@4cz(o>g;VSu2b^sx(gL-a%2VWsQb}{&nO?; z8ec@-2t9n)|Iw_!kPBN%F6?iQBd^7p`!sX0kGa3GKe^Qpl^o01*sG6&!y;m!%QS2J zVE6&L>=`8E3d)O-U3B7jWSAHUy9%z@npq*0PnK(s_P|?uxnp~wf03~%=+p7YST^zT zXPe2jlkQkzjjsoMbO$lL^a)w>p!8xh;Vt}v-qJ6?9~yrNxZoGi)-h)y?~HBy8sThh zz(?e|`7Y$`H%U7GQMgZ!Xa061f=U+{HRyh9??UXB3jT8Dr=Gc9i!(QN{bnXdIId*)M&brqib--TM^(m7$c}mCi z`1|~1&b3`|g3ojARXBO<+4S?01(7>bd_uz7TM{>d%OKdK1sK6X$?0YsqfUJ<0aO&a-~2`mxlS@<4Jk zyU4BO0{^Jro;7WEG5Y8EM*tM&B!}>k)Fg;@||($aGB%e9Yuq2=x~|il=bplMHgp8CB7U!^oZG- z%$4ZH6}pqMh1fv8U*zwpqP^TxWswV3`(Q_)pV(7nkqhpudt1=m?5T22P|AI=Dc|EC znV!86p1B=e@}iP|cgL%&hmO;{iA(68XFC=%&kwNQXo+whNB45i8}bCYU@`fv?_1aJCO=I-A77z)n}@JAfnLdf5kB$j&J}I!(;dOyQA*Y! zy~-i{W7p@jLR<3F%U*&`(ntFpI9-p=Cu7W3e6uCs^dPxla_E(FC0~>4ev!N~^{Zc2 zxnR9m)76@rDc|o$E*QNsbsxE4^vaTp{PkXy3r4RjSxKBUN9Bf{A>j9ACHp%l<$ABm z1tUL|M~LAj`uj~&zd1wcCu0K7))--bD`jN931~(c5(TG5IYK z{6|U;-EK))d*&E;yg|E5XosxP&KV}#A!p@b`ZURZZQ^+j&yT3**7l3>Wt#mw)8)63 zXQ&~MeH*^4K@OaDvnfr3BF73#BIakpxgZL>tm;6c`e8;etHUFuyb*tf@eIF@`cmZXA#A^2_b!7+rc>WE3VZ(m-&a%wycqFYHScamkF(F(Yk5HJ zv$o_u>)YWO_L|Fm)|T97ovgD@nYmxaK7^~07s@NH2i|4;x3Eru-juncd}LtC$ISby z$lW+4cm7`7#mL-NoN zzUXtvY+xX7a(}8G0~akIOUGm$N1a0s=YY!LSl8k^_arwV-;;TkYp0+qSNm|NJNMp4 zp7Jm{jy5MKrH<3VogAIa{aRzD^8U-%xh?EJeGOVo@O9-d=6&2U=Ijw)S5o)3V80(= zF1DP@++QNkV^gJL!h6@*z=1v=rtlv0$#;A>m!tp3MS(-y$DBFTy%FH_061(0=kP54 z$$ztN#A?0=`aGrRqkpHOmv!wgfccynTkJb^z1-bN83vBDZPq*3;Ux>ye7f)S#io9s z_G4H#AhW%Y*-w7Ty6Sg`fARNNyS1bZUbw;IL*Dbdx}?!_7%`)FE9QobI>gJKorOvx_ce}-P_2scV{ovVB-5GwPr5A9kNER z5>7qw0^eU*qkEIDZs%MaWb!&}ZudRv9cL)-Uq^nAlJh`VTc!RCx4dr1av9_0LnukG#x2gYo1?Uc+aZpuR`( zUVj~5dl~uHNARh)((gjz?AOVQAV2MIQ9jT6_3WvukZX|szOK>ziBCR6XX0;+-$h>I z5+9a~FUH@-xYsCHGTv~mYGFO*4ZgRbB$umT#K9?c4>5fdoDxZ#Ja8J-1t%Arru%Rt zK3kC^nX|d-GjMDGpE6ZSKf?WU5peb=;WPnfa2Gg}fCKFroA)qrh#DI@t6ky-K99aG z|B*}Nnt0agy&9g|%D8G7*H{Hd#ZTiGF|PZ38WB%9l}`|tXXATigWF-g%V`8&J3bP0 zne_(vX{uD~YP{skXD>dsb!ae`AjrNj?d{KIFH zk#+ui*Z4W~@f*MozU|DRk6#FWGkyFn+r&QKukkhT&Emu$!;rC=@CEx>{k@?wM^UG8 z6>{ysZSMb#IMmg;frxX4yxdiMp-^_X$Ysd}NaSvM-7>iG6 z1c{pqoH+4l@rnZ7t}CFP6|Gz)rfz&|vtCp|?zN457H#;B^_pGWgKsgK_>RQjyQdJ1}g&khzgS& z0kPl$^0^Jb6-X8t>!z^ z9>Qtkd2^NK-O(gcwHeA7}PQrm#C7fL9Q_v4nrT{0|W_z;DVZROE ztCHhE77x&7)T|_&SxGq8`Ed9xHRrbb@waXRPO{DRWShf&8~2%6tjFGuEs$;ENu1+8 zoWtmS@R8p(d;4eL(;?rZ{wV*RcMsQm7b(9VcP62WC#~l@1EI;YpQa0uyk?#a zy-S_5KZw&#KOe;NuJux$hq^q!hUZ=DH}l-=^89X|ch;Ayz9s+p@MT^0-?dKv->A#} zyFRD?Z`5V~U7yqcH|nzg&gTL-2&%r5^80D6JP)PHI0XL(lCnGr-Eiuhr@uev)`V__ z3WhwFJepU}*&6U*@z4pJBRfInMmSeoo~-O#=#*eYmojao%;{|ZH@#>MI_1rPlxyqa9@2KB zA7A3z$KR=QspL6%UF7#wd4``ggR=#TvT!AD`b_E|Pk4=wYAM zr6#oIjEHV>4wxtOlTj%Ab7e{uZ}``uCRdkd>1=B4Y_IwoV2qb%=x}cIcuCjV{V8|2 z@fnlte(Oc;`YFG6)+gXQHeZXy0-1=Rc?IMD0=|cqccXj@wRj9^SPd3 zJnP0e*J&Wv`(u6F`1&Pw;rc*r(|b3#@dexLzVbZkIJBJ~l>C$&_h!E==WGw|Ayb_7 z^=v1O*c5rb{x^P8bP6;$^SL#nEmNL@7QL$my$er8>ZMJ?|L1*vhg819Ip&`u~%_r@ZtfRj0im;b&W-OJQ(KCc`1<5IM2GIb{9_mk<7ut~@E44T$%EAAUdy~LPl<+$Z^8CE$7xlrC zR`|33h8wYgL3yT+UOeP$$tV0y-Pd;C&fkrThwMUprY>jqKo?!o-`5vIzx4G*{J3i@ z@6;E4En}nb$6op|?G-=&les@NSAVZtG<*buxHzup?ihp1EcSv#Z>WZ7>Dg2qR z#4UQVKA_WHp5-4ccu}3VB+vTx;gvoi^^A{ektTf)`Fn$$!IW+_tyjFGeqXU^lHeS# zG&{(7Z7Fgtr5~`-w3-Z!`3+c2_40X!dcI&NvCk^TXoM50{UF*G%J%$b0`NyS$!D2< z+x;^BI^DL*yS5?wD&<}5Nn4vP$1HizH7nay?l^9Q9rNNc;x$@mYr5En<>AZ}yQQ=_|nR>on>c78}J0+*;yih4;QeK1YwNlRF z_4DN%oR+nXZ_0V)EUON^m@~4JywSdJGd#8fc|+D-KxUWk_&c7Ke2lLeQn;SUv*0Js z9{#7YHxt>b&Ed>CWUqQ>sTDff;jm^e&tP-SxF`uBd;E?;^jK@4uD56P796AQ)@5MPby#zj4K&uxzcx@@q2Y{ zo7B~Mv3VnQfio>%IYvA48@yO7?Eo;z zuQWM#(TCPwE9cH$v+BlZZ{C;vls1efuHhc@W}@E$#zYC%F08*XRwVH&6j8><`Fo7Z z$d8cF*yBmsM&7ohTk9u~+5q`OeGdC5E*rMC4Hsl3A&9_xGr)-sHY=Hmj zPur>o>#y)_Rn3j?!}b0cjT`Od^^!(6+An}3zkSiQFYbTCE?UT@dvT7ikWKQCrEb-c z4(~{-$`M%RP*ICrl;_Sy?`rZ-+UX|jG&qILK-4twJ>YrhFWX8VB>6j{-&bd%%lBU5Y}fn9fBcTq}{XGzCr zSVpO6dD>|A&vYI3b@XXDU-QwUoR7*`srWp{>@&_l#g{!Q&pxhQFLk29d13OmNzOb2 z)nH63Pq>j%_K{rs6*^g-YbwtPr5q&vAdHnj36 zJQj#njn?Hg^2u|2zdJYZduHf~CH2y_b&r1VJI>joK6e$d1mnLvZq>q<(VVn^-mw2B zTWqF3=syo$&M^at%-{)VY`FLWE!pBT?D?U;Ylpw@+mZNkdHyNqr*Z})Z;;ICSx|f{EBm4kJPSG^jVBd9!cu(4?E$=ccGc0BeZ}nulTeQ*M4=H z7SPNDwx1W7!0#fTI8BQn=g1B@A$d*8h@ENP7(KZmB7KMGzTf33T9mK)zpb4ObX3)y zz+W;Sgp|hA_>o%czB)lsA(_eKlSMaZ!s02lRziy{N`{#iGBuf*&Ib^Vbz?&iEyu%Y zpt3EeMI@~DxUG8tq1JY>64dJUxUI1EwB2MrOzBidnW>9;Yb1v`A z``^$1{eR!fy|qI3zVkdQ80##-o!mJA{VTbl&YA=#$5)K}R`Komk5&rKTt~)lQ@FYx zT-ACjbl*MRDSelDt?Nb?3l3x)yWjSe?0d)3T;GS5dE{OCuI91lacm{#ZR@zuWm)+3 zC!jxma~6fj6(ZF}P@ zzH)d$&Qw76$UO?SFLI66r=AOi<`oW@b3MWuJ}OEN6Tc z>pyh%hHj(4HdelX!C92hsJZ)Xn_en7Z)Q`ibiV_=aaQoYi380- zUtK#mJ9t9pA159syvUr(IlYR;;9q!EXw2caQ8#0mwe{2_o!Mj2c5<8IIj_fkeB-t! z9G-*6E)385KqdEYF)!#=WLbE_xR?8zE0Ocu#iL?V>dbxozFNVb@A8-A9B)_n^?hni zkAGuqpU@!l%pHgL0Gh6^mVR)q;g;z*-|%Pd(K$i=ZqDh)Zu8J~7q-#9Q=G?oig^(E zq51l9d8#=AH{eIh8+aVs03PS}DW5643Vz{DE$=vky8bZVkwKStzU6aoop{LI{g%QL zy0o=L^yQ)*PpUtILOeC$DwYY}=%=KS!HS=cMpY92VZI$y62*NG0-KIo_$9gb~SGtya> zvnSTS6TAF@Ar=4qiEnPxJHGm@oW0$2hPimh2M_A|0=oWF zWH0CJ($Czv*9+x5Y0XpPUHY6}^oxB?Z;iK;v-xjC58lfe9D44c<;qd!FkE{Knb?I- z3te5&MPEf1t9XIiRd9zIIQ3jSD(9L%J>crP(6II8-`89?n>C0%#(^X7;>L}il63`< zuh?VI2s{97&|mJe09V_D&N&~ObHH_fM^32zid`UgVsNgooLj8#Ut`?jli-^$hBLN4 zJd6Ebv~S{<$XH|?s(qhV;wZ)QGS|BOVlwE^oI?5;mO8H($;VGF)}nDTyn-^*Xh-Nq z{4Z$>o32M_h4J@7OWYfWzVq}zQ_NNUKJNWHrtEsx?2*rLhdVN!xj>GE)B8oIRmprp z3!FEro@eYk6}Kt+qYP*FpU$U6%1^QJ&K3JmHlAbirXQcHX>@#roR93(scpQm6(VC4 z{L!(`=K9#P->v+f&0>2k(`Bw0=?zzjjWW_Z%v!b2{Sy6Wp51$l@D}eJWh2%--iv%Z zpYbwQc&m1Qm5kB$*{*cjR?qh_o>9-pqkYbn#Q_9;O|Ucob`G1*O@Tqyvw|WzcFUt-8mkSI0|x>yYCWZ!Dai2_2Ta^ z-{>dKu+}^zXJU(uguF0ZS8X_>VcZq5hkg+|^X(ePzJ$+rdJNwN<}mY?j!S+6+!pYj z$PeLBpIJxzOwsT=BFl0zLFs2VI>q@m{(X@Rnoq}X*YuC<%-xLwFPOwG^AM3(B+Eq_pc71Z^7kGc+lo2a0o6%{^)q+2C+?T9y11YoyLR01GXIccA<{=W76vC)yE$5dQ-_LqsJUwD9cuyLSk&u3`&V)1nf;~0hYp|dM|SG&)KCz$U_ z?tMT;GGDBj;U}FhF)vHU#44cy;!`T#CGTDp>zbirW89gOi;W2^FTV9qUOC?fq?}*s zcTb6tsWPuk#>filQ01JqRUI8_t)s>x^U;}=H5;)#ii~wi42xJy?yfy#rivr0@z=Xg ztMON1)0G;36}05^f%nl;+J3Q_-Tr&SL zSx3MhxNof7x3ja%x2uVDo#(Q_W>fTAIeL29y=TypOX)*mz|(m73L|{x&n89 z!q>=&7%>L%N$yj8oYQrVj&6>nS=XC(SBkPJZn!(l zU8IFPAnRsAOF~EaG$b@7eJRXu+a#fqZ4Hb4GQ6I0?;2A_4^PhHWu6Lk=LTaaoKLNX zJD&L~%w-vKS#ljr;>!-+b8% zK2hVnc?zy=_>T+gFJd$1=tP&f;rU{ChQHOC1v*UYE~73x=_|O;Y8$wtM|=eQ$+O`L zW_yGNd_%i!n{7PeW3309$SrW+P+eKL7yc3+xoAIj6TCA`e|50NB;UzTS>I0XT_{+0 zq^~*-pGOS;)5Nz*xCAH-UHMn zu`{JFcWIsJ+ao+@bP!K8eBVOWKg##}wJjiH^11i-IepFbQO3Gu%w70h>%0+5>!_h% zQ5~p#-1Qpf`Ml^oWtZ7^<0v^)w_D%gEqc;3@=cLjy3UT&G&-2=A&y;bgujZcnB7(8 z!(JFg=7lTJam<5?L!kTeY0l=|91kiyL!am{?hZIde}slTBis#;>(A$l(U{yZD|L$B zH+&;9YIhgsOdYZ9@S_>}tNg7u4DLbo3QsHFofsQyUC0*foif4CsOPmc#CkpAn@Qf| zmwHY+I3KQCn7eN_H!lT#r;e!%64Sb<#ce;IoZz|GFfwvyk@=e<=ZnCn%&*werS0pC9e*C05`Px^nwV{Qj)&Gw z@(}S{WHSE5&Z9amf2H?!i7gLbx#;$q3i|B5Sgkk7o`k(`ybj;7R=9Zv>q*CM_g((_ z?Y`Z_q4&Ioy$v5<;8p8P&fGflDfl5SGj$DD^Uosaq}=w`tL)g>PeqQA|4ZkjVZ~G9 z8z*VlxULip`=Bu;FR`s%{{#&@!hRi3D{H*;dE%(hYxyvGLF>tC*49M7&eVHNHgdMd zMz>lMALSmz@Y}DKiw==BVMhBwVp#whhWI2+F62bXjIaxon(bTE5F*42>@!h6WoKa5qtb2HB+OW(8qdgtR839ZTc z%sKtn-D9lR*zLbE25!2z6=Hqx-ERtLVtdatl8K;X`NoLQMzE)n8Jgs z)A%+rPX&8RL`D{VE6pp1h;wOLU0d z*Fzt*yg77_^hi;jy%^9e&xCPy%n5KIfMUGv+?4EqxbxoJM~UxYbtii-R<7tAF?lkbz=SfV&nK% z%d>m$``?c6?`}QMbmAuFO}5 zuk7`5AJUCEeWUaYchCCR596zwDfa${4|ua@YwkJqe+MqAtQ@^(&(4~Pk?iQEXS1y% zLqntG;%je0@3RK|hcWh^J)3nsRAKDB;S~R;;5hr3+3R)cX@0YRU4Gy2$E@6k66}4N zeX^gXPaBPzicQqFDSLdD>+SHb9=WV$oHCv+`plT`|Cu-FZ7y>y0-xYa*29U@o!xui z&pPBD@#Cj%{k^UKxtEN%fW$h9Y5C^5c}HIo*CL*y?u{fCyaSudS=anQcoXAMzAif4 z#*d6&@Q5CsIeazvysxrc@Dm()g8L8ul&$g7r|R)?r<~wa$~gFv`_eW3mx2FBi1Q2n zm)ZDdy-(x+<9qL&Uq;-=EB#UTse4&hm-#uV?1D9}F8SVJgfH7)>AK=x{rBbUzn3_GTlokh?1}QIxmWi3WdCNqr#deFqw@DZPu%kK@azqz9p8^NNVQ%_ z9QXyHd99lcwM@Pb&6~#SnOy_y@jR?}ecOTm;r;iD*T;HWiS@-4um5T3dVK8PmEiT9 zJzTg~j(r>6a3?%9wg@}J<7ICxKKl$M3x{{>H6@YBqZ^1tWBbW@Pr;3cjaFjzDn5MY zo{Hfy_NR#5Ge`Clv99aoPE$3<26x}V*UX2+_k5R&f2LxIN)P4ad_g;VM-ET!`#62H z{J`tf{jkKap|Q)i!IR_YDCO5pi(7y@#gkQ(La@m{pi}$DeI8-J+AN@ek*i*X4KnBoXuI&K6Ju0n;7^d z9_aJ%8BGIq(_&}zN5#x!|D?2a{5RthH8Y0{r@iBscqY`IN5-#h=AorJ9*!@nZ0Mm| zN{vZmuZ%_FeApKiYD}jX%U?XEp`X96F^T+FW19WexyRJ8XzG|cx)@VV*N9G0bK)IA z&dU4~d*3EL4{Lf7KfpK5-E*(@`@Y1!VI7|*&uh#NYsY@sTchM6Hg}m`>mx2Rhqb<| z4+BRoDtsCpj+~$mS{@Ym*ERQGpUNG(%J1?#sM=EgRo5;RQ<~BBAbu9IvJ-jD-l8F; zbK?1968$OT(mosgV4W>DhI-yr-6Lbbk>l_HYZt7g^S*)o!17JY`5I3X{_yUzYW=%> ztG&-z_VWlWvWG?cS&hS8oo6Ms6&~8gnpx*r<`FpZb=p1xg>BwR-$ z97=@3(e$7hUt=YE`{Qf*_9l5g#IrxTDj7-+HiY{7 zFiF~Q@`mF{OJ(gHNH@eX17uBvV$tv&R>TaY&15LH+A>q=P%>>sV-aheX~r{YGu~@M zdgDnm)gKL8@~Pj7txoruMwqtFr;QBH%Xx-UDJz+d#$#qrztx*I!|_J-rcqSIK0NCYH29;l9wSek&SV6Y7sf%-$$1ga$0#DJvGSyA+Gl!+0_z zJq0_KnT)RPOZS+)Aqqt1n-lb-I0-$2xrzDc zF=;bnrIQ)U{w|Dos$`o1Sl0>hJSS z%GhK46?I)qnX>Pjs%$PV-<&+PCn9v^V-21C5Q1!N#V>=Ej!B*2cER_Mkr)2sQ?T z!KPqyuqD_UYzwwG`I`bwjZML(rl#hmmZsLGwx;%Ge{-O@u{qe>)ZEYjbN$Yiny;YkQl&Ezs837Hn&3Yi?_4 zYi(<5Yj3BE?KIy`)$J5(=hOA^WHP??6Pa`u&t#|{(uxe4tJGJa5W{Hufu`B{jN8FO zH{aylc`g9QAK`bDBsli3>kaj%tX%#GwZeIKr2C|P^U74BFB)4nZ~Yx>2Id9o{q=su zXsN-J5MZjIFVvqF7B_a%&K1;m38{({jis$*EYxpW(%bna{9z>_*gTeiha#3!u8T54 zC-2f;G%j-BUrnQqynjQwjx@=e4!115b!m6kCsr=)UU*BldEK>UpA|~1OvF>~QajWf zPY$Z_#Ass)K@U_xuYnasq@wNd0y&n?TAcCWmC;SC?}zGdMJ zUDpPBz~q`}G9DWMqwse!D$>PF$IVDImFN$_w&to3^Or=3&|dW+)3}LxAEkbgonNN0 z%Xr>=*Q6Xc{h(=d-)Zty)6I!7)2N*6c`q}k2Qq(tsDEu}FtsuS*`?=PHP=)xf&r2J z4$W<+%>chEZ6eB5wYCpoW+syHu$4+tO4nMSUyD=6FQ_9(9d;GrP$mWS#Nx4eNIIl= z@zRE2K4q#o>kXwLKJb{14pz1+lUp5T9GL#p;!7Fv(jth$vg6iJ};BP9hnfj_E#$sibPN+ zDU^aq1eJr+B&#c2rU| z6AOpZ*6MgNx?T#1pf%^jQ})|cIez@)VQqhXmZ$KS=uNS;#J-VtNj3KG5AqbbbRkLX z1hL!XUDA0Zfk|GG^=%}v#YB%tUXkA-2c$mvCNRk_bzVRcxhJ|uzDb^oNK&t~A@7n; z-lhI$(Nj-6Wg5?u4)Xpo>6g5pB$fZrG|nS^l+;MNj&vjGc2bJ;CDInsL!{?OWlx*N zC8Pl9TGAp?1@^^L+C^ut{WWj&1TJzXcz+yKUW^X7#%7*Q z{&_BqPXyRNC@s=i&AqYU2za4B(ObWGPRsijc^6rXIXF*=!e|VMhbg*V%Y^|nh6sdJ z$YqRiYJ`;XtiV3%lzA>HPp2#1J1i7X3fToonjgE?{CL-`E0=cN)cxs&ODYS|Wy{ln zcm!!`BMdyPM#3SqELb@D1uR!v1Kij--&_?igO-O*y`_Zh{wW~7&UXL=kKVe4k2(o zPbasd;1KN%slgbIn^^n}vG_`j`xJ$h;tb>2;5gY{n<%Ntu1@h1CTpa&T%MIu33dIL zeu(^dk~EL>FmrbU&wQICA@<2Xn8uP_Io~9t@WTXjYuj{*C}MM|t%a1UV@5J?+-gxi zA!W8itQ78#QbE|jc{a}OFT?LonaYOrf{#mRzYrhr^Qug5uh^{($a+g@nPjRjDjwzl z&fIn4E*i#n;Wd$cn|aE3Dyi?PBK0ZU+LMvbpBIpK6Y|L9DSvl@AMustyQ&vj5}J{; z{i~*-6@jT-WX;7W3Uk$9+Dh5}Vx`gaBU8!N8+i)t6&+zr`rK zv#jhRv&xm7Sj)G_wb^W)06VLy7vEjBsl0QRkhp@2Oy~pNBOew|hcD-I?Je2tVu5#7 znT6%o@wo%|91WjRzl?o5X)*Bra$Y*CZtj!z?{$5}J?h!&-8SQ^6%WkZUYXaQXUVpq zJDZh$753N9rxTofYqP%oK~<}a`%>m{^JlWzM|qbqEYB}vw=Mne zBwzLNY<8baP-m4tlAllaRpq`EaDu$sS7fs?#=?FId<*d1z-vpu9|OJvctXQF3i0|P z@ZX%H{7K-i0`D(UUdB=lzWuk+zQ%h`PvKbdaK3;%m#!>6hccEG!0Uhu{)>(EPT)JB*{!<#w0S-Vye*Q=KCH`6tN&%- zhpb%t)rIv>06zh|)ZElU%M-wh@sk=q@ao=dc14NtEeE~?xT)b&+Lv$ZY9}Zvwv9?!yw@ha9i6y}@|w+r`_j zqs|33e1~0sG5KHui;%DGJK1cXZpZ9)#>%Elj-voDFl=&X9ItZ%ZBLV+pmO_0C(d!^+}trt56c(M66Dc=U%1YTkD zUa>K)Adma|*=!T}3;AE_xD$9S@RQJ@@NY31*h!vO4`s8Z=J*is3!chme?t|8ZO9zG z0{j)=o9sC*Hb;VwiW%smr?c7aBJ~J-4)8SaV)zjFV&I#BtFac*hkd|zmMAa%+ywj} z@RxM^CFRm%MgoAlgD_bG<_ZfK0zIi+I1B6@nzr}C_hKTr|^rSf8d*e znBkW81Z!= zzU76SjG8u%L%_gV==9L;8>t-^VePx7~Kr>UMD-_%d}Ee=^&TtpQZ4Ai%_*ZU=2WGo@#Wf2Hp zvZT`twABMKjAg6vr!r=^pB1XfGEIcpTjpJJ4GR*D{uWX*ix7cpdK=rqet%1-xvjl< zUf?bQMS;7VEP>WwW1zV{(A;V)l)faRsqh-3OoA%zYEN=aurb=02&^1vOrY=UaqiMa zeKIZ~G^5_?TiGkIHKRVIKcrTrQbxV{CmbJ;AQqXW!g_)Y$>=JaL*S`CJS(DOH1S^l zge)W>k`cHjiFw>5KlGp5ej=h&K8a5qe8Myiehd93ferhQV(L#mNqxdvL-;vEPe7kC zZ}y*4pTwagpOE;$*QjqCy<_K*hm+6bUFw#6FOu&?@<~j=`E%O6f)7r *mut u8; - pub fn syscall_bls12381_g2_add(p: *mut u32, q: *const u32); - pub fn syscall_bls12381_g2_double(p: *mut u32); pub fn syscall_blake2s_round(left: *mut u32, right: *const u32); }