From 84a3819a9f4a894e7680bffe247282ae682e7692 Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Fri, 2 Aug 2024 18:18:43 +0530 Subject: [PATCH 01/41] update: settlement-client: ethereum test cases for conversion fns --- .../ethereum/src/conversion.rs | 64 ++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/crates/settlement-clients/ethereum/src/conversion.rs b/crates/settlement-clients/ethereum/src/conversion.rs index c86eb89a..0ac63037 100644 --- a/crates/settlement-clients/ethereum/src/conversion.rs +++ b/crates/settlement-clients/ethereum/src/conversion.rs @@ -1,5 +1,4 @@ use alloy::primitives::U256; - /// Converts a `&[Vec]` to `Vec`. Each inner slice is expected to be exactly 32 bytes long. /// Pads with zeros if any inner slice is shorter than 32 bytes. pub(crate) fn slice_slice_u8_to_vec_u256(slices: &[[u8; 32]]) -> Vec { @@ -10,3 +9,66 @@ pub(crate) fn slice_slice_u8_to_vec_u256(slices: &[[u8; 32]]) -> Vec { pub(crate) fn slice_u8_to_u256(slice: &[u8]) -> U256 { U256::try_from_be_slice(slice).expect("could not convert u8 slice to U256") } + +#[cfg(test)] +mod tests { + use super::*; + use rstest::rstest; + + #[rstest] + #[case::typical(&[ + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF + ], U256::from_str_radix("00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF", 16).unwrap())] + #[case::minimum(&[0; 32], U256::ZERO)] + #[case::maximum(&[0xFF; 32], U256::MAX)] + #[case::short(&[0xFF; 16], U256::from_be_slice(&[0xFF; 16]))] + #[case::empty(&[], U256::ZERO)] + fn slice_u8_to_u256_works(#[case] slice: &[u8], #[case] expected: U256) { + assert_eq!(slice_u8_to_u256(slice), expected); + } + + #[rstest] + #[should_panic] + #[case::over(&[0xFF; 33])] + fn slice_u8_to_u256_panics(#[case] slice: &[u8]) { + slice_u8_to_u256(slice); + } + + #[rstest] + #[case::empty(&[], vec![])] + #[case::single( + &[[1; 32]], + vec![U256::from_be_slice(&[1; 32])] + )] + #[case::multiple( + &[ + [1; 32], + [2; 32], + [3; 32], + ], + vec![ + U256::from_be_slice(&[1; 32]), + U256::from_be_slice(&[2; 32]), + U256::from_be_slice(&[3; 32]), + ] + )] + #[case::mixed( + &[ + [0xFF; 32], + [0x00; 32], + [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + ], + vec![ + U256::MAX, + U256::ZERO, + U256::from_be_slice(&[0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + ] + )] + fn slice_slice_u8_to_vec_u256_works(#[case] slices: &[[u8; 32]], #[case] expected: Vec) { + let response = slice_slice_u8_to_vec_u256(slices); + assert_eq!(response, expected); + } +} From 0c6d8686b3d58bed8959852731a8a33561952340 Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Fri, 2 Aug 2024 18:55:00 +0530 Subject: [PATCH 02/41] update: removed .expect from slice_u8_to_u256 --- .../ethereum/src/conversion.rs | 37 +++++++++++++++---- crates/settlement-clients/ethereum/src/lib.rs | 6 +-- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/crates/settlement-clients/ethereum/src/conversion.rs b/crates/settlement-clients/ethereum/src/conversion.rs index 0ac63037..0406e3fc 100644 --- a/crates/settlement-clients/ethereum/src/conversion.rs +++ b/crates/settlement-clients/ethereum/src/conversion.rs @@ -1,13 +1,14 @@ use alloy::primitives::U256; +use color_eyre::{eyre::ContextCompat, Result as EyreResult}; /// Converts a `&[Vec]` to `Vec`. Each inner slice is expected to be exactly 32 bytes long. /// Pads with zeros if any inner slice is shorter than 32 bytes. -pub(crate) fn slice_slice_u8_to_vec_u256(slices: &[[u8; 32]]) -> Vec { +pub(crate) fn slice_slice_u8_to_vec_u256(slices: &[[u8; 32]]) -> EyreResult> { slices.iter().map(|slice| slice_u8_to_u256(slice)).collect() } /// Converts a `&[u8]` to `U256`. -pub(crate) fn slice_u8_to_u256(slice: &[u8]) -> U256 { - U256::try_from_be_slice(slice).expect("could not convert u8 slice to U256") +pub(crate) fn slice_u8_to_u256(slice: &[u8]) -> EyreResult { + U256::try_from_be_slice(slice).wrap_err_with(|| "could not convert &[u8] to U256".to_string()) } #[cfg(test)] @@ -27,14 +28,28 @@ mod tests { #[case::short(&[0xFF; 16], U256::from_be_slice(&[0xFF; 16]))] #[case::empty(&[], U256::ZERO)] fn slice_u8_to_u256_works(#[case] slice: &[u8], #[case] expected: U256) { - assert_eq!(slice_u8_to_u256(slice), expected); + match slice_u8_to_u256(slice) { + Ok(response) => { + assert_eq!(response, expected); + } + Err(e) => { + panic!("{}", e); + } + } } #[rstest] - #[should_panic] #[case::over(&[0xFF; 33])] fn slice_u8_to_u256_panics(#[case] slice: &[u8]) { - slice_u8_to_u256(slice); + let result: Result, color_eyre::eyre::Error> = slice_u8_to_u256(slice); + match result { + Ok(_) => { + panic!("{}", "Should not have passed"); + } + Err(report) => { + assert_eq!(report.to_string(), "could not convert &[u8] to U256") + } + } } #[rstest] @@ -68,7 +83,13 @@ mod tests { ] )] fn slice_slice_u8_to_vec_u256_works(#[case] slices: &[[u8; 32]], #[case] expected: Vec) { - let response = slice_slice_u8_to_vec_u256(slices); - assert_eq!(response, expected); + match slice_slice_u8_to_vec_u256(slices) { + Ok(response) => { + assert_eq!(response, expected); + } + Err(e) => { + panic!("{}", e); + } + } } } diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index dde57309..26d4608f 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -125,8 +125,8 @@ impl SettlementClient for EthereumSettlementClient { onchain_data_hash: [u8; 32], onchain_data_size: usize, ) -> Result { - let program_output: Vec = slice_slice_u8_to_vec_u256(program_output.as_slice()); - let onchain_data_hash: U256 = slice_u8_to_u256(&onchain_data_hash); + let program_output: Vec = slice_slice_u8_to_vec_u256(program_output.as_slice())?; + let onchain_data_hash: U256 = slice_u8_to_u256(&onchain_data_hash)?; let onchain_data_size: U256 = onchain_data_size.try_into()?; let tx_receipt = self.core_contract_client.update_state(program_output, onchain_data_hash, onchain_data_size).await?; @@ -135,7 +135,7 @@ impl SettlementClient for EthereumSettlementClient { /// Should be used to update state on core contract when DA is in blobs/alt DA async fn update_state_blobs(&self, program_output: Vec<[u8; 32]>, kzg_proof: [u8; 48]) -> Result { - let program_output: Vec = slice_slice_u8_to_vec_u256(&program_output); + let program_output: Vec = slice_slice_u8_to_vec_u256(&program_output)?; let tx_receipt = self.core_contract_client.update_state_kzg(program_output, kzg_proof).await?; Ok(format!("0x{:x}", tx_receipt.transaction_hash)) } From f77093b9889cef5a750d3542adc8d208bbdfffbf Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Mon, 5 Aug 2024 11:52:51 +0530 Subject: [PATCH 03/41] update: Eth Settlement client: tests for conversions --- .../ethereum/src/conversion.rs | 149 ++++++++++++++++++ crates/settlement-clients/ethereum/src/lib.rs | 84 +--------- 2 files changed, 153 insertions(+), 80 deletions(-) diff --git a/crates/settlement-clients/ethereum/src/conversion.rs b/crates/settlement-clients/ethereum/src/conversion.rs index 0406e3fc..f7597a86 100644 --- a/crates/settlement-clients/ethereum/src/conversion.rs +++ b/crates/settlement-clients/ethereum/src/conversion.rs @@ -1,5 +1,8 @@ +use alloy::primitives::Bytes; use alloy::primitives::U256; use color_eyre::{eyre::ContextCompat, Result as EyreResult}; +use std::fmt::Write; + /// Converts a `&[Vec]` to `Vec`. Each inner slice is expected to be exactly 32 bytes long. /// Pads with zeros if any inner slice is shorter than 32 bytes. pub(crate) fn slice_slice_u8_to_vec_u256(slices: &[[u8; 32]]) -> EyreResult> { @@ -11,6 +14,58 @@ pub(crate) fn slice_u8_to_u256(slice: &[u8]) -> EyreResult { U256::try_from_be_slice(slice).wrap_err_with(|| "could not convert &[u8] to U256".to_string()) } +// Function to convert a slice of u8 to a padded hex string +// Function only takes a slice of length up to 32 elements +// Pads the value on the right side with zeros only if the converted string has lesser than 64 characters. +pub(crate) fn to_padded_hex(slice: &[u8]) -> String { + assert!(slice.len() <= 32, "Slice length must not exceed 32"); + let hex = slice.iter().fold(String::new(), |mut output, byte| { + // 0: pads with zeros + // 2: specifies the minimum width (2 characters) + // x: formats the number as lowercase hexadecimal + // writes a byte value as a two-digit hexadecimal number (padded with a leading zero if necessary) to the specified output. + let _ = write!(output, "{byte:02x}"); + output + }); + format!("{:0<64}", hex) +} + +/// Function to construct the transaction for updating the state in core contract. +pub(crate) fn get_txn_input_bytes(program_output: Vec<[u8; 32]>, kzg_proof: [u8; 48]) -> Bytes { + let program_output_hex_string = vec_u8_32_to_hex_string(program_output); + let kzg_proof_hex_string = u8_48_to_hex_string(kzg_proof); + // cast keccak "updateStateKzgDA(uint256[] calldata programOutput, bytes calldata kzgProof)" | cut -b 1-10 + let function_selector = "0x1a790556"; + + Bytes::from(program_output_hex_string + &kzg_proof_hex_string + function_selector) +} + +pub(crate) fn vec_u8_32_to_hex_string(data: Vec<[u8; 32]>) -> String { + data.into_iter().fold(String::new(), |mut output, arr| { + // Convert the array to a hex string + let hex = arr.iter().fold(String::new(), |mut output, byte| { + let _ = write!(output, "{byte:02x}"); + output + }); + + // Ensure the hex string is exactly 64 characters (32 bytes) + let _ = write!(output, "{hex:0>64}"); + output + }) +} + +pub(crate) fn u8_48_to_hex_string(data: [u8; 48]) -> String { + // Split the array into two parts + let (first_32, last_16) = data.split_at(32); + + // Convert and pad each part + let first_hex = to_padded_hex(first_32); + let second_hex = to_padded_hex(last_16); + + // Concatenate the two hex strings + first_hex + &second_hex +} + #[cfg(test)] mod tests { use super::*; @@ -92,4 +147,98 @@ mod tests { } } } + + #[rstest] + #[case::empty(&[], "0".repeat(64))] + #[case::typical(&[0xFF,0xFF,0xFF,0xFF], format!("{}{}", "ff".repeat(4), "0".repeat(56)))] + #[case::big(&[0xFF; 32], format!("{}", "ff".repeat(32)))] + fn to_hex_string_works(#[case] slice: &[u8], #[case] expected: String) { + let result = to_padded_hex(slice); + assert_eq!(result, expected); + assert!(expected.len() == 64); + } + + #[rstest] + #[should_panic(expected = "Slice length must not exceed 32")] + #[case::exceeding(&[0xFF; 40], format!("{}", "ff".repeat(32)))] + fn to_hex_string_panics(#[case] slice: &[u8], #[case] expected: String) { + let _ = to_padded_hex(slice); + assert!(expected.len() == 64); + } + + #[rstest] + #[case::typical([ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + ], + "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3000000000000000000000000000000000")] + #[case::single_value( + [0xFF;48], + format!("{}{}","ff".repeat(48), "00".repeat(16)) + )] + fn u8_48_to_hex_string_works(#[case] slice: [u8; 48], #[case] expected: String) { + let result = u8_48_to_hex_string(slice); + assert_eq!(result, expected); + } + + #[rstest] + #[case::typical( + vec![ + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32], + [32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1], + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 0, 0] + ], + "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a0908070605040302010102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e0000" + )] + #[case::single_value( + vec![ + [0xFF;32], + [0xF5;32], + ], + format!("{}{}", "ff".repeat(32), "f5".repeat(32)) + )] + fn vec_u8_32_to_hex_string_works(#[case] slice: Vec<[u8; 32]>, #[case] expected: String) { + let result = vec_u8_32_to_hex_string(slice); + assert_eq!(result, expected); + } + + #[rstest] + #[case::typical( + vec![ + [0xFF;32], + [0xF5;32], + ], + [0xF1;48], + format!("{}{}{}{}{}", + "ff".repeat(32), "f5".repeat(32), + "f1".repeat(48), "00".repeat(16), + "0x1a790556" + ) + )] + #[case::typical( + vec![ + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32], + [32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1], + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 0, 0] + ], + [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + ], + format!("{}{}{}", + "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a0908070605040302010102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e0000", + "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3000000000000000000000000000000000", + "0x1a790556" + ) + )] + + fn get_txn_input_bytes_works( + #[case] program_output: Vec<[u8; 32]>, + #[case] kzg_proof: [u8; 48], + #[case] expected_output: String, + ) { + let result: Bytes = get_txn_input_bytes(program_output, kzg_proof); + //TODO: converting expected value to match result, we would ideally want to convert the result to match expected + assert_eq!(result, Bytes::from(expected_output)); + } } diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index 26d4608f..a321a3c5 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -9,7 +9,7 @@ use alloy::consensus::{ use alloy::eips::eip2718::Encodable2718; use alloy::eips::eip2930::AccessList; use alloy::eips::eip4844::BYTES_PER_BLOB; -use alloy::primitives::{Bytes, FixedBytes}; +use alloy::primitives::FixedBytes; use alloy::{ network::EthereumWallet, primitives::{Address, B256, U256}, @@ -21,9 +21,9 @@ use async_trait::async_trait; use c_kzg::{Blob, Bytes32, KzgCommitment, KzgProof, KzgSettings}; use color_eyre::eyre::eyre; use color_eyre::Result; +use conversion::get_txn_input_bytes; use mockall::{automock, lazy_static, predicate::*}; -use rstest::rstest; -use std::fmt::Write; + use std::path::{Path, PathBuf}; use std::str::FromStr; use std::sync::Arc; @@ -163,7 +163,7 @@ impl SettlementClient for EthereumSettlementClient { .expect("Unable to build KZG proof for given params.") .to_owned(); - let tx = TxEip4844 { + let tx: TxEip4844 = TxEip4844 { chain_id, nonce, gas_limit: 30_000_000, @@ -244,79 +244,3 @@ async fn prepare_sidecar( Ok((sidecar_blobs, sidecar_commitments, sidecar_proofs)) } - -/// Function to construct the transaction for updating the state in core contract. -fn get_txn_input_bytes(program_output: Vec<[u8; 32]>, kzg_proof: [u8; 48]) -> Bytes { - let program_output_hex_string = vec_u8_32_to_hex_string(program_output); - let kzg_proof_hex_string = u8_48_to_hex_string(kzg_proof); - // cast keccak "updateStateKzgDA(uint256[] calldata programOutput, bytes calldata kzgProof)" | cut -b 1-10 - let function_selector = "0x1a790556"; - - Bytes::from(program_output_hex_string + &kzg_proof_hex_string + function_selector) -} - -fn vec_u8_32_to_hex_string(data: Vec<[u8; 32]>) -> String { - data.into_iter().fold(String::new(), |mut output, arr| { - // Convert the array to a hex string - let hex = arr.iter().fold(String::new(), |mut output, byte| { - let _ = write!(output, "{byte:02x}"); - output - }); - - // Ensure the hex string is exactly 64 characters (32 bytes) - let _ = write!(output, "{hex:0>64}"); - output - }) -} - -fn u8_48_to_hex_string(data: [u8; 48]) -> String { - // Split the array into two parts - let (first_32, last_16) = data.split_at(32); - - // Convert and pad each part - let first_hex = to_padded_hex(first_32); - let second_hex = to_padded_hex(last_16); - - // Concatenate the two hex strings - first_hex + &second_hex -} - -// Function to convert a slice of u8 to a padded hex string -fn to_padded_hex(slice: &[u8]) -> String { - let hex = slice.iter().fold(String::new(), |mut output, byte| { - let _ = write!(output, "{byte:02x}"); - output - }); - format!("{:0<64}", hex) -} - -#[rstest] -fn test_data_conversion() { - let data: [u8; 48] = [ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, - 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - ]; - - let result = u8_48_to_hex_string(data); - - assert_eq!(result, "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3000000000000000000000000000000000"); - - let mut data_2: [u8; 32] = [ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, - 31, 32, - ]; - let mut data_vec: Vec<[u8; 32]> = Vec::new(); - data_vec.push(data_2); - data_2.reverse(); - data_vec.push(data_2); - - let data_3: [u8; 32] = [ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, - 0, 0, - ]; - data_vec.push(data_3); - - let result_2 = vec_u8_32_to_hex_string(data_vec); - - assert_eq!(result_2, "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a0908070605040302010102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e0000"); -} From c23a4a6af38a6d3816c5839801e6d6fa010f7163 Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Mon, 5 Aug 2024 15:27:59 +0530 Subject: [PATCH 04/41] update: Eth Settlement client : prepare_sidecar --- .../ethereum/src/conversion.rs | 80 +++++++++++++++++++ crates/settlement-clients/ethereum/src/lib.rs | 28 +------ 2 files changed, 81 insertions(+), 27 deletions(-) diff --git a/crates/settlement-clients/ethereum/src/conversion.rs b/crates/settlement-clients/ethereum/src/conversion.rs index f7597a86..65ba07b1 100644 --- a/crates/settlement-clients/ethereum/src/conversion.rs +++ b/crates/settlement-clients/ethereum/src/conversion.rs @@ -1,5 +1,8 @@ +use alloy::eips::eip4844::BYTES_PER_BLOB; use alloy::primitives::Bytes; +use alloy::primitives::FixedBytes; use alloy::primitives::U256; +use c_kzg::{Blob, KzgCommitment, KzgProof, KzgSettings}; use color_eyre::{eyre::ContextCompat, Result as EyreResult}; use std::fmt::Write; @@ -66,10 +69,38 @@ pub(crate) fn u8_48_to_hex_string(data: [u8; 48]) -> String { first_hex + &second_hex } +/// To prepare the sidecar for EIP 4844 transaction +pub(crate) async fn prepare_sidecar( + state_diff: &[Vec], + trusted_setup: &KzgSettings, +) -> EyreResult<(Vec>, Vec>, Vec>)> { + let mut sidecar_blobs = vec![]; + let mut sidecar_commitments = vec![]; + let mut sidecar_proofs = vec![]; + + for blob_data in state_diff { + let fixed_size_blob: [u8; BYTES_PER_BLOB] = blob_data.as_slice().try_into()?; + + let blob = Blob::new(fixed_size_blob); + + let commitment = KzgCommitment::blob_to_kzg_commitment(&blob, trusted_setup)?; + let proof = KzgProof::compute_blob_kzg_proof(&blob, &commitment.to_bytes(), trusted_setup)?; + + sidecar_blobs.push(FixedBytes::new(fixed_size_blob)); + sidecar_commitments.push(FixedBytes::new(commitment.to_bytes().into_inner())); + sidecar_proofs.push(FixedBytes::new(proof.to_bytes().into_inner())); + } + + Ok((sidecar_blobs, sidecar_commitments, sidecar_proofs)) +} + #[cfg(test)] mod tests { + use super::*; + use color_eyre::eyre::eyre; use rstest::rstest; + use std::{fs, path::Path}; #[rstest] #[case::typical(&[ @@ -241,4 +272,53 @@ mod tests { //TODO: converting expected value to match result, we would ideally want to convert the result to match expected assert_eq!(result, Bytes::from(expected_output)); } + + #[rstest] + #[tokio::test] + #[case("651053")] + async fn prepare_sidecar_works(#[case] block_no: String) { + // Trusted Setup + let trusted_setup_file_path = "/Users/dexterhv/Work/Karnot/madara-alliance/madara-orchestrator/crates/settlement-clients/ethereum/src/trusted_setup.txt"; + let trusted_setup = KzgSettings::load_trusted_setup_file(Path::new(trusted_setup_file_path)) + .expect("issue while loading the trusted setup"); + + // Blob Data + let blob_data_file_path = format!("{}{}{}", + "/Users/dexterhv/Work/Karnot/madara-alliance/madara-orchestrator/crates/orchestrator/src/tests/jobs/state_update_job/test_data/", + block_no, + "/blob_data.txt" + ); + + let blob_data = fs::read_to_string(blob_data_file_path).expect("Failed to read the blob data txt file"); + + fn hex_string_to_u8_vec(hex_str: &str) -> color_eyre::Result> { + // Remove any spaces or non-hex characters from the input string + let cleaned_str: String = hex_str.chars().filter(|c| c.is_ascii_hexdigit()).collect(); + + // Convert the cleaned hex string to a Vec + let mut result = Vec::new(); + for chunk in cleaned_str.as_bytes().chunks(2) { + if let Ok(byte_val) = u8::from_str_radix(std::str::from_utf8(chunk)?, 16) { + result.push(byte_val); + } else { + return Err(eyre!("Error parsing hex string: {}", cleaned_str)); + } + } + + Ok(result) + } + + let blob_data_vec = vec![hex_string_to_u8_vec(&blob_data).unwrap()]; + + match prepare_sidecar(&blob_data_vec, &trusted_setup).await { + Ok(result) => { + let (sidecar_blobs, sidecar_commitments, sidecar_proofs) = result; + // TODO: complete validation + println!("Success") + } + Err(err) => { + panic!("{}", err.to_string()) + } + } + } } diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index a321a3c5..903d7bf5 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -9,7 +9,6 @@ use alloy::consensus::{ use alloy::eips::eip2718::Encodable2718; use alloy::eips::eip2930::AccessList; use alloy::eips::eip4844::BYTES_PER_BLOB; -use alloy::primitives::FixedBytes; use alloy::{ network::EthereumWallet, primitives::{Address, B256, U256}, @@ -21,7 +20,7 @@ use async_trait::async_trait; use c_kzg::{Blob, Bytes32, KzgCommitment, KzgProof, KzgSettings}; use color_eyre::eyre::eyre; use color_eyre::Result; -use conversion::get_txn_input_bytes; +use conversion::{get_txn_input_bytes, prepare_sidecar}; use mockall::{automock, lazy_static, predicate::*}; use std::path::{Path, PathBuf}; @@ -219,28 +218,3 @@ impl SettlementClient for EthereumSettlementClient { Ok(block_number.try_into()?) } } - -/// To prepare the sidecar for EIP 4844 transaction -async fn prepare_sidecar( - state_diff: &[Vec], - trusted_setup: &KzgSettings, -) -> Result<(Vec>, Vec>, Vec>)> { - let mut sidecar_blobs = vec![]; - let mut sidecar_commitments = vec![]; - let mut sidecar_proofs = vec![]; - - for blob_data in state_diff { - let fixed_size_blob: [u8; BYTES_PER_BLOB] = blob_data.as_slice().try_into()?; - - let blob = Blob::new(fixed_size_blob); - - let commitment = KzgCommitment::blob_to_kzg_commitment(&blob, trusted_setup)?; - let proof = KzgProof::compute_blob_kzg_proof(&blob, &commitment.to_bytes(), trusted_setup)?; - - sidecar_blobs.push(FixedBytes::new(fixed_size_blob)); - sidecar_commitments.push(FixedBytes::new(commitment.to_bytes().into_inner())); - sidecar_proofs.push(FixedBytes::new(proof.to_bytes().into_inner())); - } - - Ok((sidecar_blobs, sidecar_commitments, sidecar_proofs)) -} From 5e2ea5e0f21b7cd7c5816b39f5f9d39eb970c231 Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Mon, 5 Aug 2024 20:19:45 +0530 Subject: [PATCH 05/41] update: settlement_client: eth: prepare_sidecar tests --- .../ethereum/src/conversion.rs | 43 +++++++++++++------ .../test_data/blob_commitment/20462788.txt | 1 + .../test_data/blob_commitment/20462818.txt | 1 + .../src/test_data/blob_data/20462788.txt | 1 + .../src/test_data/blob_data/20462818.txt | 1 + .../src/test_data/blob_proof/20462788.txt | 1 + .../src/test_data/blob_proof/20462818.txt | 1 + 7 files changed, 35 insertions(+), 14 deletions(-) create mode 100644 crates/settlement-clients/ethereum/src/test_data/blob_commitment/20462788.txt create mode 100644 crates/settlement-clients/ethereum/src/test_data/blob_commitment/20462818.txt create mode 100644 crates/settlement-clients/ethereum/src/test_data/blob_data/20462788.txt create mode 100644 crates/settlement-clients/ethereum/src/test_data/blob_data/20462818.txt create mode 100644 crates/settlement-clients/ethereum/src/test_data/blob_proof/20462788.txt create mode 100644 crates/settlement-clients/ethereum/src/test_data/blob_proof/20462818.txt diff --git a/crates/settlement-clients/ethereum/src/conversion.rs b/crates/settlement-clients/ethereum/src/conversion.rs index 65ba07b1..b06405f5 100644 --- a/crates/settlement-clients/ethereum/src/conversion.rs +++ b/crates/settlement-clients/ethereum/src/conversion.rs @@ -73,7 +73,7 @@ pub(crate) fn u8_48_to_hex_string(data: [u8; 48]) -> String { pub(crate) async fn prepare_sidecar( state_diff: &[Vec], trusted_setup: &KzgSettings, -) -> EyreResult<(Vec>, Vec>, Vec>)> { +) -> EyreResult<(Vec>, Vec>, Vec>)> { let mut sidecar_blobs = vec![]; let mut sidecar_commitments = vec![]; let mut sidecar_proofs = vec![]; @@ -100,7 +100,10 @@ mod tests { use super::*; use color_eyre::eyre::eyre; use rstest::rstest; - use std::{fs, path::Path}; + use std::{ + fs, + path::Path, + }; #[rstest] #[case::typical(&[ @@ -274,23 +277,34 @@ mod tests { } #[rstest] + #[case("20462788")] + #[case("20462818")] #[tokio::test] - #[case("651053")] async fn prepare_sidecar_works(#[case] block_no: String) { // Trusted Setup - let trusted_setup_file_path = "/Users/dexterhv/Work/Karnot/madara-alliance/madara-orchestrator/crates/settlement-clients/ethereum/src/trusted_setup.txt"; - let trusted_setup = KzgSettings::load_trusted_setup_file(Path::new(trusted_setup_file_path)) + let current_path = std::env::current_dir().unwrap().to_str().unwrap().to_string(); + + let trusted_setup_file_path = current_path.clone() + "/src/trusted_setup.txt"; + let trusted_setup = KzgSettings::load_trusted_setup_file(Path::new(trusted_setup_file_path.as_str())) .expect("issue while loading the trusted setup"); // Blob Data - let blob_data_file_path = format!("{}{}{}", - "/Users/dexterhv/Work/Karnot/madara-alliance/madara-orchestrator/crates/orchestrator/src/tests/jobs/state_update_job/test_data/", - block_no, - "/blob_data.txt" - ); - + let blob_data_file_path = + format!("{}{}{}{}", current_path.clone(), "/src/test_data/blob_data/", block_no, ".txt"); + println!("{}", blob_data_file_path); let blob_data = fs::read_to_string(blob_data_file_path).expect("Failed to read the blob data txt file"); + // Blob Commitment + let blob_commitment_file_path = + format!("{}{}{}{}", current_path.clone(), "/src/test_data/blob_commitment/", block_no, ".txt"); + let blob_commitment = + fs::read_to_string(blob_commitment_file_path).expect("Failed to read the blob data txt file"); + + // Blob Proof + let blob_proof_file_path = + format!("{}{}{}{}", current_path.clone(), "/src/test_data/blob_proof/", block_no, ".txt"); + let blob_proof = fs::read_to_string(blob_proof_file_path).expect("Failed to read the blob data txt file"); + fn hex_string_to_u8_vec(hex_str: &str) -> color_eyre::Result> { // Remove any spaces or non-hex characters from the input string let cleaned_str: String = hex_str.chars().filter(|c| c.is_ascii_hexdigit()).collect(); @@ -312,9 +326,10 @@ mod tests { match prepare_sidecar(&blob_data_vec, &trusted_setup).await { Ok(result) => { - let (sidecar_blobs, sidecar_commitments, sidecar_proofs) = result; - // TODO: complete validation - println!("Success") + let (_, sidecar_commitments, sidecar_proofs) = result; + // Assumption: since only 1 blob, thus only 1 commitment and proof + assert_eq!(blob_commitment, sidecar_commitments[0].to_string()); + assert_eq!(blob_proof, sidecar_proofs[0].to_string()); } Err(err) => { panic!("{}", err.to_string()) diff --git a/crates/settlement-clients/ethereum/src/test_data/blob_commitment/20462788.txt b/crates/settlement-clients/ethereum/src/test_data/blob_commitment/20462788.txt new file mode 100644 index 00000000..8302f726 --- /dev/null +++ b/crates/settlement-clients/ethereum/src/test_data/blob_commitment/20462788.txt @@ -0,0 +1 @@ +0x80f3ffd8f64bb2558910bb58f6f197e24dc624a977e5df21d83e2d254129c70d894b81c5118956ed4d9704b500a5a277 \ No newline at end of file diff --git a/crates/settlement-clients/ethereum/src/test_data/blob_commitment/20462818.txt b/crates/settlement-clients/ethereum/src/test_data/blob_commitment/20462818.txt new file mode 100644 index 00000000..3077d6ce --- /dev/null +++ b/crates/settlement-clients/ethereum/src/test_data/blob_commitment/20462818.txt @@ -0,0 +1 @@ +0xb0f7bd1923aabfcb98b8e6341fc4d5035f685ca4940f185574d411b83b0bffe2f99d2c6c6b8977e9de8ca5f15df6f963 \ No newline at end of file diff --git a/crates/settlement-clients/ethereum/src/test_data/blob_data/20462788.txt b/crates/settlement-clients/ethereum/src/test_data/blob_data/20462788.txt new file mode 100644 index 00000000..1eabf8d9 --- /dev/null +++ b/crates/settlement-clients/ethereum/src/test_data/blob_data/20462788.txt @@ -0,0 +1 @@ +1f1090678692149b3fc41c910078cd9fa7d1dce2e2db2b572da17949f050afb856eb357ef69289bdbf69a2a48eb3349b4ffb2b6a6a218f5a5dff4b6f53c4debc4d33fdff0a68657342a8e2d6c2e89799c8c0db289e1a5dfb3339cd069d89105e03e889a83e6a37eb9fa2fee024f9639494d0b1ce14010d0b163df15cbef973054c97c92d7efa119e72ddc4ea476eae202e429804232ee21cc8a496afa7dc5e9801839da51abca71ae96c540fe8275cf4f0a2963ab4efffaf7cbe905ea913708f6b96d89f1d8cbd07a71ec0e696525b7c08468a99054c8bebf2111a1e439b71386eb1dc020e20ea86bd0a3be15973fb35d246df77c192144c766a613e121c4fe348e0bcbcae76d086b45a2f2cc7c95f9f7af0f00ac71a3b508306808585b444790b4471687d27ebf46461786a38191943c133d95a6351514111f298668e6685d1404f3e7ecf32b488b761f83aa0226d6811da3ca2cf7875c984ce124a70c7e10f692844603b590928a27ea86c9f6f60f29eb0b73ab4366f365f6b9bfceeb4c4b971e86b8ae908667f242bcd9a44324b533677ddc039363bdf9d0325d43fe4fdfc051ef868e536b13319ab6020837bbc79445855d2ce190bd6fabdc3abcd5bff57665141e66770f07ed7046f6014a22b0fc61f52eac811cd3a1d568af316839d39513ed41d8269f256c35e9e74074d82d4dde6bac415f104eee006a4692ec9568e2d29315b98b33d31f3adae4e178fcb0f3f8d33372ca21a7e8a437fa4e7208fdb464bff58b03c029777ca87e76faeae028ab4dcee16f3166f80edab629e49c09b2ff19102bd0ca01d6f4cd003c0117f56c3588733de45a1d9ec465834bdf62299020daaded4fa909a9c6c0cd07cf3e48851280221e2ecf81e5bf14ff23b3381cf507b020e9d199c8a105bfe1cd61748c1d0f945ab9ac5e7d4883fe2b9251e9a7817d66438f6bb0a74ecc673a493cb4eb28bda79a35ec41b29038d822887b883ac27236cf7f606872d2ae77dbb394fbd81dce76a231d9f759d585a6435cb7f29833b3a9357148d9060ae092e3cfb449486634dc22113fffe4c26375ccf9d8244701ac4f5e01dfaa1f81311dca7d01d0c08f55cb93ea9756faa00795ee1a1e07fb15765373389a20e66fab55d82b9d0b322e165f4a653e31f4b1eceed918d62a6115ac6cbfc126b4546d27541a925683797efcaf1772b599c518b86eec76d7c96ea5b5e3dabf9963e8979a37ac09c34510c28a6d1f49094e86351fe9e676deab14c10fc32431665571c1ff237cab6a03b9ae0db0a0142d7f420b74cad71dc3b0ad76dcb12a0e62d313ed20e4c50f49aee7badf36b3db43cb0713c2cda06b7cd55384d38c2a29baa13fd2dcbe3243be48da5fc682eb8320b8672e80a1fe5041d2ca13040dae78d1552f36e8cf271c4ac67b8d74e210bafcbe20b5fc0751f81ce7568451349ac0060ee02f60fe6efe5be3916509620bca6223a770beee8bd3be10a2c4866cfcfcc8cea05ebebb967dd52b7db77a92a4d52ff4c3cbd9abda83a7b99f91286b017650e7a85ba3e985ed59ba3bcdb26961e9c3e7fc04cc195669be706041872073f2aa4c4b15733f308a376e3f3c2de1fb9c9348addda2ed1648f8226f2642ccf614c454bcc7dd74f91665d1a9b428f76e0a66f8a1fd23462cb52eb00e462110ff386e2a73f13c17df0830f0c7a76618fd7676a0f3cfbca96467aa770864a8a8c96e67a2e3f016589b3e13572a6fb0b07dca040091eb96ce07ca35fd28c53991e7f062d1dd50b79ee569b9f85b64b345ed9c1a328392a3f871373fd179a33d2c4802b99cad3c875777de7034bbebb4aca014c9665e5efa8b245b088852256ee91324d128b8b241b547d4eb12ebb86a3233d18b5a538d138d7dc68ec9f355acb77f8d775605873f5496543919e7a5c2fdd9d9aac325b8366166d5789c7a5606a38829fbe93c081fb7e1209bd66768e31428c32f6972ced072de8b51db27d572b798c6a7394c4f7316469e1b0bc9fdcca123709740fa7d6df61ef12caa6422c38c2550594707eb5692dbd4ebbb6d197b41adaa0cf8c1fe565ec7fe12077ea6e79d15e6652aa9903030785d9948bfa4de3902bcee3e803e6866de67bf6807d4bdbaca75de87b94307db1a85fbaf91969c584f21e1de2446c3d2e76a3315b3c33e1172540021644f91ca82dd6eafb6493de00df8d16c0c4c6d6ff3980184a431580bd26e509588d1a7f66c4f023eec66b1afccd0709825c7a84dc2ede3f6b48309d5ac19c45a4156c634c13c17ef64207e73a2e486737dca2fb0a32e229f3023835a0437236a6332d77b4c131364cc8544732702cc3fbd78ae1188f41314b35112753b937377a6d7eedec7717597f42eac03b5cdbada6dc15942e9f63ad30352d25f60ebea3b23a74151d23656f8efe2f3b4dd1934e9f1a98534d41384b913b49c2aa52a021187db9edd82d35e6310064e3959cbb6dce279cbb11ce8ace28ca196370cd90f7a09126fc44bdc8557aae26de77b2fb8cc2a63a93933817fc78896b04d6a64f15dcdd4383c86116485363f0c584dc9469cdc915b7a6ac3e8973fc5f19870105bfd3774e94e453d21c175e826d10f3ee2fae4c3afccaec4854757418840fafad380bb9e233a25a2fa9228d28d8a9ee95e28fcc70750150e78897675ae412e1ff9cb67fcdc27891348f95b07bd1f172fc1f505e3082593c0abe944666877bd900c360437460124a176336e4d0fef9f0f2da82eb74eedf00848e44ce5db761153907534eee393988e937f90200cee62bd9f79208a3e390156bb358835306b02f70718462cbcea14346696b8144c3ad18cd69f9369292a32f440c7d6a0b215e55b623afbdd966edad6513e569f92254366b432c307dd51071928f09c601422ae589350c13274d175b6211d23c416124968def616b45ac2428ec01205b380bab3940ebb35bb2ac2f1f959d230b5acaf4a619d33fd4ee3a5ed38b5e433c37cfcd33bc6b77bbd669faad0347342b914c832d8418d9e17586d41dd15e30a459167112c3e361e1504b697f2e25c8d9e9d6a500f6f9bbd8bdea107d38e37fee0c210001409f1bc92db5717c5e1d7e6617b2ea0270284a0f615b89d36b79bc243fde04e162a115432fa4c9ee028c7c950477f65d47eb686ed13c1ed186ea415c2667fa33fb5242a5201473f1dc87fac8d17416b8692e49a2171e63b096b7094343f691a0dafb22d70548a0f8cdb7795e6cfca7894cd8fce4444f24f7794938301e6b31da181d2a4f2dee9bfc4cba6d58db0f457e7d8c7303b791807f3a739466615ffc52c8e6a467bc114b492fea4da453856488d32bb57ff7ec928669850d4668a14a2ff550d5f8aabda872708e9768e4a7b7026a14f33406093d3ce33f3a193322087a8deb843e4766f12406a72503d59abad7fea92e8a294760489faacba6127834a0c060eac80295097c3ecb0965e07a17d96474833d796d34811e30a6a54578fc35b47eea55666e5c438db1026e8f91b2712ecba8cd15e06ef7e3bccf9c6ed190aa3c9072d5139498e504e568b9ad8f0dc0fe52500fafc5e0acd17d421e636f1d01937ca9b44a3684577d4e18e5d2c7d54f5dd21f5dc43eaf6b2cb5ad4b72919604b15f8daa36ed2ca535e9a6e199f3b6a31cdc0a1d8cc135c5a77cc94d2b38d8aedcd260d9ca4830b5a2f8731bbbb55b0d9755a1bcd721e06a2bc689e409030e15d74a331ad4eaabae2f1f796b6fe485141f0daad98f9d53f38f2746436cc7c9d50b796a0075df86d96d89125a8afe12aa413f294d7acd6c42d1e4be432e156c16d3468049d0f56474061dc8f4ba7c02f0f976e0105da60589dc7e490a5b276edb9a63b1e24fd1ce1fec84e50e318596133deb925af893fd05c5678fa403637d5ebbea39eda23dd228726ad899e412c1669b54ffac4c223338e6d1649520df1d34bdfaa95dccea10af6f3e238e671a3748688ddb0637818d044e82879e3694d1a0955f3cd2c5894428c724b6dba8c0ebf46899c6b0bdc0d5cbded876de006d33ef0288ab5627268f6d5df6705a88b60f18f5f0b5a9e7bff5fc32e50eaa328bb9b4f8dfb49e7518a77fdf979166f45e48cf0b4435b8f9136c8435bf600a462f50ce3f15d7eaec7895ac869eb3e83eaf55d87af3af658646828346f7476f26d00b0f503352adac104b7ff11dfd192ad094e18d662366d713f7a078dc155e3ad0231f0b234d82952e992a096af01557a240e07741e1b7b264fac23b4e2c9b041540158dbf190f0d60cfa13242d9a40149093f4bc50710c18442db46fe0a81636447046e40035da639a294b1cf1a3b9f8e53a38ffe0ec1384a66d563d15c732f1bd7bb14305b87cf2dfee4779c4ed0069a8cb5fa7a8713892dd12e5db52a3f0bfaf489b7ab057588d7634b310b2e43815a67b4ccc4f871dbfd6900bd71bfdb0a3a6092b5fea7dde0d1ee4c4b67322a9c6044d1f31dfce7a7dbfe5da48032c06cc52681e8ea2acf337e4aafae75abf8e859bf84d6c8562beaa6028682f914fb3463d939933a29c1db72a6134b99d5866a4899dc8b33f7e2e64a88b823098e654179e9954070a688888c72abe405f97e88afee2d0e6c6a5b3085af0f4587adc55991487cbcb7b5532c0b3a74bf69ebf0047472aacf4dcbb059b8f44c50332b9e47d0869bc62164f5aa6740cf41c707f1d8a92631ea7033f9f2353ef51a4f10f072db48aac30ae0eee0453820d42b531fcae12c2eeb3683624af26df47da340e541336a3ddafa6401e2ba06748869631d80c1ed47f19e2d10ea3dce90a3172d1902eef270d89df11c195db2cc1b30c692e06ea1ae678faa9718281e18ad22d7d90050fa61690c8a8f6ddefd59c4accf2946565bff22a425684385059e5afa5269659a06b0a754d00ed64d338ca2747f9fa6909732d0e201b59feebc42fcffa1714db2c6104a7859a4dc9b11f67747549ac1e5ed307c91324213ca4bf8e7de26d50c7b986bfba3d95f32bd0c6d7321d0ce593d209bf81064531b04d319ba7858c40e9d00c5bca088bdaa997a14d49722851b183154d3dd55cbe22b6ba589682f3b323ef2fc32b926a26bff118d25a58d8aaf1de3b4227c0ef1f20ff33d40bb983c6f183fd4cd3c1afd8051d124f3c86e41a1ff1c17117687ed6b286101a97feeae295411ee2283f3033a3a421ad127d252f39ca3885e355804c04650bfb873100813f4f8332498a593e6075e8616fc75ad7589da6c67d0a4d52b55eefbdbeefb86651c88e57065e82e3c6a330025a0f2a6026098a0541ba5e232b2e39747ab53d615513a1946f1ef337c3190843770e890524670dec814f0f6d47f8f71cc18f20c674848610ec17a14c08ef08025f500e29a55881aa70cba0820af38f341acc22b735d817f8bbe2d649547be74aad776ea0d01c719dad96a04cb78498dc26f040a38d263ac78965e2f5d58eac0bdead5079ca9ad297cc7763a766f8ebca785a7f0572715fc81821ddb59057afdfda877307dc6db973c6024a799258c17bd3f48c244fc87163f241e3181eb1fb319d609f08d62ea2ffd5d74a3fa10155e45f9e4ad352dca736cb9a53b4cee6adf4a73e13e7bc8757cb08af45e30e48cb45d44ad722ed6a4677dbdc7d1143ff27d481cf5de8e352e36754105f58be8a99090bec4f814a9e61ff70733b2d4a78ba6dd10c024bc487394394c04d63415b916f8ddf203032c937d11c3fe9c0db2322400e0843a579760e24cf450e3e4ec64e02d9aef6e645213cfd78bedd744b71b32075ff1c6c2a119fff0c53ab6fe8b3d01378cdb10518f72675607fca6f17d39e956c9d3ee125d2a2db652ef11b665f7ff020df6225ebb7efe968f9c84b3a024d667644668412f7ee95ae5b2c15172d04bf6b40cd65eb9bdd77c00a7ac483a9b2637900b0d5371632523df0057e9f5bfefb5da0c443c5b266df6d41ae1914eb15c3d78675639edfee7f5074d476f5115139ec6f59d037497cb8dbb13242c3b5009009fc1f254dff510466c41df19444c36e4d0620c4348dc237b1838a24b9650baacc1f60c0b7bc80f180c379b0515be53a547d74024b9b562afc27c90e5cb4486eefeb05eb99677aaeea5149ea5b1b0d5eea3027d5d144d5b5b5a8d1100af3b48f4cef0dc1d088ac0131279d23d46752be495852f25bd6f3c2a61adb47882879e435bb8d72e2bc1bf5b6a0f50f21e701a6b96345b292164a1d95e226101756292d5292631cf9ada03112f7b0dc2bbbd491c10777e049ea97f0efee405c94264ff17de9180e84b5be967aba6a07db8a2c2bb4e8f216801055e4e6279491367cd40f69c68c0c5811a46434704222b53e65580bbd95b135d22de2e43b0b20ed1da753d7009bc3caddffe72b254a2b8bb02ced1bd70f06cfcca433485bd9a7d64d28053da858b645796b61ee22708425bb072b7d124c42db43ce68d7b203fd28780bd594db193578c5a7322986b0006b6835679a7b399487ebdf56340667579d9cd811a701f84498581434ee975819abfc2f0337496fb0f6e28847543b994701b9726cec5df7c95cf635dfa9522e24e85f9e4dc1afc9446d06b1d180799b6ed2a1f027ec3aca219ab6b95a7b134bde579f175e3bae6f16594e2c88979a0943efac7fc4020918c39ecc89b5a4e53c8eaa1522a72e9607f414488208d9517dc60f2fe4587d7993a43c1bc692e52107336b083199e2a00c943b223d766647eb36862feeae86ac481b12a6ce26a0e053cf8920ac389d4cf3e55fcdbdfd28f3bc53be25424c2076c44105eda22736086ed77c24e00b8b297953204005555f68bbe256ec2a51881824b0ea2ae89824eb258ea266bdae89aec0a43939377b0494254c2dd755b780deaf8df16726c922b3f22df81ce9064fd7d4a5050d2d627d552aacad9f7b9a0876257ee8b7a52c6f4c86b9753936c18656966187c4789ac0232acfb9303b90b221f6dc26062e2abdfc5c9912e9b9ea75cc47f6d5aeaeb59a77680e291f792f2f5224ac3ba5fb654fa88496c2fc8a764d0e1f0371729d0b76589c44c1403d7312af8a92967ac4a8344a3b1179007f5d056212839b660d44d0041cc028689c8e29729c6b14c26b83282bfa3188667574cc2cedc33cf25da1c46faaf06203401819319aa059d5eada3c80138a5964b68304fc9102612c42b22a459e0571456a2dbdb0c0d078d214f2b7f4f09e5cd0dd7e84b4e7b1783e35d621863e09f37a649e209e573811d1735607c1fba5572485874e6a6000e33d5cdd346e759a7128058fea22cc7d5e20f51543a87525e2a49911a0b31443e6c1e9b07101042cfa38fe8a2866a21b1123dd7c7f4ea8c271b01627eda330810fffa92a64209178adc672e536d13d4acb02491d01902623654ce772b47d4343ab8206b4f5831025f5f57b0ff605cd04b5aaffaf6eb0f793643b639b6d9563e5a4b50f38ef01ffa62a209a7597e3ff822b3f16384c9b3e64eeef819e02077352e280ba62b50ecbb4f708bb1a94b1f75ac1e5224109ec396a04163444a027902614c4829c71d1337079b71305f0f8606e74ac44eafcc91f1d55d29032cc7697f06155b8f95c98cd8683ecc1f431c6e9983d3c3ba51a9e321e240e72bb7c0afd130a30dd89e913bf04f7da15970c3a1eaa8016ac5d4f2fe13ee7ef3106a337f3163e919dd839e8899591623a6b12ed03e47d530a6bb53430c45cc9808c7fcc2c43ab70e55ebb83952865a7002cdf7b0a09b71d1c4d894dfd132a5ad66b68c42f1357165f66c14e46a98c75c9f02f257991bf2fdbae733fe6f360b65c66ad60aea25dc7af36ccbcfc1da739ef16598eb85cfd12ae56b9feb5932da8fe3537f453838aa2df6fb1f40c69bd36b8f0fe36e6a82c21f4263e29972bfe9a4c3a583aa8159451f08c7e018f50f379c82eeaf7eabedf4c7476a3f70a40ddfce7834edc28a2f8578043a2f88ae392eb76f703e2f791170bbd27c44ee3d6646f622ea46ae950bf996604853fddea3c7fddd65c0fb45a29c07082b62ae113b1986d80d12a284342cc837be6fe5cf55fcedc5612672ed1a0ca3ee279ab292b55b8b3bb32b9a4d2d0ce7d8f172a91e6dce8b48615dc694bb8c076531d60599b0232dc8d2e78b741c11f298bcf18381a2a64fd939206c03251c6dc3e650fe7e73ec2a99bb9c4541166e2e83c959b053bd2beb936be1e798d60731f1d86e5784c11d67a3ae2596322c31bf10b719ac464fa4977bb7f20fc2883e2c1eb80a25c565b4bf5ca1b70d400600a4f7036370d5cecf57b5c3e7f1cea41f53504d00c158680db445c2131f7019e000f5723ea3a5f7daece81e3b3719e83fabc6d7b7ab829316d1bb05fe01dd63aa809fab87e1339e73325240c7cec68da1879c7e7ef970f2f4bab8b6aadca05b9e64912fd71a291c48c373f548ffe071844907c670c7b1d6e516479ae7dd6610b4be3b390175b5f93850d04596786ababca5eaf32b5c8c5e7bc23dba6cd9553babde94112fe14587520e313131724994308af6fc5fb9812be1fae2e1096fbb235c961b8418879b52c8ea8dabff9b27a89c23b663639a6aaf109ae2d1f8d70a1534edfffede0a7bca793248e9c223e1895ca2922e4346e4302631ba8e610db8336c5cb15a56d3df0e62b210bff614bf0cd4559bcf45798c4c291017ca72b49251162a0cce13dfcd3ec8bc27d7e0f69df948de7a178039a0e3791357cb4fac5468e6f71a7cdff37eff62ffb7b5875c007ec7e80d98fb9e3ad2231e56ac19ab1e6c26c2e12339bbac8688fcbd0266ed3a7758a3452001dba6a6ea3d0655ab515b10a34b18950a7e1a96fed5131f1b904f378989807ab9a1f58501395c1afc806a19563cb282038c77ff8cd607da3d46811e740d2f56d1dd7886d8fe21bf89f85e012e3dbcaae732ee0fe73ea8f8c7470d657cfdad4dc820b03bfc995d3f74ea806a82115f3f07d68a495ead7656da9a038247ea01a94fb19861272179ff3cceb2650b4ba9fb42650c17dda6056daf69a767b7e9c3b0b360c4fec74d078c5065812dedf666fbd71c4340aa624abc8a3c62e437fe717a9968575109cbcdde83d5244c1d1e477c873241fdb91139403e768736d4c2e457d6336998c0ac5a1977fa6f6ceb5ecc1fe8b59681f0951e62da5de356292c812df5dea0730f27f0df3ec0e9184afb0a7820480b5642a0d7c9f379971dbf166d9735e6102342e40d82f34bf81507a408d6764460826777c18532ef13a25933b235394565110e74f93c9e65f9662c71f89a5e7235cee3b757b87d09cb1ce6043af08cd9fe2e071521ce61cba81ffffd48c746c44c4917aa4bd991a2ac55e8e323543b02916beaf5069ab7c804489109fbe3bf563c3bce0549a2b067bde223b736b521b1193ffe3e54f7204eff2920dec898839fc9cebf9a8b311168deb4f0e7f9b4d51fdd41480acd1d7e231855fb477218323d9671f487cc52682bc7998c9c87cdafeaaf68106728bf128de23ed27bf5b0ce1767394ebbda1fa54a0e1a1f55ad80da9dfffcada5977a3135fc68d4aaaf00b4bb6f6fe7e9cd5a9d36aff9ed2d28ae721e12c828157bc6d6fa4d2ccb2f419e1e48e9bef889fa050565523cc35fab38a98e5cd480f9caf0bb1ad7245b2fbcd85514824725ac36ffcb2f9bc213dab1d17fd7e83c73d150d21466a15de5235c9e2bf47cc1758b5d93f528c7dd91c1bd92b3984964331e5052f99687353ed2522f853575e63443c8c33b21f41ebf9b59137744d54baf587ddab6c9a9589803ef3dc878335b00db14c93331998f380a7db841bb2a2ab6218a38493a675f2bd6df56328a64984b8a0f7045dd3d87b612fe988218b071d28a07a258b11930c7d1591fc12ff48218b6935faf4b91c9699eb903a8227a56d271be195b6bf623406b879b35c6d241f51e755cf61bb839193dd131ce67a8b33d1877c83a17ea25ef38e9a6033ed45dd3747db82a4c197bf7de1e808b408df5f4e85cda340b84244f8fe98eb4b3061f27115c294980f5c37ef34e1fd56453cf6a51bb7844f2bc16c94d2b8eb8ee287339a7bb70a040320213178f1b0e8bf6bbdf50143434fb5321caabd3291b83c69c3a3ad709643bc86300f577ce231af387b7ad0d1e52aebb405961d48a4c38ec569f325a08ba6ab15679be15e69e42adfd9cddc370da51714bb0595eecd4ef76764031346c461426f616116c98744a7bab7d1b46eb38efac3515dbf87f41f5f18037d917dc546aee68af9cedc46aa33b92317f295c671bec6112f1b168d9e5513c9c2829dde1b94a4ad8c7db11878c7836526564a40f11b04c9f4d5752c34df4d2c7706e0089fabcf2ec56c4f87ba4ddd92509887c2947ca0ca5386b6fe799ac51a6abff09c4a0c20c6ec8323fcf29b2b2a1bbf1d680ce212d8c122e8485ffbba3b5e2076020abae0d2825a1f54467ac97c9377daaee5c7b5820a452f0fdf9249a07bca91d35e37ddf083e4c7f64eb90b1ad1be3d36bc0e26815fce79281163a2cd5222f3838fa88168f94cbb285451ddc8152fad4ed79fc1ac1937d38c5be106c3a3ad161bcc40929d1b1f368309b7151378ce928033e5c369414e6feb0d13f4fee87025d33a0690da1000ae1448a86f515ff0136c7b1726dc160bb2c90c0b5c959956c0f2a961a3b2b4d1e3206d76fe0578323009997124b20135827ab2fd92df465096be6d3a6439d2d2d964803915711d416669de5a43cf0ccf26a3119dea40893d110fe58560056ff6807fc19c886acca79b374c5880802ec560701fac936eb02bcc2a15bd5c813b997a923ae675f9daa3255ed596a677650e6226aed14060785c6f0f803c6beeae8544a1a65fcf50aa4669aefa77242513a34c3eac35441542e19c681106785de1dc9ced50266f24c554a1ebbcec911b9b55735de2e69be46c6d55a3168ca70f4674d70b56b6283a40a73713c6ccd68d64a1c42673ea3830b6944a48f3c5cc2e9e8b91265fe43ff3be9e933feca002826dde33c9d1f208932ca95a6fb6d117c729f2d24ad73ff864b6ede2c6c631d018e29841eae136a51bbabb5b928865e66165b823a889f6246b07edb11fb50d0580e3c33fc781f9a8f76cd5e1cb43e1ac4f966926a3735dad5c1edfe58dc919e45a5d1eea456802256faaefb0e8210d0a443655e3935daefcbd44c8b43aaa0566a2305d18658e41f24b1b09330b6ce0493284d2f6bbd185c8cc2a69e04a2b419530d1fd2bfea7da8d4292ae8fc9755692b7631e057395c07c7414b811f4a1202352039ee247fc55782c636b877e9b6bae601da8edaa8fa797b632adfafd2c7816048c180e66ab15699859e40e6a30bfcb2b2333ea8bca0edb64ebb058155c7c84b864f3d6862a9f9e3c24fdca606d01e6423e52f374ce8dfd299a7540ac898c1055b995e2e08077f61f2759167d4e0623784bc74de873154b60f607a246372c03475c5e15efd66b3ac607406380c3d6004c15126835aa7bbf25a3170cca813b35d8d379f0623f63d88aa6a82d5a98363c01a231340a9c11c610ed85baf9cae4b369c82f4addb3dfba467f564c10ff9241a8c25bbb181281bb786c95a7c8cd932196d0f346d095b3a6eeddac786de5fa67697b821e6e69689779d8ea72c10fc712442e34106f2e57e523f820f9f53df2cdfb479902c61fa4a63c3292f62216b1360a379ad3d6659bfcd2c7d01d99a978e85d0e4609cddb1ad170a212c5031b44b5b0d96c8764ac8b8a60cdd1049757d6cc5cda88f8d3832e4d06e4b28c2fc994471495a1413c4fe7af2c8d1a8307f14138858494287b058c4d806c697aac8980150683fee12008cebb26dc2909677002a358b3cce429a63f0a7aa47a05628c933556a991d28a1ca404c7f3829cf853b56ac9ff40ab1101faae136d742fed3fb6552e4e89631816b970534d1c7047693e5ed6e4bb06a47aa4a6667ef449f78b7333aea83c0ef35c3473e7f1fda644c654a58844b3ed75152cdaaff795b42699ba959fba436b3fbd121c628186f8e64575132f6713fd8d2b6a3ff688ba75ab7763a4d558f88cace2cbb4d9483724e020103c192a7983304139c8ab82b4a6848c3b657b0530e0c9f22ed2741aece87990d5717bf920e49e24c1a432bbc4739c60eab00e3181ad7f64a979617f8cdc3348e3fd7f5a398bd96bddc654ab9051ba48dd03ab5077ece69eb5c05f5b28ff8588ee541d5ef186d93e4e510a372bb63b94d8c1f01e14fae7db329cd6034be2789a65cb2ec31a212e3f036f75ab6bb85c7a36067c613c744cdd6be9c3b48b631fd04d5392f704a84b4b9a7bb33e7dcf0aae8406df79bd1863dbba3f0f027ad2ce99b6d4e64044424baafaa58546cc4e552505e2499d2a0a7329ace969d3b51ef0e1798579f8b11a66900cac776fd4df42b353f6318d9d65782d8198f451a2c9b5d00c23f6d01fe9124ca648c44193d696c35490a3d64940f84715823cf816c621ffc6c8c2aeee61fb64949eba6da11eacfa6553c7cd287d73d676c3e5a09ed5049178b92e73e40cdb7ffc2aea0e7ffe92a69961d49bbe5455f6b697fa8b54a4521b79fc58423d304eddae42298397cac40f0104519262185c10998cbfae09a1d6294c7e4c26fcf7490c0b0be9b881baadd1c492f361d4022acb3c73c5d9c289c892c3f3be63e1292b4ef3be33f8210cfdedb156e9308564da6597a6082f5e7b37fbbdeb4bce50a20b80b1813b6c175ac7cc1ed6f3ac851cb45b590dc494bd43c98ddfe0b6de3e9721a1c365611d0b72f5acf1a37b13099893d5804c94413ad0df2ae5579551a0824da7e17434748ec97fdfd7b3a220931022b143ceb1a48b3776a928e3e2f9a3c282ee633cb253f1d147bbe5d48fa7ad2d8b05960b898f3aa58cb49aba6ae878c49c8a095466143f97670458e633228363cd28fefe381feac203d3d52e48422c3099c34604c48d10d12b8ce5a277269c106fed8ddf94e60684281aca8a237e2a1df3129f3a62e2ef2067c55c6091094abfcc9a92a70dc0061dc5f257cfd2cc56a7e3bbb9233a5d6d68aade2b96e7905f683e625b9afe1fa3fa75a104e34549a5b757ddda7a0099d5e34f996fc2cdcf8e767dd65eb5a78678e24687c2fe301bd2947d17547e1953d2ba57294e709e1c82f0fa0c9b88346a08bfcb518f339a46bc9b9a2856284b2426fa46181f73fc0895a2dc2307bcba2c3def35eebeae33ba2b0f132387dd6f2f0f860d7f31b70d40fc4df3e2d937f258f0a303c7f412abd63816c879946c68975bcc5e29b6843ce1269405f1c858bb2f6f24dce7c2e2bc41ca269be00a1daafdfc1546d8571518846ffca7c0b76cdb273a4bbef357bb8cf270a9e2a5fa604d7e32fd753e44c19445bf9905bfe7b6c59213a4e465e600be4a42c8c89564c8e453709339bc1cf514ebda4b48ac4531b95db3301ea5bbd499ff6df6e91f74ce872d2348776a01e000e6084028b00cf106dea30a35ce2da7f5fb0f161b32b47e4340695d96aa19a1c501b79f6973175a0b46c2c9e58bee7fbc5da8aedd03d8bdefeabd48d8a83995e608c7fb3cf879fb92b9c4d96bb5199b5e046589ac432e77b673dea14676ff65c9dddf79f231a88c2ab556fbb871285404ff04124153d0f18764c4db16ec88106dfad0f9e40896cb4313aadd53c9d1b055eaf1d49f009ce6956ae41dd36ad53121ec33274ab1b85783bb92f73ac3c54c2eb222be192d73eaa3dcd32b5338ebb13de2caf8d65ec4ae0929aa8f114f7580909b5ad77e67d3228dffcc973252cb24595f87d1a11170a575f5d1bab0fded095cf0b0a5706f4d8bdd88a6671acfd95552ee8160ce363565112fd7acc261cd960d07b90eb1158b598bf1ae5f320d0b420f9cdeced4546fa15dac40459cd9bd45c690eb0e577f3ce220f98ecb813240b55788b8313aaa018e15154116a3044385c33a700bc675ddfdc8c42ca57f3338b0dc202f51308bb556033b203d8f469a60bc10060bfeaefcdb555f60db85b0db322dd495a4f9206ca96703ebd850bf7eae6181c30bc6bc06835d34e2e9d60816e3d7fafdaad2016b87d7581ea3834e3466d1c938e0f1415a6fdf414f4d2ccd47354b60b80a2392e53f472a5ec88bcdacde6763166f065029410b1161f75b026b10b8e4956df67debad3ba49976bec8350185bb6bd303330e66de98f2d20e2a138524b167735dd1a8b8fddd5ec2913de27f53d92ba7c386d24a33a982cffef50066bdc3218f673062e21da61ed497a927616ec6d9d6b19d7734ecfac80ee12db6a5d3251ac1615583dc4bae3b858e06c30702f2db8947cfea509b2201e053f4dd04d0824e82a260138db300fb5aca5a5804cb9fa1840c5c0e14356b6bf700161514db5c5fcf9e48c34386f7219287f17644753ab140a4b2332b9ac37a391881c1e35cd21034719642bac74128afffb7e8b20454f0b2a926b82d43a86d2c4669ff40c52265318fd81242ad278b5397d18912976b48ef6dd27da390e5a75e2e92be44bac03cb3bdfa6047bc30161a92432e07063ce0d9460fe9b93bf54d6754202d62f9ab84f83f9b35875a5afa035b28c74b6107e69de28e0969a5dfe75d57171c90929108c1d8984f9b5327109df005c9682076dfcbd2b098071e641356555b8f11212a0f50f0fc862133e18750b4a792b75f6bad771fc8e2ea6433a7cebd2dcf9615d3cadd6221e5775f6bae7b874058bae7c2c45c5d569f609db3c2b42db4073531701210e5036cc3183901cbe7f8b1a7d50d32c445405b6369e63603772c8120bb482690ee73f11b1b02eb5280ca06b9e0e5f2270d13977ff18a2e8df7f9bb973261629f98362b3efb87e09fbb8e1362c9f9f02e98634cf07c976e564607a471b0619ec6323493e792ed4f7a3b6fcb6231bb110aef60511612578e05ee3d29e4b194c7fd2624a7d6064f99603456dec632da2dc28e69d87127d1deaf61a3d1c0ecaec1105202ad3c473db6a7c466ea40c2b808db438b9b139499699dc0231d30b4ada377c35e3ea6a5a095365175ca39c86af4323e378ab2949c0491c25be7051b895dbe50151863dbcc632e66e94f7f3383b11d208f731a2d17a1ae9811f254aaa3710d6718ded856c34cfa426f442e697c39b5537a912a8a2f5d349cd6f8261dd8eb53c043da799ee5371a1e6b7d1a5c68fe8cfaa2866386ab67c3747450809b3178a890659f4b3bcfa5eb865de400cd1de9439f60e79b90f0c89a57bfb7e1cf810dc334fcaef7bd68bee39aa8d37d6661ceb86c1d221922182f0cd6bf2b43af655d60ba6a182e401892478e93925348c474b537f3fd97c9dbab9ed398ef2647c4914d70784c8ab0f9eaf88fed6570f59dc0c4b52ada9a6280b00bf7108860464b0217a2fad1dde2fad2f04a2eb6aad4449c996a0907b4bfde8b469add267063c14650c8b59f930b95ec5880228ac36b72cde0b637291a12b63b1f48c17a6427c2cf3a3252edffa31f88a14706b94db08479b008b067a8e20d24f81c8d6342f914d211303f5bc2132c4fa92fe2eb00ece0e4564b140001428ee28af9b4fc3691fd3c8e5dce17c8347b86759649b818c2f1f0fe6ea3989224018931d2f48b504dfafb915e087d3072a5d8a81a2b443c9910169d620f52382aba5de9fe0f59b309490c201458d70fd467548eac6371122e6ec55e3a5cff79bb03064f71736794b9140bcce4825c9586ef2d8ea546fbd2e6e53af1f5ffdc2094956c1b7cd040d43844407b52e2831404360409e73089eae6749f3ec19a560fb5f8c5704a24a250fe40ff6c999254b0ddbfa99f8cb77a7bc68611ef32373b0bad7718a5048fd264c23edf052e7d36e19e1c1f3e79b958e77f505e83e025859cd3b9765cba461fb54a8a239727badaba2aca325a8462d6a1cc1e911bef6e0b8cb4d057e273276bf07c16777d2f944658c891edc6ba57eb5cd042d21910369a20500c98edc3e4e8348e1a5ece32b531b1feb3bbe5ac9a350c21a9e29ef7ee003ba8491508164cae83d99bf60667a68490ab7a3f1408d12fef0ed0685205d12f5d98e0c30474a185a324b3f090398bcf95317f59ec002f4cd545360e061123fd5e2c428059c0b0d9002df4deb1b4a89b11e1bb8f17690915997b0cf85ecd687d7f1ea05ece5de45e40d85de04606bdd9cd15924eabbdfb134200882249dca5470d7e5eb80e871a4684270b2037bb7db39c510b0411551ed670258517f896bf0f20f3f8f93c19321235c9bea0acd32936147ec2f8744edcf1d78c06fc27e1f9660daee075a163faa1837e58ad287ff570d54a73b1944b5cfab568eaaf59f3cbd1e938ba155984fa54a21796c378bf7add3079f1a6f03be92a2e7fe7b63b11547ffc58068b157407d7d0e1454f34e6b72114166c667d28c172d12194c4adf21763f6adbf98835febc2e08fbe4419adb970805ee1d77522d4480d56e5a368499ab71bde410c287325d445e58d070426ed573140542366ed74037e3f384955858e9b141bb2d16231070f60bc71ea5a5f665712343798d6d280a58362a2c6f1f82ee4b80028beb1d1a6f66702bfa0152981ec178e6f151f1581bd7c7c5ffb3f97f641351c8ad2147748ea43c597835d3ae5e285357fbfc2ab791bd857d32733d7713e06e480685d9c564bf5b330c65e4c48146a01f84d4b528d6cc436a8af57b517764469752bc64732cd23387111625f4dd157124ee57cc93c16e5806d71cfa3066eaaf9e94287a7591fb1aff2720c90ad30694ad52dba38b01d640ae0669d047c432500461c7ef41fa1e61ceb99c4505c3e7ce266bc6c5e6bde2b416d506807ca12fc798bc459f72b50e61084c9b026dacf9c36db860c2d3bb4d272d0e9450526beb4b7ef3e721b9b03663462862fc01afff4cca725128bf1d7d098b47a29f8138e388fdab25527b18591958404c36dc88803a9d8eddec763b508ed8830956269d448ab28f7c8020630d3116cdafb0f88ce3efb0cc8a7b43609c123989bf5025c5527f50e00e8937ed745c7ade21b71b494f28cbb759fc7218ada17c6936b1f6838585211e44e26d8701203be353293e84c9e76a680aaaecc60a15333c93e3ca193ba3bf3fe5ae1d4156651f4f15cb33370d08b88c36f1b09a2fddd5af862c3d2fd981089d69510141284fcc3985e243aa62778ee60b79182e08f61f7e2549515455cced5661a7d337a534993553382e21c29ae284de6a401aa3295369e2379527320f6c670983d93cad09f80a5569c498a21e90a2727cfad5c4124026b73595b94a510fb211d1e6b1472dbb31b5818ee9d07fde4d62edae73712b92f4a395ba120e2b2fa6b263b30e654f1a12b5bb8c2da868ce996140efa81a657f0055a33613e26542729e15e8466d2c721e2932a2c772634e6a79c6a632ea9cfa871c9600f8eac2e6498d2832411433cc199b80fc55affc6ff13e4af3cfd175bf72d18dcc6551618fa7e2590aa0df5086b7f8e61b5a3b0b0ae2f4ab12dd76b149797a9e0fdcdecb740e8758dbcd0c41fadfeb025866a7114c812d3a0af938949244856c0d3a38edfe0486a01d36fe52c2793e0377cd4ebe98ea2da4d52b9c67cd979fc98be92f155354e797b625b956df1a90ffc57845ebb5a02d08bb83a55d64db9071b2dc17f7e49f1ed3f1d23467bab2334583f3f7141b89594e0bd6690646f5b6b9b97482525e9c284b55731f3bda5a163cc8e17111137cba9a2e2c0fe29f6b9fc2f7f0331d95246edff985f5057d07f7742944a59deac054b3e0e1cedec97c53db2e5ff4eaef65ea7a82f3e1069257a8a965ad5bbe496388e825e35073211c004e0356571f511958da1c88793c52ba63603dcfac61bc6dd0895975a826dcf3e08343fd5bbbe7f2b0736c044b14d44a7c71883751093e0c881627207e680656713f908d098e12f97c242a329b1da73d24bfba031f95da9fd92d9d4a0d90add4e6576adf39f4a32c849adda3b26f1450bf9bdba5165c044af54c5df3cc69bb40fc24ff0d11823130ac8ff8f58433b72ad7a756c2d3fdf95feb2c361273f92a51d1a73167cda447d75a9093fefe4702b951d1a65d7af31a467f68a014354dd4980feecf344dbf337f4f82e5a213322779afdd4b35eed445fced75d332d1d08909258ab1d23603a7ab995b5fbbc3199c54dd3aec5340f9bc747a670e5a2be5177a089b5accbedd3df69a6cfa08a460322f103131e80150e970b2b6574607d4ecff9e3606b811535b737a0fca7a4e5279e4fa89f9dd0696270e1b32a87a7212302b9b4146996ee77145f5816869a01409f7da24479ae036d126085555b9616b9aa9012e505a25e9b1db869796813266df8e55f1038176b607611a10248aae25a2c8240d16798aed9693500147d3111eb61e1d8276853fe880517f5d9436ea88d4949f27ba3c5222685c2772ccf3d46fcdcb17b580309fe4c2230649d0d12db96342060926a4a1cf520d866efb87f735beb54c6d2d735a6cfcdd809d7a9fa5d0a7675a840539988d2bea991dc9fbfd65bac0f93e03da36bb5d0d4b2067bf5ee63fb9a5297c01954be566f3d3ccc4695487ac022a784501046ac60fff3d99cdcf8560fcc594781f7adfeee150268c7b702c63755a8491c1b4520b053c439a386d29aad067d6ef66c79cf189d3810ee27298f406a2f35c7973f7f7cbc586d8e5f5f8417bed06aa2b77b3545dc049d2334c7e2588e891c6f9fef3a163506ed5a543c82fc67ec72aa990edd3c7fcfb132f320f3b53eb4cb2c43159bb0f56d2f5d40fa0f9c9bc5efeaec7d5efec6ff88eb929cf4380dee1e4a539c1f981fc8ae10fd880509b0f1db91829c5720788d1ac84654a99f024ab9486b51b93a04ff322b9caa92f57dc8cf0c12436649331a06a514664cb9086ac2813ceb04fc8bf1c6da1ae52b94a2087ab1efb23abdc1c78c1624850e1bffa08e18d0b703f053e8a7ef278e3892670f2b9382fd32446fe3396e11189caaaf440fb9c37ebc06c57bda2e6ab36318df12f36c108a9e82c008d89de6291cba86cc1377b752abf1c92bab2694dac3eae6fcb8108d2d1aa1895e33b17005a45493735698078b1d320fe7fae0d348967bb9ee30dfd7ac96410428f113f535b8b5729511d5c06b1704d799100c49d19c8d11a87963e23a36ea605bd0b1761e6e82fa6150e41a906772e51bd8c8f4974e1c9137630ee422d84481f4e38b36c27c2d40ebcc8de3135a532c816ebae73deea996c10ad8fab3a554af680afc95edb1241c28eafb49c6f9c4020a5f800b60f81e1cad7fe7c4593d81f35180bee5c6ef34fd6fdcda41126412759a14c9dfce60cfee68996d48fe4613a5e0a7f0f12ecd1739c55860dee8ae5fc58873995ed4a170b381f427584a73f2b99d51efd6b32714a5656d1ae80d8d3cd8c59548544e5839d8e2894ada27db0d369fb1ff170a334644518455c0384f87efd41059ac0949d4539e7a9bad97223768900fe220ae924264dc57e838f1f6a75c21993ac2aad26734298807a11615a58621fe1e73f3ef399123df1db588b33720c65b035f489a034e3e88579e21a6bf6dde975d56ebc497af83cd5058ab3051ce044d3af4e4c38ecbdc1dc51c23b1146bc6524f86342648347e2fbdd87f3ab7d0594eac6fce30109384b9055a00dc800704ae1f037601b5c2dc34fdfc36ec7a23bc1779d1176a46ecaab6f6ccdd50d5c7f9e7c055528070b1255f8397ecd39e2edf28b4046a340799dce2cf79e3e477ae0e9238323608e377313853c39c6db51245e8af093913f56159936a34c5a762d5902880c115172ea1ec81169f914eda77fa4ac64dd0354bd0bef2bc187e52d8d7cb4b1705e63ee85814624dd2967dccaab6491879763e84220294405c758eb08790a7a6961ed255c6c8bcd7a7b1d51eead113ddb430fc0000ace8da0fb2c51a1007d52955df3ecf0547ed05f87a886553e2473ab8422fdf424a872bcec5abeebcf4e1af765ae635520102ea7f7eec471e953ee20ddc212cd45676f35c0ffccf2bb7545e11a61f389f89a7cb7043575be348a7c796d2492bd9a2e1aa46d8c09a229b4c8f92147e0d8aca6aae33e8027338eeff44aa9e49beb267ca657e9088b8fa48d505c195c8d6bed28f2d76be47eef2085053cb8e17a35ced94159b9d5fdfa7c27e41e248a9695d2fa7849ef7483889cc3803c8347154bd9edab2d75b08b0166160a6a462d80bfecda7c8578b1bfa57aee966c4068ba71b0c2b94de9b577d139e4cd2259b64122d9a08c528c4a8d125bc36a374f764f33c5ed26cb4989498ecda15879549c07399a09843a9ea14bff50aab8f5f7e34d06300ebfb460a40b620f2007804a711b214867131bae5fc1a76d0415723e2e61cd532915090a9a2b9fe2f42fa112b56bc145960291f3ae046140b3aac32adf713d08014bd2c5e9130bd69427a717a857f942fa7dc13879545f6a40ab9e36a46dfc9493da9204f5275077d9823e45a8b00fb27876d6a167a546750c0a81bf2c40f6c9a82e8bb604967e3b3338fc2ae51cd92285d5615cc433c1dbbc277dba56383cff2dbd9830fea433ee30777e662fccc9ef3acfb80f678f6dd311ae1a238cb0f3f98fc881191c17404277e698205535e6cb1d6ffd6f1d16b784eaedb3f3004fde3406112d4a04a79cc9362a813b82b261378f0d6cd0b1dd3f2a52fc68f873629c39f5659b610c11bb2581b52d2fe7deb880cbd38721b5b04cd2489106e41a55d3b98b93c097502bab13f1b62c231cb4dee4c7e2643a9a0753afd40f1798c7646ec7b63c8aa856c87e858a9a17318c3b854f2ea46edbb8a03e9a0c36b715bba1c8745b29dba2f85734b3d576c2221ae342b9e899b002b651e979cbf9535370b49575a5daef292ace8fa39f23b430b1d7a23694c66d499740cc0434c98b08795314d338887906aa4e368447f1cb6b802dcadabb1917f5284afcf9bb3eb15f9c3772e4d92c12599c48b120c6b911472d29134cefdaab2d6d0f9dc41ce91415b069a42ad9b60570502954a1e54fda0e289ddff91e45e70dabc94f06471afdae2684eefe8988dc58684ee5e5d60ace073fd306a2c7a0358ecaab40b248dadf0bda3992f8d4ac5e9f5d428361073a5843ec908306fe8fb1b15f3a0ef90baa20c4a971a7103d783dee905672922f52fd23eabcec2d3c07556bb4b0131553a6edd315723ab630460865bc58bb9964ed0626fe5fc8ec13a75a0303f2d0caacc1e745edd2e44b008b1aacdd84a622c692e506f79ef0aa38a92f09f96f4c809022afaa18c85961a6afcb85b409f6b213b07a54945fa02114e8833ca572eeef9f56c8c0b0019d041ed1b89264b2bbae3afcef6152b3a870c34c399f573e4639225bd4d707508695d0c6e60b58587e4f31da3f024f35f039d30fa4e2353ba3d993decc70f75c19a33cc11dfe152b182602791139bf046a372a797a788b18a10b412765259a808821ef5f54e1e3203f41d89c8269fabf2c040cd402819958d869fa7f1e23c9a3730f1891d35c8a963b4f6132551b9e06adf372124bc75cc4a4b37aa8b1e4f2de00ddfe5a4cc6ddd356df4d7c9e2ce407e7510384baedb126a5bb5d8b9d9d4c28d010e43d77a8e61dcbe9f7670f3e79e00d8913a9c6125b2a5d5575736f948fb5d14180531a89068184b12e7490718979b9cbd10ac499bf235b469694b33814a41047523350a1ca1a53e9d60d0d3c1fb5a89452000bf2d90e48dc7bf0a4637355fd7e8bdcf731793e7c70964ceb6ae9a0f57509750f0473b090c31f65d061426435c9ff8e8a8d09e56d471aaea243c703d712c4159ecce534c9bdf1cca05238847c32ed6d4032f4ec66ef8463fd121452a31b99898608151a7cc240e1bb6d2d4e6af58e3c1d25acfd99501038134f98aacf9a6928d65d3b77f6642e8ace89f57387c5410391e8d8281fd8f9364d2e67bdc75346f90b5a29ee7e9c62d5b8f164738ef1eec51ccf009165cdececcc0f498fdc4085a5523e577a1e10cfa81218b418a75ed4c0ae3f9ef0c6827e7ecb2bd50aaa5b1d850f538f7473caac81e42b90c73cd339bcc1a0275e2be3b2344f3f3b7cd37cf012e6f2f694c09c673b9417d36d9c58c229c0d3884220c833814e69c7775187ddbf8496915b2958f06a23bbb697a27d2a1b11ed0b814ea48364dd210fcc1782d299d5452680f5f62fd41e8c9279779f8280fc1b461d33195de9bd492a229aa8791c3d46209fefdcdf5ad129eb0afdde4ce7bab97ecddc2633654c3d4add2a273130c5d3b580fd5ff4a09aeda53292696b1da166977a1944a67f0b409306c09922991e960b3029a010f241c29b8263f4c14f3c3cc4ef69eba296ec5246c2ae007668d2f3e57b2ce8252d716298fda42cf462d9d548fcd90454e47b28e70a5100ce2cc1af87030d8174e24ac83f3b9ad89b8fe26664d19833158ac536d898f0f855917550a2c4b8c16aa8091885368e30d1988ca26859814d91e3d06c6d3e7086017e6ee2ece2ac5c47e60d9b6294f14994357aefff843364fe20403fb521fa224c188a0749c99e2cc1acf083803518a395b56493e27dbecc624f7b36c96911db1de7fc7d65e2cd96b5c55c1dc2da3dc812800ddd9ab2ed007156eb6e484d4d0289c41a7c09e758b38fd186a4eceaddad9cd379cd294e16539342dd0048a00324b9903ab88e2d0e65772fd4cc0a0cc8e4d703de14388396528e178e634aaa945b56fcddf8234fcdd40a9ceaca1ecafc0df020a810c108a8ab618156557ef63391ff512689726c28b3ba600ed5d72a7c65014e2cdfaa169039770be657e8556ef79d4efb3d118054becef8985a707cfe9640048553ff9d03ec5c3c1f62f9224799dd3809b3d74553a74c0b59af165935cd94684b12c852a95cc67b1e621dd6a10f9e6b05eba41d9f320f12514138fd695ca095e1129d62b86e488a1e0db9dd7fa72922da87779900b218fdcd8b6a279f3b60f9d03cc31dbf60dc95436ec5a60c6b0b0b58f188aab465dd078f4d1c0f9d0b3aeaa69304804cae5949c33ddfd0a2e9d518e5d6e0ab01d6268479fe68c468d4f81f4231b719a1f5cf189f3612bb09942737e725f0d2aea4372a081bd33fe75c9e1de2470c167a804352d640c6df9b5fe8ef01695800c8e41b7c060477876dd77596c06fe77e922762467e6929e5fc55892bb2306e442cb3709ed98ba9cefc07a4edc7f4936c54b1aa284e4267ab2862f230e679905724c3e63edc88fc85d57b415d133b349385a2290c595496ddc195c401140ce7a440298bc1e2bb59093a35a4c11f6072e31ea32d68e54beb7c0126d77927326d4bcabc617a26b1001b9df42ed6ea293a67cb2de1a81d06704843fcf8d4d8a5437578ee0dec5172f9bcb7ec1f06f4270d831e9b850cce72188d2b48c8aae41b60d1887e59ccd0cf73389f32685655edf74e75097a0f1b4386c805c4b27e2bb6d05f0d99ca29c1b9972e503be072daecce2f687db62d42415a6f8ae105a7cac1d322bfbc2efa063176e2b9406551eef3a8c83acbb4982959fc65d3f3345bd35dce5342dcb0c62ba88db3ed7d635ab8259c0384d4b3a9920d2c545a73398fbab2a8113d6c267c0d4e9e260cf0711e3c7f44bfee2d3a2c496cc74fc218bb686b66bf83d0f86ece903ae851b0ae7a2eb5e0d4a8e9d6f2333c62e2564372d45814504edb97934993a5beb70a2f6f7912968bbe94f310131e4115d0c4098621b262b1218d7be2ecb10c34f94da0d6c271412e4c488a696af0e92c38e41c36bbb3c46d00c389c195bdd26c7c5b60253cc53f176ce68d9bed0ef104d8d7942144ef00c381114efed0c6a2034d18b3fa654d0aa0d015818f5f13c1550e7ca701d0faad96e2737db32630c1b93b7303d3a00a6aec3dfa750216e36b1f17ca9a5f1f1f9d97e229a44f33600bbffc0bb1a8cfa636e52508992822e6e16de8bce03ab12b88b160eb2a13a720e02bd730dbaa93aa1a0233cc1fc8a6a37320214d7f3f6a47c847e5d98b86edf1909c9bb5425ea8b8dd605c555b9af99d6d364637b8cd2669763bd6a97812105ada4d1f8e8c1b296affaeae75085662aa4e482077a7705126b29fef73151895b021804f1e59caee865a905cdb26220d851b4bd43f9bec026db80001d30960b474d20bf8dc27b83fc3763662e6e2eee18dfe4a54ea3c214357119d451e15629d81b3a0761a62eab8fd790025fa7fd1db90036de79360c8f13e4216850207b2add935bffb8fbd84944428c8984b6e83da759036e917ff89228d6d1030911bb7af1edf9176093940abf011892aa2961214eff91d6e7198b0841465f3c48383e7b14b0e7768de8caf0223d4ceab0e87eb5d2b5c6a59ba0063e59692f8ef80c7e8d114ce64daab26f6c7d5a108e8a75b80dfd304192532e5145894b539c4b48e7286b96290da4ccec71a998838eee42676b332536630a497254642925d001dd9683a75a6c1613129326ab5887b570b761f7f71f0179b67cac401269459c42bb373cdc60e777d845572eef41d97f0dc6e3966e1183cceae819ddc72cd89b544bca55edf66b0aa985acd9799cf2e53c16f0f65758c5e586f6d108c4587d74c981548d0a03a2fac8755cbbc2588ae96b3d52e9f7f592f56656dd531e40a8e4593e94c24273efdbca41f475757a7ebaa2e5c818f06436f3b7ad6eecd79a2b6f2c0a997dedf0a590e00f6e899ba7eabc83edcdb80ccf352f8bb63a693da8d19e94319f8c347b8ce796cb7d98dd4ec3f3f892bdcd815ee11a45ca79c80445e7cd51103f4b59eddd2abb059a37ac89f534ed2fd59db468f55f71f3a5dc7ae3184d11efadc0d37dcc785298f8c4cb423b86cbe1e197d475737c393ff70d90f45641921ecbe02d6540eb57bae0ef06bd8fc3706077ed22f4542c0a5b3fa1e1694578f06d3f8b84d24210e2d646d1b21e0b2523d734fae16e52cfdb4b974520f9a0aff228c3d145a2bf7ef175cf00d195e4c033077c219760b07f218298374b4810bd88680f43bfd788a4255ac22ddce2bac8ffee4b623baa02d32742a36c4e1b7995adff8f6d7f634389af1a817ca613f36034c6c46eda69a35fbfbb9e2e2f286ddaf23d211ddfebdd3ba488152aba337f0cf94b38d05aa27667050f2d99284399324c5440b0364756a9288baabaa092b5fda3581bc18f65c5545d34c5390ffd841ef156e0fea27a457497822b8cdfa32c2d231590a1fb045376be3f68120821e381d67a5063aaef6ea216696d4ddf9eb16fcc278fd4fcb991c007b98cf968c96817de81080772797d0563146932a7dea3d27a442b5230eab6b18112c731c0e057ae005176c00329bf2d420c36111a05a4c42ae6763b765c565a6388d056d44b6a5120d1f743de0b30581f6bba2310a2345f14705d4be19d4531ef57a630449de45d5cb758fbe5c9826770499806d939a67214b00895eb4815356a3513227fbab1d4d8b2a48289721e29b27bc4ae1ac60ade37b257b853bd45e1678e06f1e7030cf82bb04ba2b60f555a7e11ac54b0d6e8e29cc5d43e09b414c45a210a1bfc2356f2cca21995f2ebd3181cdc60152ed6e4fb262d0d25a53580c41412b67cbaeabaaed68dd7ad8fb9906bf04fa6b4ac98e076be9f77777ca496efcf8f4854bf96b7a32d1e4b5c1e53c1bbe93dd493661822669ee8043bbcdbb1bced0ece62d4cd7ebf295b3d54487fd740a392bd1f44a240c501342d3503a01251501bfb8bb73a880d13fb2b13af3899ab85a2e08715ac1caf72d0be5a59aee3563a98d3ad5ca8187f4497528bbf5e133d85e5bbd722b4123f81bb5d5ee7d950f513aab946174465bb31ea6eb17a70775c42ae6778622e9c140eac4312c27a4087633d36e913589128fc60437b3e84b2201f0cfb295bd6a4d7530a1cc5317ba5b15a544c1b69ec1718ed218232b5c4637486a8660de775d9ceef98d4fb00b115daf29422f99e8b5fbf2ebc6c5148e10fc5f6c8694885d4571f7fb37c9b063854cf89ea2d2d39f15e6f5ce63bb778cfbc23dde52b76e8d7ff8309bf489e9bebe5365f993fa38c73e63028ee508d8db0a49395551f112c72c58a19e1d336ee9570f92301a84ee7055dbff0af2ce9a52659140e00727b5218d41710c73f5f238e15806cb617ce24f0fea3ac494e0051e05d3987ce437b6a8757d493bae7c7f302e6ae171c6b1b2c83174eb68f97dd860c45453b92389bfa86ff6d398b3251ddb803660aa292f5860ff53316fa3b92630f2de7b8ca32a3542f37e7bcab4b9cd4fe23e22ba13de9279b570ac85a0380144d8021d4da0776d7f9fe197d7d295f785445c375e3d36ab6b63a144f9663030eb8ef5bc95978f2eb31e58da72d7b2bc4d041dd91b929beef89ef970222327378d204de65d382a884d1ff1db33bd73126dff5bfbb79b0d8a5530d0f1921b186d3d9cc1110dd42f3cdd21a76eb5df0049f9ce22f92aaa780fea41a6edf859237c14512204e882439947494aa70c0e995027c63867a0d197ee962e80462f7f709b4f4ccbdbd85e8f1157f9b3f8d18695b6dbd845ac7696feaa0e9efdc2c3d4727d9de06d44e59eb53aaee3e6ab5a47dcb217ee0fef6f991fb10733f02fc45db4786ecb062f78c6e965c9f615d15ea08e9921c94129c84e9927454997e960d68fe428886fa660c189fc1556137282cd8adb3fc1439165aaaee06b4cb43242bc7fc48eeb3fd21ffa90cc3293b552de3b0c957782458efb7f36c506cab2b113caba8f8122efb422bf48c155161a472f48050ed39f20920f0b2ca0a7946a349b16c549293c344d6c765147f3eb41566e34914c1ce1644a6ace4ac34c6c2a767079cde535f7eb7f73b71538e107bde9f75050578f8933276a5b9e6306e71df87045d9978f41ab42f12254ec90c3e1bf99b6ec2177af06d71fa0978495f806505e234746ac83c61c4c049fea19d4db2c27eb5fb8c1043e4f0b7408a62d78a4f3f04e8c5c59bdbced554f478b45f1525fc6593a8fb78c2f44bf54f9ae32ab28dfb67e538fc513fd25bd48a30a0eb704cb7b68f1aa0480507ff835b69d64d2acedafb8a40d9712b34537956bd2b5e61dacb97361f628c7178008d158c3d3f60c6fc5d50250933169ede65b1ceec4f18ab72857ce66e67e5a55340c3db867a6ae39f68fad674b4ab21147e3302043cab7e327f17bb592050c8e42ff5ad240e46b1e84a4bf153ce2236bb18aa0410717f93b85c2d31aa8f512aa50bc92ed419b81b9e471447e50f3551dbd781d8e18f3e5c62cd352bb74bd4e0f9d1a00bdaa6355d950279256ae397b3640fb0e8427baeb8e064bc993a86b25672f680c89922830a2a0bc07ab9cc107573557f9f79b7f4f5106be058b81f872940a499fe321d7a0a87a0ce60bb1b5f7733c3764c5369fcd8a5dd5360c4e5c6a33002b253fe960d3942592522d05530f491fc0b37ff9383fa78f9c74fd3a3231cb348c61bf637c3a30585ce5d8ebd36bd47ffbabfaf172ad20cfc9cc7c7b811e2ab5336f0bce2fa33620e83a1e4856941efd4898f93781606d96e8238b25df0e6705befe128bd8f0bf93c99199ac6e895e74aac61ded5174f442bc9fc1fd21277c3b1dc0e448984e871451feebd455ef8b74bdec50b0f87acc2d910e7ae0d461b3e1e7fe7b2081f199a4e836189bddb9df9e9b225d7434d596e0b9395ab5f96b29abd159bdac35bd1d13beb89cbfcb883504e7d8c471665220bb736692d3a66900c774e3a7a0e53254ecf50481c8e40fc4e4e47f76c9827d323f5772c29daa3d52fcae9da13433d4e31feed16c011e87338e36eca15c335e70c090d2bc36d73c3d7ac10ca0ec18e20b7b2240907cfdb76617e404c21f9db59282e2a55d6b604ddadd7e250fc45739bf2ad6f5b7edea60e46e73f4db8ae734d431f5f615f4c10993edb06cf3312b78bf3240ed93fd12a06b0bcf89c3849eff1abd137db6a68964c90813339da47be38928f10287863354ede24341bae58ae33ca4f61a8cec7a0f87868ede3122df10ef13db253bd6eb03c738beee61eb29d7d7f065f5604e3e3e2d94d3ea0a72eaef4e27ecd3c566ac85316c9a95849f1be7833cb074d641c15f7d0b579612ec00f8e8eb643f811a982b7a471b15f73c8cb2383b807210b2326a81b16588c8952c174d0f0192a5ce2dc1ad5ffc6daafb939042c863b2948a744507c92b9af39c7a52edbf759baa73c039856097f6944bc6becebc30971c7b5740e274588dc0e03dc265e924c33cbcca15e6d8b23d75df0ead552a885b93a35036cbe6fa7302f5fd75a0a3e71ecff5fc25e8220e0252d2d3d30aac60fac73cf3030897e8c42d9e107be9f375699098f4ec6f063e3d18eab5e4920a38afb9ffa43ef6884d2a48959ae377a27245b8d88f142c60d451a487fd6f29b56ed003bc360cb7aadb2a11c32ccd79c70d1c014131897dd304e077c2823aec17bfabeb7c3865d82d480e8d7699d618cda607bf4fa630ce03c141a00251cf59e3ae9b269b3504b177b0f0f84392b6d882771cb638114e04a1bed4b3c0bced9167e3a0315e2f6f4e51b2be66a9ccd0c1d9a558ad92c6af7a42a41e0ffde50aae353900dbe77803384d6ec00b76e4b09324663aed213eb600968bf76eb672645561263464ba265c57842181e9c0312067fb438958a713a101e5505e86486d97e277745b2e4be75e93f964526630cf18233562ce4526efba2b9347b00b26b59f00fe529ca76fad54dcc2d64c3c0b612a0a35fbaf44905d46a58a46c91ad30ab06a3e122275821b63a6e3478715a0660475c0b265b7014f6159e900bf2f7edd26aa7837bf377cd903f32a483fc81918bca715021f8f1daed1c2642f27738c344a43fdbcf8d906093f3304beb305b4c29e4ac28fd76d2275e3e50a90c3227cef45b59007a7b867fb572a03fa3fa55d728c6d0601f27b9746a6c9ec7b2df5c4607c8e1a51b94b21f618c3714756b2150e282a7b8858b0cd9c0c231161592e1d78b7c9355c90a0671a579cdcca6310ccf3650ccdad43a8e3bd07c1c8166541014524f2c885f16a8e64143f19426e82cf4039e6b4f9d570cb16e4325538974d63c12d9dc1e72a9a8b832abe4894ddc4c61f244a25cbba851f3b83248d5e2c45bb9279b52c922ba2b8f0555064844c9bd4279d046a656c054a0444d828d9a3c0f8451ddad4ef4a295d176d61f967b5e59a8fdc0d2596c20b7c96b74fc94796169acdd99f5c33ef6c4adc08c241dacee1c75d1d7aae0133f3aa93f5ade568446a3f01799e6dec4dc9a60412d94b33276bc72043b1afac24b38259e4d64a8255ddb2ffaf2ab3543d14b2003cbf1d9f40cb7f1337ee5bcc6ed213fcbf288e613cefcbef4a999a362c4bb4874c86879fb296e67e597c236890bf1b860266ad8716bbae8b65b6914f27b6e689051fcac507a8d1f355fff071a15cfbfa23ebd645e6064d110ac1d35171a344c7118ccb24b65cb2cd2418f1247d0123fc109f058d81721b2dac480f404ec15ee66dd3db2373fb69ef62d47eecf1a1d770d7442c0d1927d7b0f5acbbe44ff244cb6f5206bfb5cf584ce5b64fbc818cc6e4b76b39fb2b26021e5373e4b1515a619021f255fb59b257c373144293bd667b1697229a4dc8a5fc8facdbbaffbd66017b5f1a915de90ed4cc711e16e4e587501bc7e0a32dca6a2337d7ef4d5dd1b78c836501171153387619e80a5300f7a2177f4879980df80dc9f3e7970a3ecb527286326a6100cfad5113438c074c3290c17ad48ff7976ec481359873bd3d52b5b6604f17d129d647dc3424d7ed2ec6c2465afae8f4dfcf1aceb1771b67adc4e60d9e5a5b41ee9ba7bb4b7125988dfe933f608a58d300764bede1320f5ce7ecc4a648701fa745f0e456fef499e6dd1ae2d4d38127693ab1058be9397e7a49a1e6871c11038ec8321b1167e163d4d5faf579c2b6c08bda539570bde8d69d8cda6613d6117fe738cce2f10c0092c6e53647d5c0a5135b3d3ce0fc6e81298c2794a0e92d09b84f0995a38d0412fd8c3a4cf2be8aba18601dc020fb1095f533a489056b0f093f8751d1ee6073faf59449114dd11f4ffc3dcaf04acf4c461453eae666252a1f9107c7b68072b213febb4f4b041bffdf31cb23428715e699172e988af2ce87647a1cfd082ac37e327c48bb02acc3a3a045985e76a9498d868d4db2e41996224eca8477b2962e8ea26061472a8b0daff4fa07d963f64c560f8ff1d30a9e9ac46e2e7e71177494c2bbe97d63230e4e7d31fd17779c0fc24599e626ae4095724d26d2931ddb8c2d93282292fe25b550cb10cd3532c57fb8657130e51f23315c1854815bee6228404b746d0d0bdbfb9c191f33794f4ebd0e3ece28174a684d662258e11fa921afe6addf1f2ca4fdc67b1a9f3f397251baa3aa0a3f5c2da690bf8f1d5a2261c7b1f52b2e0755461a807e9b634f75581c3c3cc76ca8e14a34276ab153fb92b6087c8957c761fb0b7a7423ffe93a9c15f79e45ad0aecfd532607ecb534500cab4a8160e989592ea76cd14cac23a17c999955e61c56b84efc9477008f2a7184f9837b9fa19942ac1d77f2b5a57d31274eb7bd5a5fb2e94def6f0134d92bb1bcfa4e9f6d44c7ccfe401dcab7d5059ccd414b4a36265acab6d52826e00a14b35f2c06d35b5affe511f9ffbbbfa921f9848d7a478669bad0fcf5b0bd4a0a71825f72ffa8e349be7e9ccdabb72369ebdc502b30d5b4d4533b6d080dfeaa42233a9e4e2c97faed09cce68e7776cbddb0a38f915f6166d3b8d61a3284868a3e4264de84ed820ae5cf9b77ea916cd0215a09873d5bf2f4a8dc955645309c097150925f7abe9c4b3b58cbcb1764712813485d0fdd5ed7e9fd6629449ac044c0174072ded88c7d9f88b7f3d8c0567369de89472b6a7fc1c61283de51da885bac6105b4a6249ae3c21d69dd249abcdecd3cff6fdd76192949d59c772a697fbebc247343181a52b64875bac41224119ff42b5454cdfa384becd6ce8a6960bcadeaec105d27e3f6c3f84c727126123ff0611910c63da7de35fcd6a250897cc29e7fa65670851ac7cafbbb33f090b205198021769e68f8b54bf250b7858cbac67c5ddf367911fcdc9d13f6da57bad96fd6ccaacd83e414e1c26a4df81eae83e5c9eb1e27c87740fb4a50c0873b9bc841ce05dbd6e271d52bb35623264b8f1d217825ec69fc41abefc877e887cefe50597f863aea86b16cfb0d87d337832ee70a41a80d6e3463ce9d543fa542d9010cff78e9a3d9a963d547c0d11640c24db1317181d438e7f4728851bc57cdc267fd8e83d181004adab908ec118c27c9c676433b6bc70fa4c8e37f571d212d5704bb4c1e730b3a1d85285db50507f45ddd40c5c0a56a4b3caaced2998c7b2ec61d9aa9d42270050f6aa7a80ea9fc74e6e83c4bce16a060d1213a176413df272acc42fbb261feb0b01a3f06376c0c75d9ac6f425f101d62b340b1281c43b98f95d310109090d5a465270aa22e9d3364035b5fd66a4d6b4f8e42187db2a8f1adae9eb74b8df1ca542b26123b10ce48bd0ee9c675e8abcb43b99b026f86caaf3258e8d4f5ab50112c42d628b6fe3d92aa7fa9837fc1fb066afea40841bc63159bcaa5ff51bdaa88b80f90d0434d4a1e1a1d53eea8264d4158412d6fe365001cc76f782a3bfd231db439067c5e818663d482e1d8c9e4029b578d191f186b3900debf1f9ae829a2eadbb5349c5d1c45ff77b2c72fae14311a738a4d12bbb7b0b146e7ee46495e37be4142d06ee938564e9879172040e487653fd65f277a0804f9680b9905bbb83f6841cdac84254b5b2520bf523df018fa7b30e36136256fa359343b17086eac2354b22273f7b4ffe7d4362938cd828e3d4737c0de350c2aea1bd3dab280fcf78b9382290f32da0d2bcfc598075582e338ab355cb8b3dc352431cb6c293bb19fff3bdb440838964a4b134a68b8b276a09efd108a9890c8de304d5b9b171ce72607355c8564d9ce5a2077684821b41883bfb6222782165de5d9968d4e1739ef3d595ee2fe232b7ff01233d8ccf439701fa8af15e2537fac737a992fc289a50f49435dfabaac2780ca4f6c30c2052915cf0c2c0d4afa9af2ced61c83117621f3dd16f18ac415646356ef6798a684fbf5fc1e410783067abdcde27765567bbfe5cc0fbad31ff4c6c0bc7dc4400a89fe689c99294f8513107ff113264b790db2852a2d19dbb88dc2fd416974c2373852941bf04419239b859e39145377dca5ee56bd0b5fb2a676814a9e9a97bf4f9cd7b966fd2864f94eb9b59f8408563703b3cd00ef92cc7a9c4bcb23f0cae33cd6aca81d36855b49b59491659080a043a2ebd5c5f6e532e5da3410a24f3ff9988f50ef9d34a829eafc2f99dfd9f67e693e373a742c057bd29c7b57bff66a69f777fdd19f58ca1641c77a45275b20a2f022828baf8292b95983987b608b5089612c2cccda03110394736c2716c80add84cbf0aa176e89fde5aa2d722ee83f00ab400310890b8d0c25e1042deaef85a8361fde4f4b5ad7acfb86fb0741cb49ee36a4055b003cc112ed935ca239dd7d50ab2536259df2bdd5a3091f57fa7bd37df4500ac10e49f333441547ad8907b42d1666844612eb36327932eb3cffcc47d2b76d30552d3ab34c3f2ba26eda5cfd6ff0ecc6086b9bacdae19155e89c1cfbd30e5c6e50ff96d369af1976ada26b5eb4045afafb816994776e3eb8d42deb35a29cd409b429bada4a298fc24944688b279af2a8317f96526a7b8cf8fd2f1aabf471bd61ae350819660763a1a8824f17035f6fde95878dd396f95c46dc6beecb9d4d065a2ee622635afeddec0969422ae870d665006917bc68c5c91c5da7c05be69936bd7e57866303d77fa68672482f5fba27900e7b9926f30f39541d177ffcb5b88e1926aa13ee26c63058d090ebac2e3224d29efa125d68467bd3ae3158e91bc2e9d25d6c8b5c3c7481ecae79188f64246a6033b1d26aa645891c7fd6395a0d4ef405952fc1291f4ca78ae8c41bad9c29c02b4ecf144a3704248a5b61e638619a4da5e58830060bc01745236c33f5685d5e7e307021581a6b24a66b9ef3d1f4ea383c02255e3144979486a26e6df26ac9ed7b52c1745d2eb754518e5bbe2cbc6599407f720f1252a7fd117d7067e9fe27182964bf80bb0f5515b6dbc4bd336e3188e0a0d41e1e009b6755bef9d1300dbb1c57d6f4bbadcf2fd3612f33ecc9f78fcd24a1daa06f4af61b581f6ae8e5aae8587d204ee37861a743a31cd0f7ff6c6271137c1fa5273ebd9cdcb0bf0b7d15d919dab985288f1bb4e047f0b59928ad909f6538977c462e0f3b20c6794afcecc5da5fc310d86ab584598be21fc42bee18ece8fd1dbb69626b692cc69e127b70966273890932cfdb942b416e2a846e670d1e5210de4b14644f2ad7e46ac030cc342f07900ef75fbd02e6fa2917c58407556e1c624db45325838094c90aac053f38fab07f6972b38d5015d2f58b2cfa8f2b3ad836392019367f96fb39ebdee17962209fad8ddcac2b5b727d200dc76556c081535b0506893bd169ad2ac36ae5e9800d9b6a3abaaf3ffc57b50c4824fb7fcd7761328bcf5a4cf708dec166c3b8a9eab202203e0edb7754505badcdec4a6280a271cba728c53bae7d19782ba2d3c420949cc30437775f18943a39662e38c443dbbbce6672b3194451d940430b9a656fdc13cd14c79c038e49195cc3205a80e16e7e66fae799277389f833e9783f1e72b211d234554e097973ea4869221b9f5dab02bbc37e654ae3a36f775575584445fab6f47ddd0f3d80183544ec07c04fe27c3d7377da8e5a147fa557f415fc99d3b9b1daa0a0718a33f2fdb1655863dfd26e402298c99d53431ff8469445dc3686730feda6fa20646258bc8c8b7f7390ef5816f96d10566f4c5251a11c9b7895da17f98ccac876042261ddfcc968390e8ca9a972c1980e265e87eaaf97f0c6ba0ccc19463dfb46f1d239335868ee453285f0e04b6ede380eec3d0ee5e5ed13d2f0f3fc7e03a9db4dc166e998fdb4ff910441c17af1376d57c2efa30893cd58ebacbc95386d25462e17ebf1171385164ec71c6f971c79493abe36e4a6a446348ac413f4d2891cbfeefbe77f6c0554a2da41490c5b92cb092c54f2ffb2ea512d3ba6c4a9055b9a71bf37ea790f248288aa41cad117f3f61e08d64f3326cbd03fe69fd8e483049f2e9ab7774df0d11761a68caed04b8341445e38597716ac612f419d641681040edd1d9dbdfcfde0a01f0f21c4ac4a91a4692697934b4df1c7a7dbbe6b4a4c518c7d4280dfdaa282308694cf5e0ed93e8a652f75cb4a4ae3d04cf7177dd0490a1196ed316fac379997788ca7a4b827f04fca124725ce7445950ff8381cd4fd6f9f6e6d890c2d0b2e06ddc34b7d84c448d2c45281125f0b20bb76280cc66c8057add2b1389f7e5610d17c8d54e8ec5d52a2741743ff0cb363b97318de66cdd30f2826ec4a8b5f6869767ef6decc1a2de5c9de2cf5c1849633785cadd653c067b973d3d89260f1b5b67da4be56aea425b63e2041420a3bd4da59e5fc2fc2d342b9e6a70de6da18670c54d6c5e573bc7e7c5add44ec827526a1cfe3528ea5125b4b11263b1dae9b72923217d3fd4211d941aa3b21224db53a5b53cc0d280406a0299d3174ce83389de461321c011b9536e9195f3fafae08b0f5065848a804710ad1c50d75cc61b212e500c738ad2ab7f140da9635c054b30bdde6486fedc4ca9875f8bbca60d3a20cd21cc627da296f9a81c44754693110c89131e03f5fde4f01f668ebd7818360d94d79259c3aca512213a026357c473326a5f943b7a9801b986390f4ccd4c2a0fb6172b6275d89e1f88e7e373a4ace980174692ecbc229a1b1f805358170c324c4d428a9bcda23bd7a51f3975f75b4a72ecdbe5f3e64437e9235bca6a1bff0b00e39ceed9a6429683527db5e270887bd173bddbe5c573cd50b0f09cbf2a62716c942123d4893d5857523442b1e30321687f29178881cb008b1f039e656e684c0208e04c03a9fe76feb91e023039b46f9b4d3ae57f31c577c531fa7e98a619618bddac2871ec1c5434c227acf1278a1eebbf8c0bf11db945bdbb30e0b5eb5f4b3b23f091b27ecc1933c5f24cb2302298bb0872e4a2f6e4beee244bc8ad178c2c00f4c2e6f9b90964747fe5022098d260b6181fd3b42e7db57141954379496e0388cf78909a193d3414ee3c31c6712145b3acc8915909acb63e1fe73ad3debdcd396ac50257d30661eaebfc35725deeab7aaaac650b3c22403328f21ec392a28b0e0f65e4cb89ed6ba61a9a35b68e7a854edf100bad8967c2a03956b634a4a5d01d3b7ae02538877fac2cc92b201a9a61a40697363bdc751981c20cd4daa3dc24826f5cb11aab67bc6d3d18dba65c27b1b2a9f38b710a2d9ac5bce4bd7d7179060f544ee2fdddd03e09ef5e8e565cea1f2f0ed328dbc83a708f17488fa07e8a71b00a98f6b0f72b2cb8fc76b5301ba43223b15f3d4d1e9fa3949c305990d81db18ee91957a037cac4e001156d3455b092cd94859c70385024276920726ebd209b9de29a8481d299c42d14a92e238ea12b4fc9c9f48e5148ba10027f94e04c215096b1c0eb0cfed18da9bdf29d9261f6bab13e2d6547eadc69342875f84b4b3ec0eebadac84152e02dbf6da56c550bde9bfffd168dec10594019f6157f2839b8e8786ff4418371a5a86551ad55841eac4c9a83c76ca4cfa87d41b7fce61da846f2f5d183ab3e22485c78994825f3cf4c6cb2d34f7edb6f501b13fd5bf4173579c6676208c781becdf5a228ccec52919c489a6421d182eb119412490da8fbd91616c37965b35d116a1735fe5787a5ae7e1891c83497fc85642b92516236761e59b34e248b4b3fac798f284600bd80eb2295e6486aa16cd237fa9a6ad3153e15f61510598991f4710260e13c0b5b906a2fd211c9c4725afb0d0b0bab1507203ee004bab54b8fb63b44ca75eda6c9051419942e43b163e617a3b4195e5538eb55a491835ddb5ea5caa4b9b3b83ff514cfa2ad0f602f3c1f9e7541adc6f859729196ec044e137bcab9f7c05c1f831086e3840d5408610cb72ad2e44db68db1eca61068291510cd2787324f952bcbfc46dc5aa8f13aa8ebb50a058d488e58a34215945d2695cf7f8eb5e34fa5c2f0c882b3769d62b2e59dcb8df8d07e98244c48fff0d23105974c6414434629bb5a70e667d3f3a6b3ed6a84e98f7ead85a6ba3431a9c5dc184e6d3793f61be9ac84d7a2b4de9329d65d29f7d4e9c4b7345c0ee94a48f81a41310951ad3ad66819665b318eddcfe91001e132dcbb99d39511b26658995d6cd45038954ef98160c49a47134c5dbd68ddc4e774d1b34cc1532c1264876ba7cc944e6bdfae65385bfd6b1d05125b75bb84a6a5a1432f880ef5241b9763bf7266e46f3a84dc97121ae735998043317ca6195b5d42c4c56596d69d2f8f7ec8e844522df7d8197c2ca0704a01304f7b695ee156ceae6422645cc61830385ba9b45aed2a9da1b42d58083f524655a77dd2d416aaab60049307acde9d5c8daf78b51a7635a6fb1636e890ef3194c4aca204ea6b925bfa564bfee0023dc911863b8535b67454e7d65f0686cf6cd105f0a6d0d60a4f80778ffab7d95d6735d30991c216e692bfd901746c182a314c673037a8e97a7c96e16249d93023ef7d223faeab3afb95ce2a1e8e0e750571fc445014c96f7331b99837a76e01beade3415d9c64e894fb9f0391051ce6a2cd495196cd1597eb5034ebdd7a94b7dbc21f8e9bdf1ce29152b1b2861df816da50e1d4cecbd32737db84ef1e49dc69e1be707761ccd0e9c4f84c4d903f06f43824a951c5eee923ec80e1aec5384fc81a194f8be6e50a81bf008fb1658bc39d9fcfefe580beac3fe912c0c92b3aa22737430609f6da086da2757c894c9385b82741860314a0fd5e58694bfe5d49627fb9b8607e803d94993ebd43f867659d66bd2c308083d423a9e39b3370654f23db40d0e770e3750487870d3e304f21ab6ee8369751359947eed172f67999034658d80132474d94e75a229da75a95007dc59d7ffd8335523487cdd164823d090697fc6f94b73d1462fa6e2181ef3eca44cbf3ce8ff28efe6333374869e81083d20233cacd6ab2c4c4e9409b441bf914a6985584a052a90e5d2349e8d87898e14d600c45a1b9820eccf25978799c2d357d5c486f89f3dc3cc513baec3a67b57ad2de03e4bf26dcb1927472fd3b134416ae7170444d6284d915c8cbf7742c95512d17e95bd67d2cd1970db041b3d0d867e06f065364864be2eae7b7013c184f2c3ed6b9a9831abbc3064b2ade093abac6a5c2f88b0c7497968f23dcb4c20a9a5faa7266e6d3e077065b1deba8878979749d9e21898663f8c99e099c564a9b3ef096c9b240b0df3e7322c6f86939c3822896a1a70101141b62f3a2e2bcef9b45dbf7bf541ed29d39029dd678304e8e30a812cffa32d7e3f894dac5e04fad5d1fe32862180b2bd80ad6f93eebe9e0672bf67f2e600f90331fbfba9674150ef4a6476e5371c1cc8cadb700d2c41bc8efc46e06aa7e83e450a4de5b60f07aae2348aa39204d5a75b7b156c8a781dcc29df0ea426980f2a446283392efb9581bf99b507e38dc718efe9804c8c962154f2f79be8b65bba6e2e10945ab72b68e2a30ea416d65b0fb701178895ddd208705433e205b95013f7744fcd83331947a81a6052d4850a14c59fa022ea121d22642060158366547445d70387842926718abd76953cb3e4a6a6e10cfddf8d3666746a858d8b590e79e49760b2056ee6d6f3f19d65f898571ea494508db194134171eb5bf8bc2bb6784fe74c93c728ba7c938f6a76ee666615d9d341752be82f6bdc64add6edda2c4034d66930307b63578df9b41780b852697110e7bd3c6c0893b107917ec3bbf4f6ce1116958de74cf25a9b1dae59b8d39eceaeda1fd90a2efaff9e5f1a03f5355227a223992a818a2c723d037ae74614e19950e5343015df8abe6757c7a8932862d5950191f235e06ef614e60789a51698eb5159b0a47c9798d45d45ca87381ece7a9334416562a1b5da3b8ba74f10db5395b5fd2b6188664c8ec5b5afe750b34bd5200ff6fc35a74953837e666dfa0c886c8c33ee78222d769247700a8de6d7eefc6117078592355318c4221c11673c6a8be268b093c96db4818ef146d744edde06075a437eabe5f37513f9cc0381d58d3587f1466600d00deb8320e1b876a40bf1691c1bb43aab8540ee875888393864e0fc9544708ebd8b6798b38a05c69d56eff600b4bf2d3f75564ba902cd176dd7e171bc02c50ffad336d57d938528358e4bb6208fe8922815ca4a6101da061e6f737b8b02ec92e5414391ddbb942f37af5472391c5448361c5a79d68fb9c262e49ee876e8e78c630f8677ba3abba46fba9555329171e281fad9614dd919c6f35b38ad0cb0c56e58671a08c92facbcc03da2030b90ca69ab7ac45c74705bd59cc79e20b65b67d4ad317f9fa1f8e878a573949b0d90b1eec61373e27511679badafd11449be0a22cd67d474a290131e7e9e83b746d2ed2e45f96b963b7c62a2b8279a1e948099c213ccbb355b62c6548b4b92b64e84daca4c089a049c502daa6eac4937bf35796732278ff3df515bd8cc9d28350cb74ffa3e3c46f4c32be105fa2819f540ead29125b9f95e5f90be1f14d3950659f587cab0261b2bb305701b15d50ec38700c8191aa22cca44b0d5722aedc3d06234b8120ca5f29097e97cf6129d1c120a748c93216d562ea7289ce54f522dc71b38929c8ed1700293af2c0361a04fd6e29b6c9732ecafd9f47d2946f367a20244cb276472fe04e1f4a53c963daf229dc46ccb5d78b0308d0b8c1ea30409f0a6283124cde9fb6bee9c0a1259a113a44262a5f637ab749316d3ba8f1ecced2a8769e87b36b6d444c3fcf37e8966a577a424fd321afd298e135767b116b5def3a005ec81f6a5517575e730dff5f99bcb4548266315e0c4f39712c97a69417eee403e0d3ef6cf7ec606a330f5009e96f3efdecaf150c69ed660fa0ecc0f13d1564b40766febfc85efcd3a242feaaa69fabe230219f4f61ef5dbec5c6de55f13422363d8b40ab94c5e6d52a91c33423024be08d6fee7249154b616b1f9ab30537c7c374c13cd55dda62ef7ce9563e2a70b3cf9f307f5204c28384c1ee107db0bebe36d7ed0c855309269bd42a6e1285e9a6bf6156700c8891217b6eab4d72e710495098feda9115fa2fa7e53ca74933f5a8efe21728ae9cf2ab40c66ec8a8494931e45efe4b3a9d67c1914e18938f442ecc67c370c1887ad695e4c7b14080f8e5d032fcb2fc4e14e39b386b2293c7f946cf51db7fd6d2706006c55088c23db6458cb54182b72854e92435de99c518ac2bf8354584a9a77502179d3ae5fa0766b61cd2b2a61e32b82becb3ca190f595d041364e73b89accdf3256658623f1e05bd86d325f614cafad72705536d130ffa2effe463d4a64fd0d1f906a47ec0454689c071aaa999c1a335c714de1c8a60d5f066d73dd8e20ecb11c8530370ce8e8516f76036859a7c801fd5af2add931401e960089fc7b3966bb1eeda99e4f27b019fe876b45db4f886318629e2b1b5178db348ee9633d9d86b1db47ee3943e0a97932d74d2e81d0fad852d39538901e01a23699ebd392918976be2a0db5a7c4dd66b3c13602977c7b2d35e6079c002202524a1049564265bf5dcfd6bd368887fff9973f129c8ff4564d26750fdadc8585d8ebe5ad7074b8e2fd15dbae2e0e590e5b7c1a102ba66570a0abe5f2d0c2cbc1b5d3c2965e17d73e62810afbc50ac136f06876085fa53b822d02f1b313015d913903bfe1d9bbba5c8e140387b5c2ec4bcf736c688f3fb5c54f00b842f7fc5cf0298a8c77fc4721fdc62501d0f9ae764c524f8110ff3854b36b166268205ea1afbf821fd3ffbb04a984817d7ce14a583e5227485291d6f7656ec5b31df45d961edb794932bc9c78cfd207b854779e8e9b325daa2df5a71088492155e77548b15d6b44dde0c623845502b2c5214aece875a0d3d9466fdf0e3f2e694029e15b6607585c0cd6974609670e1be4f4d65ffeb929d71173b4d86a31f3111cd2ae4607cb14064292f60557ce34ad1bbad3fd49e4e5041c511fb3a96ab33150dee3af20ea814ea5813095ec9f84a14a79397a32e39585e572d65806c6a034ad7c0797ad6b4fbde47d1a8f3c6c5cf6e9b9598023f903149d023ebe067e5dc03f07a447c98f8f5c8d61d87598aa1a833cddfb21a23d0f2bef40fa55320c54873d8a902a845d1c43f531a65fc618d782bdea993a03b848f289684fdcb7bc4fe5586972e6556c4702420bc7154f6ea76b19ad7a332ce2c6c92d4f1e8cdfccad40121d55f66d5283a21b07d5cde07db085e3c5cd8ca0b6c220ce4f938007855f1650c4ddb679e02845f4044e3c8c05776df58adcd6c5ff230fcf711a0876c09964170ff14f0bc57ae16763d02c0c2d222ed412dbc1548ecc36384c1b028586f268dfa4682be9261e475d1bd17853c4b875555d5a9ed42beaa33026964d2ac2e5e4283249a6f52323cf276fae90a53e80fc6eec57bead12bc495116afee6834560576247c83307eda7e0d0c640ab7396de5ac3d355cbee2aa5a74632d91cd5609a2495332e83c14be4dcafa4301ee7d4cfe749d2de553d96ff83b3c977a651cfff07db4860598f270553df9accac160133611ec08d84390c0f4105a46c668e5a0d611e55c7e7e98866216291bfabfc431978887cf77ba3f3c9f61508e6b36d0027b367d86db1e587f4a2ff81243b702d2dd735fa361c9fb947ab254c9f37c2842585ce3d1d3e5e7b648b5349866f02b3e983f6406ef0fb42eda35370abd3b8f6896f1681095920974c0e73e83a1c43f3aca36d661a1c5e8238161248c94378bc4c779b4afb2eb6cca80d23dfcfd64e70491b2206b46379a51097f4fb6f81815b45fa64ae160aff4f9a314e5a248d06674c6952ff29070291a8cb25e7e04a5f1d643c5be5abc150c1483c47c259626a2d4ae02c35ea1acbd03dc7f0903ec02f4498e5e8899cc304702919555569aff11a1caf1c6eaaf1c518cf40a303278426a4f4ab6704961c99afab7bbf74d3ca320f617bdb341f61199c8d0713c6b807bc10eea3f112978fd3fa5dd1b4aede860f138810972884179484bc0f330c06f3bb6194198ce128027c63e451dfbf92d65dd3dd1d57f5b7e5fdd46f4ee03a59641cf1987db72e3741710fc30fd0d5f908ffdcbb06cb12a4315deb2a699590165c7f801b2e3877582bcacbd42ed59c3dcc14117edd40aa7ea7d6ae15d02080e525a94ce196edb69c5bb4e21b5bfadbb61c941005fbf12fb82ab4ec10b0815429938839e9883db2dc15709007419f44f5920c0a5a326fc46de68cbd907676ecbd663c6fae8135f9070008fc1089d9e1a89cdba2cb8dbe370b243de5cbd1b0351459b3dc1cfe29f0389c9fcca0955753c67b7ac447ecf0b222739175811023948ae8128ee075b0619db578bb932e0ccd9135502b4e8abf4e503dffacec0ad5ff16a22d91b18f6f03900e86aaf3db3698122eb90d2a614ae572d40f88612b1548de5c4c990ed4644fc5ee5f247e41c27d638591ef7e0cba02535fc12b666cf2aa390501116c8e94ca32c2bf2ae88c39f0c2764cf85d0746b01af5194c10cc956c24b398406d207f1698a90c31d0e36104c84379c86e2846c4fcd8b3a85090c527612672887e8f7ac7df22033c79b1e7cdc3fc4159a2c30e1665e599a3227f918ce2b5fd6b85fbd7e7e9d051c12c89e3ebed26ebf3561ceb1e132995696224d07bfb56ae71563dd1a221647358936f928abdb9ba4b05299c3fbc13a5b18620b475279f99bf0ff666caa18ccb8a7f415fee4ee03dd6578efcecfb56c1fc1b92b0445bec8327f5fc9b901170bb760b8aad2b133e5f8f23047857ed8f6d5ad16096a3a29d9f8cbe5c59cbe77456e2bc1146e7c0b992f25f79a19b8864c56d5442d60a853f70237cc79849f619200ffb49faf03c5351237a89b3eaf0bcf671cc5f03b8624282ffd54e69726136dd31dffeb6d3a3437a35240d0ba491225ef709fe953709c1418ad1949424fd25cb216065223d589a0e1190d710a7dd3e11c895f505b2b171b2454a143cae9690de8f8f536d50907062e9e18976593528fbc6ba88e2e62fedbfab6dd0e38d7f9624fa9c56bd817762f266599a6d46ab4559ca3c512144a6bd7f861dc74f694cfe8d81a30aeb74e01112c444c5c3a63a1e8d9b363fe4f9b03a65771fdf69b05cdddb6a56fa4411e5d01bf19993857097943b1fa2d94249122123ae86cbc64a2e69e5a444afde1bae6053683e166f3e3cf5f89d431b519a503f2871754519339ab8489fdfc3498389f1faab1363619b646dd9bed3ba626bf1c3f806d91e4743fa5221635c491c561b415c058a1f646fb64b112d987b35c2e201a527c4d94eab60f80a2fbe5ba7d767f44530c883155a3e6f897c30fd04aee06b7f06ff92f16afa0c8da5f3ab1ab34a2317e80e97cad50c274a097227b032f2c1e5b72471311b6a8abb2b30d24344667d041f22a41b9702691f299a38659b20d92b5e5bec10e1753de453445a66bc65d8b4b9924493748ec5dd31f45e8675d7cb2bfc062d9a6e6baeea5d8b1ab1fecaab222293c7d4b294ef1ce1cc43265416af4be00c1ddba8b263a5c997bfe59e9d8a5b9424de3100dabd8609655f66914b936043523abd54cc7ab26d6b474ee34affb946f58e9171d6bb1b9901ecb25d2138a26253e94586b5a539707c8c2b84e286e7a2bf488d3266255ab46cf69255e0e56707208c79e32e70d6ea6a97fa1042525079913b167e890c266713370152057d5ace8101fe2fde9d998c62811286b414df9652d75d6a1910509fb213b23baf32d08412f782a21c6a7c8f5f7e317388fad6c936152d844d8435c2e47524eeb328f7ba4dd751bf278fae1857bfe297313d8e7f1c450e218ffe08939bf961f014a32dc54341cc21fd3021724e386985d35892d67df47536e59a6b2cb8bb4626cb8ebd0e9debcdc483eb09f022bca6f38423b997751ce42d303249df29aa36ba9e766766f1aaa195bd7b2cbdeeee98784a89a625031f6bd72eaa6ac088c9d602110a9ff8ce10eea3a2aefa50b3571489a0e5e30e8eb3eed44b10d8c75913d329339942823d55c62c0eadeb505753b7a5138e970f35925e50812e864b8460e22917896c9e3614a9dfb3ac2eeeaf2e0cd006f0dd5c854b673aee57bfd2720254eb88b289f0151b74de78174b259d8600590f7ee039fd0306ead0f3780c36c1545450c5e1f042e68437035a00473950d326f0c9f5bc1b9ca3011adfa6a3e44114ec93ca25784ab0a1d6315c4809143f998830421e1929c7df6cbed622754a2e90a81f63e21fdea2354e35bb3383cc04e765d3aa3e2b03d9644ae8e8a06bd8d85629081eab91d871472b92e7b53dbcb6828e3c5b7868cbeaa9af43fc1ad875ea368d86d427badea59f87c2bb6d83d913f84bcebe06046f435abbb73280aa2be1317d41f8196d1c94fad4d0b54626ef1387236dc0aa094bcfccae6ed839973b40f16e534eeee8138095ca878ac2eeb70d8a1c2cd243d213f78e8552f83ec2467ea1f6d5002126b3d7d8078dd60eb0cc70c4491009b12f6901852bca81d9328d04f46b013dda95a5e133b86638284bc811ace733bc70f364e7bf15bd63fce854dad48599f4aa31c085c070e22bd0e38fdc2894801f897bad337c56df783c12f1bbd4ffbd70941b149eee06449f4dc800993541775f71ea06e5170fc72acc81ec7bf11915e907fc7e076174a4a17863935a1a0286bfad2d8016de076c5c93216cb8c4ae60b5ca702bd327d071f2cd533d3bfbe9f8b1072085568bf82a80a750d2b001f87dbb5428028cf63f40faca3b35186eed6f8c1b347f561480bba8c23049b576940470811cfbc4a9f42752c86308e4db3b7fb0c9feaf15ef8924effdc4fe62e49f3ce8072ec64eb72b4bdf2166bddeb9225e47bf220d1dff887218b0c8cf0b716c39455563f0104bfa2531b12f5a00e35efad81140133952cdbb766f73eb02e6b44d029da2b6983e82d11756a3115ede16ea95a3ec19572abc98873d09103160ee718ee1f41f16916d64305979cbb888b3eb820866d6dea40cff2bd513b09120b75660163d4f70de096bb13ca8eed36f5ac5a16222c45d8695f4d0136f9986a17e561df3c0821ec37016dd375337a1bb865833b12b4bc5a2f03788036663466238c5339b5ffbcf51e3304e8ab0065eff96397271967078cb0c47919fdcb70cd01df7112a3a04df9418785154e65a7695c815dec0b2bc442cb32f9d8df3321bc3df783af4264d9e7c6174265dfd9a90611cfc59766956b01592994086647cf1f5a847f8ec48f3d266e9363b09da2874d4b02be52aa2ad6e85e7adc3a8323ab600a25a639e91a8fa0c59f8d3cb128ae5b329cb1ee301695ed21c6fc5309fe499d5d85517d2ee0463f7f6d4e946789887c0c7d0914bb6f5749a681b89684438eb36d8799b627727c177b1b95ef5d4bca42ee77097ed243a041c41597d423adbf420e1e61749e36b3691094ded8c7b7caf748615720710c21348c150de0a09e0b8a0e2d1215a15f1245e93c5b5340e1107774fc46765b222353112e73fe7e1d53f16e539f7a4cc3e51e98792b8c8120d3201b3de147da3f953e48bc09ead4bba3291c1c3e376323f4b9f8a5f36ef707052d275d611b25cc1283f0e57a96a61234ac4da81fceb2bb8e33eb22ece66b7c69a31f6c82cd8f90ba45dfaac3315a8de3ba07ce806bea43f0263aba5c5ea7bc30939e079f9034ca07be46b0b32cdd2492550ad40748312e43025704ca8410e55151a4492da67098cd2e3304520e2fae0a724e47b6472342648d59c050b2d61b357209f2ddc2d546aeeb548dacafa07d64071afb6dc225e3b359eff4356fea68b9d3d655b28e966fa8d9faa32c059c8bb10a48d3d1780c6c02430d4d8844b7d8ac021e915541793c78f73458302eaa19f21a469682dac66c979a83744198a70bbe49f7715aabd07147ffb73b40ec0417d59440c1f21cc4f6f45f9606389661437ac683cd50abc8eee0abe4328647aac252ca0c312f382fc3f7caa797f86f7e4d939d5ff8e69e06d03fc47f06b25495631673181f1cd52a6c671f73e40ac6dfc13b6cccf18f6ea459e4ba2982ca586d7b6f134c91fcd9808a35324e814625c10ff0a84e7eac8ddb4f9dc750f6ab628fb533c62ca6f4e6cfd777ce41db7fc0297d7c9277b9981550554426077377b2000243ce5fbcff28858dae1c83c86db0cb254cb608c16755b79ba1f73566be066ec17a4000d3da06484a26774e2de654e9ce322b8f5a682579986f133f07adf7ad10e67155e6ed4723c8a7242f4015a87d9b0816ffdb3d3798798c91043019b00e0187e84c37c0fb87a223810f955f4aa2b9a8900940c3cac9cb2c528feb541dd96635d55124e399365c46c0ab2e6a65ddc58e4726abd4f9bcfc446bd40f76403bdce3e43a0e9150909458ea5368bc3d73323be210b6ba35474e21a27ff19d00952c31e307a857f2ea531e539209271034ba2bc155dfbcd564d2e05d0c4cdce12f7416230a25b30e3d34dd1c2db47deae3228a0a29a0b337e5136522c48e8dc39561610c2a32a5ec97259123507d4579c4649610640d5b738d1a73b503bf9fe308e8c1ff258be8b01408ab540ed0dd124c24cd3bf0a294303bce961d8acf1da50ae4339d16f326b5b895cf330376e95a7392cdfad63ab65ea6e3c81eaf67aa349c7c1c9d1b6e1ce6bf9ab13a96a7b0cbe462da81bd693b8ffef424f8b26bd2496246d688249a2c061ffd27dc5a806dc40d540abeb79df6aa4b8b0c8d86ec3a1edf6c190f537c61b2a545b5ddf3534bf450f5606f77e5c7a1c82fa4e6a1778244f344805b0f1abb8f4dff65b3fc6f746c1f7b5eaf2b6da8bb9da17d65a12446d99c6fdbb541e299624df85a74a16ae55e6b957326ae2939fe65ff824a703fe1264c0f5755297246a1cad47428e14e1efa36ccc62fd7f3b9a0a1cde79432a30c2625935c1b1ae2c7b678a3245706d8e7358dd6e89e2db2fc5b3b2dcf88b20ebaac71e809be2aee11d8e8666f4ba8219fba7e1ab4f5c7134055d4e99cde7b0a9dd246e4606f2e0f21eb39bef943f6e0a9a67e4e187b8076ec45cdc87babd47474c9ea58f90d22c7633c84963e3962fe3210b9381e81ba3f3424e777e797febdeb7ca904d9d44aae8f2dddfdedc87f81c53bfdbb685c61786d2ee2d62098cd225671ca306d6f4614d4ffe5fa6a2911f6e82309424c037256ad07cfd6396e06ae1ef2ff88bdd4168a92b486277d949610e5977b5bf3c62a51ac3b060179712b70ba31b88d299f3680b9d988a899341c892875601e9b17636192fe20b52147e9242035d9084d133168ca19b1b5b0993edf29605d39829786503b37c662ba5e28dddacb2e501a410ea3e40780739838c03b5c485ffe5a014d5186fd51df2a8d1d2a039ab0b5588731c785b65ef745b7a47f95feca1e2a7c2e93b49450a8f7362b29da657083688255ae9516f2fba6445138df13539d637c4799e16f4b0357adbf999febc42f8c6f5c905be0f5adf0e6007bd0118221fcb099fb99b9a0fcb911b1ec860520f69b2e595fbe3770f351beef6d1c986755c5663ad55bf7251b405f12404987bbce35be0ac4d2973810aa18d74fa34a98db00a6a01b333833f57e222d513c2564564f680e275df5b4fa4924f249cc999d6df88567580e90040871283d141c51f4b25fb24ce6f117a106f02fee65ce44b23860188080e1453e75cfd05888912b52b6e3e21345a6d3b655d6c2276a792b29b41fe82fb7f0558fe5712cc470d89c3b0bc5344cbfe4c9e79d790b2e49a7b7010b948515323c5fff39232b198b0c7fefd57357731a13b6de81a724b6104a5e60e067dfdbb761a13f180f998a3dcebca3101d57502fa27bb9a69ddedfcaf61678542d6b5c1480cdde69c35a4e6e3eedffeea3d83fdcf243d6fa01132567e2bcb28e0e8359e2a28cbbb90b635c4545f34e21fe5f653374adf3a45e7d57797b850d91592bb8a04bd67fd8ead3113f40715270e6ac3eb9c1fc88e4814e9b33888e802f70cf97dbda6d08e78823d83de2d3b4f537633673151d88f28f3ed7e552a182b18204a2e61edccffc1020f4257ea899f657112ecd82dafc28e1710f2b4b6838ab2fcc5f20615328b9e35ec34b789ac75fe1c7399ced0ee13c20587a2668789e77c6d25a6e78443e253d5b0adbc86587f28de02d7b3f03220a621f3d559626cb34c654918dafd42992f7a0a197ed789606fe404ec1ca367ba8ae35ef2c6ff2d0f4f6311130731d37a9aa1229cdb718f2abeb3f3babd8213b064f4ab8b81417f8e452bdd3e8207df09dd4678a3ab59881a1e8770904e6876abc576b9886cf4e93a7cea616a2b94a4d7e3a8f0c345a02213c837868020c38f2bf249c4af43d55d587b45b1caf7d9fd89a36cf0bea303c0d7b2d5b632a922441e57944084543794c95f2d6e9c17fb72feaaa52781d290b5402d825685cb4ab8a3884fb3359db861eb36884743592bee2badcd8021a197ea0d87dca64c3c556144dec150f5b493cf8841a8eb3ab7111e45d340f793d1805224c6bba38debf9d40f7da33d475c41d4228fe25a5d8c19aec4fce9795d7433ea0159bf631359d4f6d53ce067cb6cc82af96f9c7be9d5a6f242e7e96ac2e3295ceec91c55e8114c70a537fa175067de3d22ce974a9f8b0db177894caf8a64e3239da2dd05092e350abd53fe35e1d310e8ef9c1e749f08156cc7c867fe09a8badb6ac92af36884c0844620e5ffb9029208f21e787e5249c41c225718809588b718bfbbe3d1bc38c9bd520be7def594da478045389b3b9720e647ef9c44456cebf83ff63192db10ceec0d7e15ba410cdb7a51dab812e931e3e134ac72b0a0caa209c021d162d29248fa8390f69ba764a068226b1c96c1ca6b7488735ebfdc497b175583f993510ec86cfd30e280b21344cc831380633e573c4703fa3dc51f5e60a36a68c673c13853aa7a062f31147a689e3c7fe6dde03c61bd381743b6d364f207223450643d981ea2f2c13c46726875cf07e946ce03fe0a426c0d769381b1240b28a413d40fdff7d990975d212830336234dc76b4395c510764a692b3416a9d480f031a85ba85a5040f76a25e15be8e696407f62103d83ea3556352828b3f60cf6631477723c9d70cc96600a12458e32094d7295d555a8eb73bcc8a5f884bf58803a795729e965da116f249944eac6b198fb52fb5c526d99abe6c060f91adfc9126e9fc123d92ffc592c3641b6b0ee6f5d6f94e30c1f91368e77a8c1f0ce48e2239da88149ae0e96478f244cb650e18800049a0ec589985638e187e91e84d6959ba4c1c738f0f6f4c57c39ebf9fd4d270746319b13dd6a919514b2f8c3d6760d1ebee8d76f26347799f8280465593bcf9e0343a8ffc5e4072b7bd0568c94c0805fe6366363a987a5a965d9e38b8c1a9d4447d92eb350ba55d95c209dd6d70a5aa7982f0c55eb40f60f72ed2be24eb89aed2362d5aaea1e99d5f37ba064a48a5e7224c1ac63b6c09de8806a46861d58115e545a67a3ffe6c513664fad3b82038f4135c7881b53b63075a1a4dc2ccd2fc02cc68cd8c0167136dbf1ded538a58a558c25bdbf0efd2eff5bfc9ace16be7253837cb77ec2645915f63a5c659cbd89f843c7f1be3d23aae3e3ea2e9edd274b33da1406b9204440ff084032a4ddbabe19b17d1e045adba233ead0b4702faf2f1d91fbc140c7f4a5c160c7c963b76c455712f3b9356a3a707802ab62f2bcc2e0efc78d51f74e03aa1290f265c1c77ed11fe7411d9845243e0d79b348e5ee0f93c10e2efb9dd6de757b36f64d3005834b64b7cf44b708787a0e49b15435738d37c416bcc64e8abc7ba734ce50852d4a2c38699fc35b315e7cf2d5e4e8c1eb8d9c2e692d70b47b4bf8fd48fec8489963cac1244d99c70704c0f627791c950e716275eae88a1c9ecbf89fd8805e818ee800969a87429c233d3a880450d6ccf516e340a35d24a5d1edece68097f975ab68a7b66124f56746fd1023d44a4da691b94335acf21811ed8a3f1c8a6c2389f2a65131b116725b111c11d2794fe6f3cbb092186191e7f884c221f7aa53a86f71d979dfee33b32d4b1afb55c6289cb460978435d784eb48b2b86a3d1032b31739ee6a5c0813e071723e4ef3e3e47283a6cc7231da5eb20d47bf288cb7f149484e123deb9a14162e6c4b56e12ca82101f03e7dda75ca2360dcbd0d6186579588400995bed30f48df03b68d1258c8bfa3b187e1d253b648e853101af3ed245fb7d3410f9ebdaa1bfa6cdcf4bb440440df6c2205a6fe8fa55aa40ec0d7baf5b65c2af10f1ff92363e82d7497f1203de85a00547978af5d1f5b02fc9f997dd3cbf80c7be42e53df650e2980bf5d43cb94c2c4cf2670da4ff8d7846bd2d2f1d689cc54535574ba304fb11ce37061e849d5edc6ad8683ccec0a4d272a2c62feed29119eae6c4917fe036d709b51d0ccd87f8b2206e4dfe68632bf8fb64c86d25af49d9ee15157743a1f0d299b683bbdac7c1eaded0ecdc3e763cc664b948f5787e7955e54c1ed76238ade2bf2eddfb398cdd53b2fa5c1b1b8d43cbba90f34c560786a3b267da05e64f09c0c25335e6d5be57dbf6c75b3a64a148fced19aab0e453b81ec10ae6bbfdf3f526f2edb99c9b97aaa229fdc44c566c27767355f207b9c38ae3f8984cf161efba43fe46c6409b090ded3259347cae8b54c6fa60b9a4a7de259b8ae6ff92729517369ceb1bf4a7a91314d700b7969b7dcabe719ea0f34ea3f9ae1085bc4f33a9eb8595a19049abcdaf252b23a07c9faa855b252a22a656424b2b1c9f1d1ef4232100139bb98d6c08f876a61878574738bb3c3443582ee7bad7ca1ddf80b8309991609d7ba3b8c9bc0aee858fe6ee784a6f496a319775dc0ea9310fc583008051ed74bf2af78cfcba845e8e2d8706c71ba108debd67ebfbce3195cc5de8d753a839910b506f9ed7b337c4b4c63883420537de8a89f2e5dbc3fec10c7ee5f63e5a92d0cadeeee6bb49012ef15d8f697e77c7883da45ce1e61757a0c4e7ca805e6c71967894a52bfcfa1b722632b509dccb05d56014434ee88a14fa54163e11873579b471a349841d2bdbffe86a277a226a8d5baaa4d16b978764f2cc5ac4a11ec41fb247a9ac2cb8b8956e36b82bbf5dff46056df581c542bce2b81d12563955933ad48e70fc0cc59d3eb197a47b2bc49fe61540afa1c440eff5b08ecce82b1ddee643e8478ff1e087ed5d815e0702a2ec7796f64ce5b4f8a68c57479689f882e0989246439959002d0306f66114fbfc2d5835f1b2263babefd95389166a936ee29806ae8af10b14110dc1ab75ed9571d209ef7ac703f07aa0cb6c44d05b705ccd488631954d0a74d992ddc74fb8695e974ef506b36ec58cb4a2c489ab72694944d2e1a4e7d0b5e62b8f2f1e1090d59d5da791d9d4753f635f22d8f9e037a153b41b707d4c190d0d0879337cc212dc8ac9a3d531e7ef78288da012d25ebcbc8975cbd604ac9830619497f721a97e47c7aedd536e99debd2141fd6510b8fb951f5692701d9bced88b08ad4d783283669a9d918219079aef68e04d7c2b026f6223e1b945e778c84c7aa090c8a229921b0eca030a790b5b4cf0ba85e4fecc082b1f21b3831cd9f3d88693fd8f2179894b4b6e53dbb212c096739fce47c05b16caeb1eced3e0c5e4d00f6e2bdf640581b9335663e06cfd1ffb3834a40043505e983e6a3a2179aba9d4cd7561511b99150302be8eecf57dc2fdf8e43efd5b6211f186add8342a18cf1832971bcb180e0b62b49407b3cbe8b426ca899e0fbe859548fc5a5f615aed41595a63ddf636fb50c87668ea5fd49a824ae1cf7b8bc05b3c70e492f8341110870f32d34858af4618e2636e656ef13aaebc76ec7cbf3c5b7b24803ae834de4439655f97b25bef2820254fd97d70cc329849133e728194089d801d26f51621a0d2e6f81ad497b077ac378cc3600a0fabaadfc9622c98f6d52929b79babc0d6363756902acdad3364007dbfb76ec0842357280bd4b2a7092da7e91db1804259b25c1a72f975ee8c1201592b4684ac1fd6f58f2e42882aeb975da3083f46262d5ececea78157cb53ee93a8ac5f6b41293b81c25903d4c041c738d16d4b2c3282ed0f75775d515a8c5f3f2c0714fd73e9bf162c8d38803b37074e870eb4fb812bce855e9f290f1b5e4c4212c0837d40c353af205a06862e4cc2a647b4010083965f1b98fbb61cbf73f1f8e0e2dc4eed0bab9340ab60705a8efacde8c922f065ad71d5b141e9a3b4486cb62547bdc212c08bf707ea130486cd8ac77edec177c2311c4fd8e89590ea0c29630ede28f004281109e5d64fc6875e122e412f393722ae412a90ecba4713679351a756f089306a264a66e8e83959866c89662552cd41dbba918713bdb7a15ef27bd59a22395e0798adac3609f6f8787c6f74c713f306baa0b7d4050c9d49a612d560df9587098355cbb78f1783e5c10dd6ae2d47d9c14f0b58a76120e50d904a5a2f78a0e353e927bba7fa98d58b7bfdbc97da6cda8088a0fe8107f6a486d4faa6fb6358ebad0bfe7031e7843f2e0ff07819e28f6c6396def966e0a062ba75332d8898ddb2d26ac9d643501507a69b49269c8ae3e5b551536899f2d5859b60f196e653ece1d115187ff07c91c18dbbde51c39ab0e4753a13a778370da8189e673169b5a6a702f3ae5589b67f3fda6b9bdda8e888812300504c72a2c72e2a8fe14513dea89bb12dd5c0f9b5e1cb107b00abb0a92a96d3b9017e71b026fb78fdc0852fc59c1886c8b94711a0be83beebf2960f213e1db2eb6be935c63a02dfb8953eff407211e89dac6c9897eb3284c240fed00aefca83262850ef4978efe24b7fa77290db9674f6a0f3a3b2a1416098cc96388b860aa697a7e0347b637b483259414ced1d113d6ab3d5bd5e3006b2e1e53865dd5e8737328b3c3dbb590e4c4e78fd6ee4f7542482d8e232bd1de7b979efbfae0d403085a22d9aefa5a29561cee608176ba139d84b526c3ba5d344b740b7bfec61518536fe8c2e589e9e19f4c58a257697d39419ea7f875b3aecc2d7e7c461e7db984bf3bad4a5a07037d8ff69ea006ef4bb42002c14fe98095e6488918a3b664f1c3e5596575a9979d5e10e3473949dc011bbf97881f6f667d5ea8ba185fa823377c1a2ddb754f25fa8ace81eb6142953abbb3c65da0bc6490c90e423f0ef2217f1f3c45269697cbe543adbc582b85daa4bb09088785eb801b7827b5e84aaa64d949b218044dbd97b8644409ed68627e0dc245300320d1b047e574e9f9e7dd857f331068ed5d91834b63543d8c41411d27e10f82dd923451f5814183ba0d2c885ddf7449d8563d916a12414f120459ae2b0c12e0a4b4aa6c7ed1adda3bf03b0e3efefd33371994a410a3404f1f76a11cb8b09993e1feb97d8ae1151be18fca16d32d6965980b651beddae4497f11c97089ac0414a72f607bb394d86a81ebd3c58121a856fc1e2b0599911bd390ef69663f0c1dcce199ff77097726a86b68716af5b4770781c4155b61873004daaf8d54d7023251403edb2682ebb4832b02ed27fadd34302e5206aeef23d9a6c741cba2c4600c30e32d5dac6ab3f9e448ad00869ecc2c337123da6b465e9a0b07ace5c86111b0cb322480eecd594b0dfdb74c9696a63f514089c9c4e5aa2c903b2e8f6d843d0416d44a03627578af9600e6e35ae64aa907181d2ad6cd3c26b1c9ac92a9b4f6e1e52b71760c001701605437bd33bc898d4cb9e3f8426809e1d47cd1e1261eefe89b81efc50af888712bd967cb8b63170a72bf0fb8db9249e14b0ba28fda7d58ee43176d0e32496dc751a18a824f3ef6e765d4855d59e0d1da0b4897457d575cd6faa2aba34563155626a51f09113472d519278e5d5aa7dadd772106d196abb6ebf5bb7bb3a7d56dde9cc86e3ba2bf2b17086ef4d48477e1ebcf2c278ff0235f0c529fc1fc9a3d60181253f23425aea8cc73cb989e7ff5e14d434292d92354934f47bdff1874de77e49ebfd22946b4aa7341ac3c2c0692e712fb2f75a027cf08c4efb7fe8b66ca61c9e2c3e9aa97a3835c51822138124ae77e5ac9d89ae2c384a9253022d6069df5c7b35eb723470e0ebd0ca115ee809b6018cbe38bffcbe66a1aebb70f1d9082762966633d58e4148bd103f7fd7822ac92fc69a97129ecb6bfdde10c644899327053adc4f752e90a5fa7691d1aab79d2b0b699b57e52b0114cbfd6e81271ff014dd850b97c205c364fab6fc2bec292f3f673dab9f52d8290bafbd4cee19de7b1fa56dc989d9059c478f16aa113cde9ff195a9cb19dec275a504e0d0c4e9c65fbf53b726f4842c6a2f4506ebef59d794a62ab1ed431ce3f0d9e2c5d339ffc8bc53b676eb02ad4f81fccda41fa3f26b97888ec1b582b709d8e6b89856c4352a00dc6e3ada90ebd20dc96ff58d0fb0a399ccb0e643c3d55a0c18f02cde851ca41b2ba3c3a3671345cc8faf002a01c4b11813c486715238377f432f3cfc297075c365edfd24884dde91a11d714af5f98c6c19ef4bc18e58757c96410fb182799fd5ca5fdafbab53e78c7c55900a594db879774dfe602b2fc45740b1b4c938a1ff9bc0a8be826d4f6285491d8281c07377b63fdefbd10d7782c874df73995cd007fc4bef04241643892c9574f5394d60419eff54bd3571f741251adf069383dee7efbdf9a14b45fb1c64714ac432206535fc3f73c217902d1709c971ad3d5701009dcddc999efc5e8469e470e55120a3c373614fe40ecd30910be822c05393000304ea892cce76a03cc23fbff0a670f9dfdf17cb63357682fcb9856856d4c068eef251e69e32f2c584e40f3c214eaf7297d4124cf1c7c6d9495a6243e504dd3ab367945a361beb8fefea4a61860284069d2a05e713adac001f7a5074d3aab5fbf36c3efa5749416fb42f6426a33a9d6522b45987ab7681f0daaa66c60d08b2704e8b7f7b42c6d577981ba9755555e66765e257129159fdc019a289bbd34a39a689846aafb77157cc653600f3269310712f646b225e22170d4798341164722a73da691321e07a3927d02201c7228cb85093741b8576789d863c012c7173f20239f4651f21f7442687fed6c20551c020cede775c213746bc3794b5441abb82a3fd26ecf3f6f396b78346ecb816e406fc9c771dd7100625ef1349c82549012f09db142c56d420a42708e8c3311cc64b9deb0dcdfdf1c4cc84471012b7f9c3ff213c066e6c97ba5494878632c8b385b730f8f6e3430a501f6779d83ad805b888c7d3790124628618318e2cf3714d372ec81f9d9771ebf1e509cf18afb8a668a21dc2ea11db33a15d56c675597f3132e01db5de94fc497c7d45bf6f1fd4b22b7d8ef2cf556d06b7e9e4fa1f25d62894356c5037154fed7f6ee3d279e0b8275c17d27197695980f5af9bc7dc1bde0343e3cb80c614243cee23cf5481f2e577ebcd819185650278c47a73d7341aa5cd31a9ccf060c0caf48e8321e8deb5f28ba6c12f27c86600724668e9c2bdd10fdc72a2f9c256670f62bd513c4c92e93b3c02a2546c802683c1b9ff6eb7cbf0860df6d90ccd32ae4518db286891c611ad1ec2e9251821b0260e91ea0223f24f68c3606da55292e6c721100feecec4f6355eee2060fd48a0558e0e5c7d5a1e9d881a418f572c6afdb3e655858d29886e670784daea5b5f7daeb77723404039e791b5a262574779eaf501df3fca49faae27a9b37e7598f896484ebd66027ac4d3da90b2c8150cc09cd0549660f5a90e95430dbec3013620269044d13e9acf0ff1394ae0b92aa130634a0cc329f91444dc51b24fa0ca84ef323d1a889ead1c41f0324b1246ca0a31aa6c9012f3db980f3f08b22d4f7034dfd2083f4f80df8d20ccf1fe00dc85c738b98a59b85552d5fc220f982c1e442816fa54f893d86c395cfff6f73205a8e493f9287905c37dbf1949d0e96aabe0bf756b0687c2a38e552f40af04f101182584d524eb510e7c932789bb5e8fde3e28fa88803772a28585913cf64b342f8f9b2c7ecca1cde37ddcc581de28be341fb0d9df5d10ea5f6bd2ad53d2763432f03372bd513c17cb4113838eaf8002c9db4af0175866e50fc128372d2605b62a24d387e2fe4a49ec59fa5181290a69ec33193d6b57233a309ce11b071f1b740cb321f93f4465779047bd0637d102e3e89300589a21684da55874d16a44b510601d2f0f7b5a043bb0ee99af36512f7082a8e32b0742b7b7ef91aeb67b0941b54541e9a5aba535bbd58237c26778541b0ddee87b2195ed64a1d2054e25545582ca54dfea4133df10c419e49de5302f8961feb9e4c17c13cac5956b723227df071d129a6827e35d7f96f07d5195dd319982ae78201583c83bfbc6fce5872d9a86229b316e1ed26057bb7a61dceb5c387b44d8a1621be35a1ddb0e9d67cade3f40d4e443bd7ba466c961b0116e520610ea388490a24b9f634d54cf20a7d06413273d76d2bfaf299cf46300d60b943b1a58e25222efef784cfbf0f12bf5cd1035401b70be22c39e969e3db1eca4f84d113b4834c8f03ecf6aefbf9e9e12242f7093ff95b5b1c4f3effa01d329f2241d080e288b7aaa6be4f49fe367fa46e7e7b5d6d1cf5c5dfcc88d4052b19ecf612902dbc119e16703c42e7320a54e5fd45a089127d1bb53e168b33de57601a3024ae61d5296f10e49c1facb5f32d8b32205cfc69684948c5a02a46849928e8325351bdf405b69527433e8e884ff5d8927205a91db1c214186cafc64d115865e635afd99df95d3c0c5554533afb8cdffc85fd034aa4b44c1c79188c30ed7f5f0ea99fb494c00d2006c76f6e3cdedc03adbe29dc36d64ac2ca92f78e453478012863f9cb11a2dbb299d2647235db4b2768e6aae1178e778677e694bc0139f58474ca2440616dfb740b63dee8493879a3770477c3357f709897e46272060f6cd2dc018878efe66398089626dab5ba1e4c4ab0c044196269df27d10546a9f1db3c5fcfac35e3f7a45149b04ebd0a9b316fbed04b453e4ad635267923e94f3f41808f4af76062b5b54db0596a1f2a4c23e634b45ae042e6d9a650c7c2fa58ed1a7e393df1b4dbe9a9f299e295b6bec7775d29c08e64511ed3a2b350780b19a9430e618515a20396e4fba597a50e6e80a8c102280d5a4a64e07858867fa15fc477206935954ef3c0b8b9b48365df692edff34d3b03775dfb8318a665c19617de93b271c40f272cc26bc00e78fc3d0b75095f16b877dd274d5eb45ef0af45900b34020b0f0264560f6d0c7fc475dfbc70173f9c6587215039b2c743ab94807206bf5f3ffb71968fb4b57ae9c9baebfd6e65c9f9f2a4f46de8dd1daa880de78120a8b1ef6caeb1b26ff03f38d322beea1aa9bd99abf5d06f5519f508c3976d3df1b0572b0e4848dfbe1caa2f44abb2ae854bfd5291bb6905cb165e3e054b1ce3b4ed945fed9da12edd6d4599947864ee35ee13ff682f396354e5c9f2d5884fafdb2fbacd71e0c31c61c6547eb7684818f42da6ebe8132b5a1ccf4e4e8b4ae72223ff977ae212ef9ff6d7bb272de3b7da385ed896bffefe22b42e0dd55afd4c01d6991024bc3ca59c4677fae800507303134e15ce349dc8373da732dca3d1a01e5209b7a70ad8519d590c65a92c703dffc38d60e5c3b61e411447b2bd27303cc8b4e7d2261601678ba14897697206d1ead642af3b1a23a326fbaeadcc312496f274a2a15748de064e18125bfdaab385b72dc196f4a10faa0c9175a6198d6856e5da5762f6cad3c5ba2188d48799952730ae13b0183cb792087b599c6f93367e91eeb73bc1c8a8515dad2d117c67ebd582043792f7525f7a1cc294c8b26d5d1c77249d2a69e7e04a8eb2037059c59ac9b9c1042db83295965f6157b57111f87e40fc8b44840128c798025c9d7b8668a2d9e9ff77872cec276d5e002a8bf16e18a679fdf659ddc60393fe64a0f1b4a898a4f5e93768ffa29b2b18fec0a5fc86ed904da7ef534c134884b25e44b9a1a6b68a22b0c9f6475e75678bc9c49aba75f7844e21c3bc04109788bd3dbdf648dd4c772cb0828df69b593b97761ef89a7920590b41bd8ed77c153985826251842ce689b3dab60b5b061c4771baaf2860ddf19eed6a5437a4a92321b7335615c255a2b7170d6350dc639f656fee51dc74f9fa2976e4acf449d1a5faada9c543dfd4ae46a33a61e1ae15c343939aee83ca9b625b4441aa81cb7b698bdee95ac7bd82386ba0ff5648acb9f12519365dcd5a3c290f338a524476ec012fe9f5b01885ee4996d10be7a18c90ab31cb669987d7ab25eaa2a63eb137acd7877cb7c42a6511991b0c6b4e528fff980909784da7f660f2af92788131e8edbc2626fd46ac26363d0774411bf0d3ccf916679fee85661e5f06a2a7b4e235b4c6857ee8559d040d1f08423abe867160aa7296e0ab908d3ad9b8e0d5603ca61121b2d3ff748ef46a6f5a8b1250a3cc438a32c34c4ee823ca7d082b22a5ba04b0b49ef1e35933cbd0ec959ceacd38737dff1757641b5aa42833bf1c538b865d8c2b91de3c8da4916dc6b464b88022d0e22616d8a2bbd0ac634ec7696bff1eefb48f16b1166a93b294f2429a0efb10a1198b29190c5e44596851c099981beccff4f1198d6178034aaaecb8518c4c9da31f6d2d5a9104e8aadd96a1eaeac36b3892fb2aab35024311582f70b09aa427443b8c4df636890577a0cb02df03cd0bc37af2e1dc5a0fcb4f2637979defcc1f9d8f2b50dfe0d1d8f0c5cfb173996eb2d3ec9093f71154caa8a4bf8cb67e525ef8ade62ff721502a3e0aaff43789ef8c40d53e7b210fcb0db1732391189907784388ea3b2beea3c0acf5b10634aa48cd104b2bf78db204d22853b37c7657239684d64921e3263f68e2be78eb07df56e2a10c2a56ce869384b1c11a379c5be8055c400c0241399a41385ffc647210e4a8ca07d384e01cc1b3695f2a56e4d24353d8133443ede3a8cc012d38ab203505006c4fb0f52413038127b7a16e062311a637662a12271609b2eaed592eec5d27b486c2f87525e3b269c2992312176c15a9c953906503d125591b8a6513dbafebe9daac6722e7b25b5022a56731b581515abce5ee2c08157829315dd29fe3c22260b8fcac26094351d58722271d038dbe5151481149dc36a6b3b1a2badbff5a0a2ad41919fd1c28b5449fb836821a5757fe66e2f572c7054932808843ccb2f84e0cdae4d4047e8130b895d5e4a6bee49d55c139f439d0255328baff56e2bef46650631af7183efeadcabff1b7cc5a179476e305f36d1c1af536d3462697756a9c1bbc196a412c6abc3e4c82c8f3b926584232becf554037117c083b6b71316333a9dfb0ce49d4ebcb00fb1dccb4f95573b4218ac36d8aa3e413b75a738a1ecc61c10fbfd2cee9e9edd0a8a1c1e38a6e1ec6f46ceb516a6436eba33f3ba73704c0fcf5341de75eb65893fc98ac794ab14bc8d87ecf0d714de46ada49821a4c0ca4ec3bbcda7ae918a5799cf1ca594cd6c331fa04602eb9420fd2b9b192cf6fe85b8f231f027dc9a55339527dca3a8520b45aaa55c66fb6c61c44079cd9eb0d415d7237478ac8f9996d3b2e4ac905e94a6e9777e7aa159778ad3e234a8bda9a81656af51623c0fd17938d7bc9e45c3905a8158d6b3c1e09cf34d726f71255974631a404f2289efd01650d095875e9b906beb4040a5c2892496ef9cf818e59b6b3396a7a266cb445e31dc2599338275fed29f5dec8b10351dcbe38692f997572567d8425285e8e02eae3ad19fe060ef6aa72a80236bf3112529bcc6fc2d3cbd2ad2afe2ad3f54fc199e912e85ae0d0653e8aea0af0cd2e4f193030ef434ef38412f14c3a79ddfcbb3832d48b889daad1db7e8baa3823428249ae93ec4e996a3a03f052106d893a86343139c847d41a3fc11d24d42fec61df13adfdaabcb813ad382e1e048392338e6124d533a5792fa0e7a2929486f624ad97c3ca73864b3122665228fe818113753e4d9f5c29207f3be1045276b0e86902a1b95eb170a3de3bcf87b0c61b64070294220bf6b314e35750dc082192196b5f03a66488d7335943152cd7cf2f9be4b82f6b60d4b4e0bac084b653f48fd037f10c0e63b6158fc650ba8bbb7d07adbe80ea8928bbb5c88fb516b173f80ccd68093b19aa78c424d6bf83ae7c796601e137b6bc74564b56c955e91aed63b9c60b7f8f003ae4fd0b3d4dd2f8ce3e156b28db49e6265d5c4e18d270af994f6a60031d50f2cd94ac0a44bacf03259bd2700952cbdbf1e8cc25b5cd124535420886011a4d62557a034eba1aac8a2c6c3efc920a76808828ba3e7640d07b6b2cfb222c0e4f9872e8764f0f9a9cfe43dcac7c12878ebd062f800a49f8bf6661fc222a612ffbc156eb7129d30fd01800f6b5f71fb999bda0423dc125adb6182b95d2036bcd167d0e3f8d93f02646b5bb3094871c99182920cf54995662ac9e8c469a36504bb5b2ba344eeef2bc9b48bdf3573f1b363cfac21d7194391c633f8525c3e01e86dc55288a0ea43817a7c23704f47c90ec29e94c6ab128da51a58cf80ae4f658088fb0c0cd7fdc7ac5344d253a7b36d126950ad1f41a2cbcc6c20c225a2f85308659651122d2504e8485dfaa2032e976066379299873aaea44d54dd1e18f1151f2ec21aa62a9097acde31a140b77f89951581bf7853a37aeea0ebb1d159ac75e776d30430587f0bfb0b4bc3b0c85f1d4eefbbc281498a5eb3cc87ebe606c94505df35d93398cca5fe0b0cab254251ceda3556fdaa934c7fb96082b0bb4e98918dc2266c766e5acb00b3240aa9b5bd2ed6e36d7eb77f0ddd9186dc83013e1ed1a6abdf73675d3f183e523631d451a76f93065f466ed31778b4ea83aa90d331d0eae9a8e10973c732b78b138fa91f5e9ffdf64be56a0a4ef5acb4611d7de514129cc1ea90b692dff072b4ea47bd032b32121bd4e571b70ab02fee7b7c5d2c5f214d12e91dab140a508a8efa471a3831d7805367ce04191b8accaf79eba9003ff5c1165849f8c4d4a265ff4063e8c3e6e750ce7efc3e4b27276e7eeb45f7389ee599717ce95ec9bc0ffac2593b49686736f6b37878d00e4e050ac23ed75b195ec7008cc7e1003b2a21a7d7dc870e26b5a8384c4865c5c3fac6311dbfbdc420ddc693bf7204ab38a36418947b22445221d187e914fe6ef2dc80175168e9b07db73453998d159b39aa9cfa4bb798820e0128aaa73ed6279cb8e6c6fa5ecd3dc6d82549341b9b0d578db99088f93c63c7b7677bd0ad8474b956e69ce1115f38f872d02de3714ea4823edfbeca80ff2fc17450d63b313972541f21856d1258eb9dfa40aaf0cec711259200a46140238daca3a4294732ccc9c213726a35f4c9b8a612806eb287f7043bbd84866c5454e17bfc2e4ea378d27572840f881872d420c12cf6c94d332592c65a006967d84f2285a9085a4c957813e9d9c3a112713f65fd6cf32e9b6b9498489bc0c288255001e6846aeb0c84c1455914c7f938de56ee2e8d512f1f0daa1e6af5aba97b069e04f7fac62f296659c2ef2d40c8aec71fdf4711a3714d9cbef529690b5773dc46ae7ea77e8ce2d31f85f72351500d3f9473112860c30f8744a23ffb9e48fb9cbd112b88b707d9061e63f5b606a7a299796528fb526c4a3bdc4de2ccd574e45cf5229f17e7a8f6027d784b8ad1811fe328a5f0d7f506416bba817f277ce2faf8a3c58858a9f610640616e691fd880330eb9e960ec349c5ba5bd593340074a1e60b2398d38c1a123ef6c72d4bbb7b4f59d6c3c76256a14cd4cda87cee348732bae765d140a1617711054d2c97515625dab71ad13fe55de9ca59587041ceb4c31d52af1e718a7455c5bddbde04b8a4736b4f9ee862549310768af341c99459dc3eb17e44db07b59673d071ebebe21331860915db1c6422113232f40370ce439db41a744acee3a8a295f6bbf08b297da9929fea5e6e64ae5ab6ffb2555cb509ec413bc80e37ff928dccb8876c8c6fed1502dd89ad32d2e45ba914ebb537f97e02f21d02c79b3eeb2a98d429bb76353ee2b199ddb411508caded914543ac1dd329035cbffaf937ae00a3d61494ea0dc6b3cad442041e750d7ad6c79d8d4b35d53199ea865c65b488751a9dfc9b563eecdb3b5ab577b014c8d2b72826c5ca17bbfcee7ef64e4622d06df5c1933ffc53a79b416afccc43e0b145d7438fc4219d74e2618c209d4ef04b5d84daf2f0acfb6e6cc0d98af7ee36a43a8ee686c8bd454f218bfa3378c6882fe129e89b6d09dc0c62f20f36b395716b328d0abe46b8f236df8e24787b377259265b3f123b46fd0f30094f29893015de18f95e0b528414c4a62522a7f2cc4f8d5475601482b46baaa7c883bef568d6bd9d8e81676612bf057739880fe582fc3de75db244f1cc8f7927008e7d762d23967e9d963b98896322ffe31b4d0a998522022388b1998218b0a85925898382127c74beee7abce530090c4ec4c29d1e000285957857e8e70ebdbed4960c21804169a5c86b2d6efba60136214dc934c5d5728bae0aa599beda0b131445be535ff4e9f6bf2977454c248b9b6cf5051695b7c28e7f674c7923600c6e5dd1e0fb4495f75b295be7abb235bd0d85095304f6aee3f0586850be8c21eeed4f1513275d510c534422f73439dcaf50d5634f337ce751551bb38bac43580404048fc3bfec858bdfb32147de8ee82f7cc03890f83c93f36db04221c5c730b77a8b74e974ebf0bee9938240e3d4f871d99f703f5c0d72e2bfaf8ec882b953e9c42d96a222ee21d21d3f2b2b2aefe987c1c7ee1c8d5f59d5a67d20a80c4a34f848ee1bcbcaff2511bd1dc0aea4a639d893de42c4259c73c693f2ba0632ded3693fdbf661c44e65110746d04d69f60df82b31d8bbaaa9fcaa0eb666a361fae005fd9e418f3be6c548ac8539e9ac5015acb376b9ccf93f821cde37aafd66dd8271bcb1520239d9304b94756f8dee3bdb89fa0e3cbc3d99c29a6cdca4254df233cd8d2911abed49c63f883d8912cdf93b14052bbd8c9d5b2e733e7a0c8a6a9308d8ca5614031391c10ca1a90bd78ba49e454d7980ba103e59f51a22dad5feebc15d689ce65ffd6ee59ab25e14a1f5115f3f52539053338ddcc5439ce99ade7105430a55983743abd5ce598ef71f13ef30ccead19a89a2083896622d4897929f8d41f27ded50179b435ab3f9cd1c2f234cc4abd3b35cdf0e7ca485e2dcd1319b96905f59af4aee9c5114cfb38d6456beff99debc6aa4629017e63830510f7cf94aaf771704f0a2c324b2aac8fc1c793abffb75a764939de93fe78791c6ef4f579df0fe1151ce5ff4d0c2911d7557e88220f8212021d8c97c2321268abab6d9efb666d357daa12c90670d92e7bd37dd270116eefe74ed2f7ca26bc916f0746c6658d4d3989cc85696221867120dca7ea92a338fc6ce043038fdadaa8d4bc2f9fa7d6f6b6e68a160beb71becf97aaa4018d72e5f02bc8858caaddf3b1d70bb9549262411107f6a4569e07f865e0ba2d5b00ac2edc139d82a127aee20e3f37af484871148bbd4f5dfe962c0ef975764b2b26d4049314cdec01e34f47a81c951535847dbde14613e6cf1228e077cbe0b4bbad6f76c242251cd5717ddc9c6e7b897357104a5ca67b0ca2895f8df51e621eaadc13bfa94149050df6586e87b73749cf07565d573b79a053f03b385b4d26ba0c063ccea5de99273c767206343047b01cbed0d29a84ea00bcd751a28d5e3c70ceecf43e614066007a6e05ceae0fa0968469069ba982e5f2993e28897b0b37d4a6d9864a90c495c6388a5a734c580a976ba96adab6131648b78b033e18f692311e01116c404ed7c89ba099b4c1fa38305621a2a7ceec80693311450271c00c50e5a38aa8ed4f77a192681e07f98c36242ef82fc811275fad893132673c89baac2066fc60b7b9d86f8b793d02d6ccdf8e59da67dc9baf30f6b6de39b586635503910e37d4f21d5111d0a19216ec03cc428d724b9486dec6defa8f0e21a1339d8bec82fe1abc33310cc0777534a6a02b65fac1c5e61a2adda9548c2f4fc1913a1bb3f401149cc7059ee993de3e9c1e2a4ee50dc1a352f4171cc6003b09bfbaae4fa0046d103ff47113b0e626abddd95c66b4b9700e071cfb4f9f13324ce7abda6c34082d5a5f98abf8fd3b3a5e63a3091b8bf53a19f0ec6382579f3d0f19b8c94dfc59eaf482c2a8ede24c6d9a310dec634e341eba143971b1f8904b30573caf2a7d538a17c9f47743c7b8ed6001e298785bb8029f3408cf248d321c7618feb7cd0b2f42082c47aed75a10b0f305e8261d37f91c2f6e478593ae953988ad11c202994332ff50bc8d9c6a6f857810b28018de422b7aa360973d0ac62e84e427ff87e4b4a4d1e0f717b65abe04a46431e75408f6c0c7fb9d04f345384c01e3ca3987b83d0040210862cecbac10c46883f2e366441dc676f89ad56b694b03364b308599d7030d56e302683b1b4e48aaeacbe6e4fe572713c0baf1e1110d8c6f9fd5c787eefd6e47d6c1fa01fdda1763651e467f3b14265f25c6ecab38110212a11ec65ad4cf0a1fe1ef9da5f6e329ea9cff850c9f3e019c2f4a90dc4218229d9b5458cbc21f6c7ee832efc83363e9315b7eb68fd7698b22529200835a467d25e227ae8761083a3a57b401d0e5af6063e3490e1e8a323d6ddf5a2eb2f600e634f6e0d9c70007750e535881fdf9193f08f84060458139dd780e10f54c5a52887a6efc600fd6f17948f52e2217058d5392a9fee270ed6e93f48c81b475351e3b8998854d35468b015469fe97290522dba8de0da0e469b9af448011b7de223e4dad9d1503a20cc429f8ee8dd79f38d8dfc31440c9185a229b4595858769d5544474979aa9f6175f21d259dbceab50612620f27ae41b0d8fa84aabc0c1b79f69bdf56dfa8da2e9d230eeb2ffd31b0e0db299cccdaef3a8e9aa65b639a4fdad4c9c72f3a0e592cc03da394e37e4db4074876b312dc9d0b5e086fa0a5d9b8bae0b080baf5def9d40efc43cf88f5419c8c391c72d268e429b2b0dbb208ce20dcd1f9cf533ee6a2d088fd0901a02e20fd7447c5672824471a1f99d446a6002b7f15647176a8e73cd45649b29c2df6f7a60b0ed18e82c63a00b644491ae412ddcec1ba0fde3307054f40677d550489a04102421f37ff0ff39c6fb76bcb7e48bc4eb327c4fe5ff0c4a7b97eda4b1325d50a8c02f9a192bd82fefe1b0b8b8942b9636288076e7af858484a1251b68b83317406517d7347fc10572dc6d157022c01cad2a3160c2d75e7af557404ff9d0d4e61e21a87a2247fe331b5748190a448024676143e2dc4d36697dd5f7f8582b315a2961c213938c9cc1c08a79147389fb87a44db5a07dfc7c5cb0774cf08846d5ccdd333980b362377c9e1957cd3085e07e353892c088bad01367a29e4a27d2bb47307ba96982aa66f059891600ebf38605893973c5b921b569da025c7c3905085ec5ac8c8add3af5a7e31c14e347ef2b46eb478809310e41586c27af0bd7f337d55e2d522df5f68c1ce37442af896df248ae3783535065e9ef68aa7d79458eee9c4fc77a5ab333dee737a83544e3ee4cef130696740cee58e93dc4768fa254e952d3fb7e7639d2599289134fec4625413ca4253a89e39523d7261fe890c2eab7754ae2ed874fc898e37a3dbc338ddf99269d3d2cb168898bb1cfe35b8c3fb12fcea37e29b47bbd6b7986d75a2287c51a2b6e0c151ae1d2d13437d220c2305f895379ac92bb61be5e1bdee768bd6ffc98301c267b6c1dfedacd7b1cfaf3972d4228155a74d4bcaaa6cd325c771c7d3a4afbff5a8e7da5f0bb21a4692cff82603a216b7ed79772fcf142b4946bf985d1fef076604a70a896e9612eeea742b1e8a1cf376746e91debad40e450876331733d81340a0ceb042eab428edc7b23fb30c9698b0c0d3bb61aaf040bcec72d9f5099466856114da6e041404a510b6e2fc17558d56093c97d376dd33ec311d864ed3c15515622b0818d1c52e61c841358d0f774f219cd2349a51e835cb175dab112be04820cb9f6ae074d0718560cd3f739d00ede2338fb9561b3c635665301d7cb88d4b340f3cdd2b609799ead1a38ebe4c28b96100b38d6d104f3395ba54277b2c2759c3cb3c5afb15a775dab74d3dba8f3003e487ab8e9fbd6cb292b3ac39bdf3ff7752b1d95e1b138ad01866639b7c806121dc44232150e8aacacd73c9d5fe83db92627eadecadb5dba7a1c2b06c3a9a486d084d7c278066c9e340953f7a4b954ff5620ddebdc3e14797cd98378217fb5a71bcebded31f85f6aeff32d99edcb3aba73678190ade3c920e87063e4f8d240ced51fbbc947462ed1336404345f58bdb68550bc9d129a774174a79d602ca6dbfcf9d40b490037d8626028e6f633f887951f37aa676ac6122d127cbcec7d14c20e6d8ddf73b9caeae3570eb88fec7af54fe74d6fbdbf3653e498de397c09e64fc53439fb431279644fc1ca45efac70d7afb2576d7412a4d51cb0014c75bb6bcf06978954d869ffc1ce6e4cee5284fb7c055b0391f51d8aa807518de35b2e212dc7b6baf4d63976efa2151aea9a647b55ebd03d97790c7588c7a9b2f95b424b17bd327dd21f086efa1250ad0780928daaf8e062a4fcf991acd15407e70eaa78fa369a5b9f4063712ace939d02646d5af8054a274b9e86d11139aa76de9512a462431c2ba0dc6bd64fa9cfe02b18b22638e7af5c6b9ca42d44f9063503d704a20228d75dc206149808b020655b4ae55247ef234414e8be7e22c6f47bb92a16a6fc53c95867ed75f3cad519633f956a37b46f785b192c013da7cc38f9e359da572d04f12bf3607109c6d19e2e9c5be1f6981ea8145c39660468bfde049bc8dbd344461b485bc7ba65d6999099e7b21a8b26da415f002e8b1a28f439d30bba42fdea111bba812899d678d6e2842b79cb7e822fdb688d4b42c0d920992fbe750315fbcdb7825d50781fbffe7282f89ede4f2321984a7d788d3dfa66837df8295e675fc4f9936be3f4f2c0a91e4115d97de14b70676163937e2c232d3a32f39aea9a367cf4e0e9d694641a7e123b700a068ebdc8ec46920e650c9f12bc3d309605794f08ce84e97343a990a4606211e4bae83ea44320a85ec8a569f33cc0440e179a70d2477e94ed82ff281935aee183066607b9eb6afc761b3ace01b6269a60c8d011322eeb972f34a0be890c0ac40ec7d3531788585febd0a7fb2817f598a1b33ac09b19e93cfce5cbc698af0191f935e3e1599a2005194893fa07f2802a9f394aa0ace30ace2b6a8286d07e4809e35c4695880651139e54804b09296db367a80f678132c973916ba8f79f8f57216c5ea3cebb9e05c035df282ea868eac6b1c77325132225c0d4a67fd9f33becdae01be1839edc4742d1c7b3137a0926ed9a8c4095d40e3d244e42093157491e4ccbe5dee8d3ae5337a206c5c551a8960383612a2fe8e529201fcc36b8ec8218dc9a73f5d5e05811ea7429a0779a46632ff6af7919121c885895cef0ee106dabb629250c5e4e31454c3fd5a874207edf3ec861370d6a8bac6612a68a7f159de52c1b06e9ea86755a425ff75fb61de66ce15c2c3c9de42b9830eafabb63ee4bbc951f12af72e8df045b9b442214441b47a977e9105ff061fcf4f916fc4bb3a5bd4467afc56def8a2e2d6f386bcd25200b28a7c41dd8a66d6ea7c95de29046c2c1d539da6d6d74b350fa226d8e42cc0f612c5517c56e4c1e90036bbfec02f082eee079b06729e2e969fe012930c4102868022c4d153d18bdbaa6b9b11300bc4cd664391af56052c402715b91669358ec2dfa3ea15ddb49ad9ff6fca6d42199e13fad1581ab3a6eaf0f8f733bc623f8543742c19a67af074b79ec634a1aa94ad3494b0013402e3c0767a11dc55fe799876fd610550dc731088440dcda6bf6dda081fce458c654be4c0a7ce51cfcffa717f1cfdff6d8471903b75faba06f732600adb5ed02f8f3f052117195ff01501f7caf3ea220028abd1088e35be120e98d641cd49f44cb3b9ce20676019485e5c7bbe640c8ec715eb8820cd539a1597d40173fed9ad97150563c550aecd13d221eed8e33e9b4628a94d06c5e8447e2e3a40d936738d749aacd8f071bdf34c09accef9a6fc3232bcb29ef74b6669d62e79c70f2e2a54b3a7c77ec372952844e66e0ed9b1b685ced860c154c576ae1a34acaa0b5a1768cd93ea6af69baa2f87c917bbed4580bd02c7eecb16ef7a390e7bf01cf3f37d69b8c96b2421349f8237f586ddb14a968f25e4ca9f4d778428e529ccf62c32ad5b7e5f6b1ca18408a1598e7123afdd325a13ba999c332aab95c2e295105afe3a3e13a31e57738512062071e4da725752885887b302774b4b173f099634a654321309a7747e82ee45ff99cb7966890319361b37f5369bfa338efbabc058e027505e8545548e4176dbe3d585d1ea6bd7f29d7c4b482b3d3db6abc873f38d5270814910feca6265239c954d6554773e0899eb13114c7e9c338fe1f68f777f541a3006b68f4901d44377e03127e845e0bcd88ddab710754be00463a5071a9d8f1b7d91deb2fd364720c64adad130aa18613006491b3fa9abebce334a4c51043215f3c65a7b92c8a03a55577e1e9f925e5aca64b39a78d97c8bc909dab5503a059f11e7fe9e982c81e01ba15bbafd80b17dec18ca412c4442c4cfbdc7a0fd4c50b19fe62523872595b84e78ce1452df6343d5966a3ee8f29459c7143b05cafd82004114dac27be6a102cd6eccd7c5ef39250b27889688fe86ecf99a7e2d9fec8b4356b81fc2c8ee4196f7190d58734c41b650f8c24796388d97e9b92c1704c333ab3a650d9fa9e0002c7e24241e8df5b95efa951798bb6bf515b72c6843128a5c000d18e0ccdb2d52b35328f70abc12fef02f8722173c9526a3f669bac98ebe83b23e560540f184327cbdff1e65feb740b0920f9177f8e8eb6bbfc51e493a0bab0a7849a3fb33b6035667e1b594142bba99452333bf36755283f57cfd71eabcb6aa48ffac092a2db731e922e55cdf4e5415eb04f0c13da4f7cc3417f91ff38155f4a8fc5806cc1df4832ca891ced7816582ca7a29809ccb81f17fc249e1a55a28cb2e6ee9603f0475110191ae24f0ae04e7e1da28bc2688434d2e8062cae3a987629cf0dc823e0d162c2304bb4a3b5c46eb60c1c6837655fa9fcda2f14c98551ad1e8d33060d912d420d2b3df520744a45d84dd1546e5d518a649bd4093f51f8eb91f7293a2ccb264f89746ad98f1c50fd28c2f78c9af804fe7f257b79c7f166e543de840ea395f35ca14fa578daf2c47bdb7cd5285d62c555b7462d1100ec97d37ef897d63cf10924ed0bf7a46d4900b620e93ff5bc145eb09eb3c12b41ac5695b154da8d7c7cbb70e70f590f76834049ca32345386347c73946d42fca6beb4d84611cc373ad97c2aab50036e8bb4fc86aff0ec88eaca3f331b7d263dd5980b7612caf654846dfd672463b3ae8862b227e00db42efd9d6ed816c037f2acc5d792eca252ce28a38e37500f06db3db9acae83b37021e11bb17db26454b00ab2c87d91e3e8ea6ffd9573bda1b429fc2fc625460291f48b3c1e679310b70bb320e76dd18fa7039f0d3367a4286d40e782f20e2ce264cc9fce4f31015b68037edc3a18a51d6ef3c1386d23f584bd57322619be0522ff394fd2bfa756b5f607decb100ea629f5b19a0d8c38bdb05ad24706482d53755204deb0fce49c54f56ef268e60caac92a5b3e115935270aed3af43bec6e4d1a1a3f1a4111a4f594460930cb63482eb6b3937f002671793e213f94d56d3b597e0dd5ff73bb20384f39a92a42fc24bb1bf211c03552105f66f8a265f941d30bd02e207cbc1b122dbc74c3ef0dff061a5987da9e63ec5278336c1e37928e4045785c4a4ddc5613c2050d64b747f7cb7ee18f1b90667e540468151c03117eb636f45f2608b77c81917aa8c942a6026e2b444c953e44001e58079b61c3d10bf2ad33f9407672c993912ed1bc11c254a936d4dd486acc652a0649cd49dc6e24792cdcaa37af1b6e1493ca4a75375bc86053ef3f73e2bb16167f48f2db3dda375cfa88c956201846e7653ba44b34d0b0615b0ebb8decd0b94686ca797523e0382bb09f85ef7c3e42c3a6db8ce7d5bcd5a11860c798fb161f5d111d4cb1e41bc5b52820fae4edd56f813320fa314981ea2a0e3bdb2e40b8390ef8c4e083480ec6a01742ccc12908260c4572a807b599a5e24cecc73d064ea10155547fe56dc936cd7bef689fa0631a843b4ee241d55304c64cf9f839f37f212eff33df7fbbe4cd52dc93214a539a08d2a3aed5120cd3a736ecd32171973c6145ed0ad078ca84808c72cb2f43646d8b785e744a8244c0a42c605e94deec678c24a05b68bb1ce31f72c37910d95956f1b6a4378472e3dc183c49547f10988b4e0472a37b17f14260f112e47eda6e714dc046b474576251027c04300136aa4c4d24e729d299471d8e909710a8d0d123c1c29fff4a34da16655a37e017955f6e582822f94291c0b839b15f0304c4dbc16d0cf6af805b698ff67eea5590a3612e501fccb0deb4a0773dfe9061a7782d0884ce04b4e0afa96e5cdf3c194374dbb4d209a55b667c5405f40375302c8a48fe2747fe9b398c8e69e5380d8cf087e641cc6f1c70c163029a69d2ebb4ae7593a3680497bc7c08aff44e84e3576ac948168a5e1769e92deb78911d3d96e4e83eb920dcd726caedcdc719c0dc93c73744f86d0c26c628768eee723bc04113019404790a467adebaf1d72d7ee36228ce5e6121162f23d2a18418a06299536dcbee4f52163314712f06a652d09e51be5de9eda425b6eff2fdf3a996dd0d9f40cc43235dff2eeb7b20fd632348cecaba01bfff73034a4b28b157cba7f0fa4aa41fdc637c5952e8f780cbc8e2864832b93aed51932e92b1c24828ae85684b7ee1a86a49ef3f3d91346183c22f2286bb2ee2e5b05335c43dfd271a7d3e0970031ece374029c8d052effde0265f2abe081a8d8d01bf5e02c9ea30cc778b65df83e6d6f06a56d0162f6a9bb6ec8dc729df182b0db5d900180aae19f7998c904a648a08a80c9b9fd76c09b3b2efa7e83a457057d5eebc4cfa3052063fe0fd223fa7049faa37068850d7a19041fb3c4dbf77f6fbf3d64803b2b935afb3af825d2b5d854d093748591414c3914a2bee263b26247c3d87772d9b137f61ca362906a240da2d4e3452e3961d62622733a2caddfc4f45bf1e51200fae162b89694911df835613ff191f7535d8c69e4606a38b2418dfcd303a4164234d298257f6f95feb3c19afdc6605f8c58258ab080aae49e748009f4babc3730c9abcf87624c5df56c620a5a7ad56f306ef68d4a7301c01ee908ebbbe9739287124eb89c89575dae4d2e07725342049639dfaf39c063e048fbb0df2ea0770421f60d9e5d2c0c9ff189fe9dd7c25e732b01ccc3d6175938d4141583d80e3d0729fc1f750881a09a18e811d0ac0ddccf05532de7f61c207a8986b3a2f02ee60088614b903e392b96b965e33d850492127f464ee6ed783a778f90ef5fd170246063d32b9f615ae0876700c1cabb07557797d6c20f915edba84efdb249aca173d120e21e39cbc18bdadc3d275be837af17af34a406325cd321ae6e3b39244f1593f7337c261b60539f8fcd76b35adb823a0882addab234cd9aff59b4881d8ee0940a6399fe691e70fc29b973931eea12196874bf39fdd7c0f4abf26be30d2c33b079c2850b867f432518b616018d9a41d9e95a77e48ad184275878fa7565c2e9e50864bbdf1f80366d24322e328e6f35861cd610c47f980c7c25be8b7d32905ab512a2207872a6980b65ed70e7b7fa78b9cf5ddd435efcf472981cc3e7a59f764578446191a1ff857397c8a53fa05b7ca06a6036d25ccf35eda2bee268dde595017904fd1c1d902f9271716d0743828ff1b3c81e24dceff21a02364c0e77a94a96186524192483bc587ac1cb3d745bdefc71e0ab920162bf7e5c0b375de2ba1dd388e70b64ebd8dcd3fa37f552e8b3e9cbc495109249d9a16052abddafb6fb15d570165fe40078ba02d7cdd6ad450519c8d83b93137617aec5006dc47ce075b5c684f62ad6da1ed311fec608e5139a1b70601cc664a17d8584675882a4c4bbf3a1a8055ca2520a67acaa1b5af39aa893a185789386fb11aefde14a067f1fff2a2057712895522dafcdc8e2ad9bc6053269b236882ae96c82a5204be4fd130a14d425f665f7c761c732399fd0fa361fad3ec94884b66926b7038e3ecadc36d186f589ed1a352d67fc0e45c49209ac667832bdbed312afce3bd31439bdf353cd8f85447cc50645eeeaef035ad9faab624e7a1e1dccbd36df6a6377ddf0a937951537297fd500abdec561be67dcbb120163524d5ce57454ce821ad477083a4b70cb9216c46e7dda5cc80622d05e1b39ae8a5d810079bf62a84921cbb089b8559d51d679e9f1e8a61cc82fdb006eaf1bd151e068987dcd4a759327443218d303a19720ec95b76a79cdb2566231e73b89dc948a1783af54f90da8ee63e685c12ac2cb50f2a43719858b9244e82fce18e52a2588424e1bc8c69d11d65868017b9bb1c042eebbae587550b242f53f1afef5df10c140302397625e4ae9be416175791828f05821b6535077340df4e2774e7876778a16bf99137195daeac383dd36ffc06895f0b10602d38519d9a247e93f394ee4d4f1856c2418cf8117fcf6dcca5e682d6304ee9512522c9d9a6346b82aef5b3a7c4eb8ce7cd5aa880ff578cef7058807213b7a8666378ee4ddf6f0f26fd93b07fa98dbc0573140f416e2a0483771442df3bb130c5be1383b514dd17f7d3016fdce50f8e7765ca8b71de46b2b42751f6ed30a4c8cd88bc4fdfe4861de1242135429f43f0bde25c95d5199c786a2089f62a18477b46965321c223551e79634fc03a52df6872f478750643edcb414f8a0fab14f2b0faa565c7f892bed4ed911cba8f2343ba7fc66b35cebbe06c218ca848db1dedfaaaf92aaaf881414254e2331bba418a828aaa1ed21e07eab5f7943534c24b3b86a2a34dd39f6bb2ed2939651502150eddec2bb9d3e0f81102b7983fa0fc2e49f3f9f5e92ea32cee1025da31e7f707096fbb6422d400f523ff1eeecd1b585fdc471f4b644b04f04bae54c96a23dcf12ac410e3ce8e994dffad674507a6ba694376cfbecad9251a7fb363d22e0bc0a53e8b068e71bbdb74510845ddac40f7547f44155404e96fe09229ade42508865762d15b03ca61f7db04281d4c83f94014a07e4363fde7dda4956b92f6ccc4d155e51d94b6d86afe41a35a5434ef826138774edbcc567fd28ea270ce04beef5e1e93b15c4739edab04fef773eae167901f0c7521b756ea3870cd3200090eeed698da36f487ce69f988d6a92cbde304ba51692990849be54408e88866281e86579df56e35e7a7754a0c44f2a9855939b0231eaf1b1f41f503edbffcec079fa028121cd2294f35ad91d9e68daceb1097a804c0c95bae0b78ba6c73812b928b8c4dc4b05a6bfb8d074693e4d6d681beb1d60c0cfe99a725b2ceb08fc4d43b851de3a936f5128bbe6e4de78c8cdfefe0342d645da087dd31ed9f3746e96dcf516730fd5693baca88ababeedcd62e4185b31e341ebcc34b8c007e90843634e207cf5671c1ee06a3630c6edae52046ec344bc92398126a7df140ad51546bbcac666015a1d47029e5be0acd54eacc183713b1c12ff16eba4589f9700c76624b066a91a101c553d0cecd28ba616351317d99fbe761568524cff1ed81848a46eb196123dca9009407dec3162b41c9cc5de014edf261799eb370bdbb48abf87a87b879ee291dfef02afe9dd68c81ef53f9366d79596d199d07fb6625fe0b3c80020ca691b5f081b015fdcaa34a3404370b5e13dc9e3b34a32de6362a279ec9ae732bf9c6e2a1fe2873a71aaef8e7d0180a3b174b5b08ed2fa3feec021947731ace734c5967b0a53c6eef557ea27e0864310767b71755c59cd5543f0e8a844d24cd9f6989d4aa6deada32ddbd4ee29f794812a061db0d7fa5deb28da4ef79df57f9f72480a2caf2d3b54d0f3d0bb12aabedf8acb7341386ac0e083b2f6ed4f248b6b73ba38aeb18e30d1ffc85f3f7217867917acf260b21f2119c10326c9feb44ce323f87181be0fe9d7410552bfad85ebbf43b239e2e9e5457a243a6ecb23cd3f959cfb23761bc04006be8ffc34a14ad65d0cc4691239cef40010c1f9bf0ce31d42d39b32b8b417f41ea35c225fcf3599097f4b9bb2d2783d6788e5d4c458f518f57af238bb0ee5da6547a59bb5fe1d6f0dae4057965141241f00c14902b00b9f4561e57278b485d7a7b758b861bef75242c4a0a5a579a99362d9b1f2eb4a6cef12d6ec13d8525cfdbddb553ecbb761137b75fdd3634f935f71b2570c661a0fa8873c3fe2fbd9278dc5c6470430977de3bd723251542ce9b036efeb9f35845ef58bf02f57ad441991ecbf3093b8a7536041bdd4c625d8d163d6abbe9dfa7da1ed786bf9c6769aacd19c7ea49759b19c86be4dc86d6000fdd940280d26d360d3c9b8de5f14c10899e80c93590a005f554ea56262d754917c287736e98c5af70c425b75d53e6a3d72b6d4555d768b1e0842023efb4971bed98a09319633141d68016c8031a79da5c115357cc620b67e75197f3e6cdf4015192e747410211ab0b93a4fc8c754686e793d18e15fd9d6fc3f94cc4dcdceb32a61dfb6335eccdf4cb1985f76d9cb0af81702acca121a1d3985403a9ef43242efce03ff0e3f5b5e41e2c938999cf9a170fc2f170755ca45a031e6d5ec9b0d156cd80716e608ded65a4a72a2f42f47432255db6a515c289a01faf6b814e81863bd421c4c37c3a21296ec2e7898086216c018276d4f00bd66dc74cb6e655fc0b1ff60ca7ded6abbf5a549f5e14235052d5382185da1b4b9e7569d55e230bfc140190060ffea7e1de6bbc762af828c1cb687189abee5d4ebdcee9f656cf74697e4e3cb731dae2b1bff51254ad0d1e9e9f01b37eb768515ab3663aef09d77f76fd6da4302732c791ae610594d3350e0a12a983c3ae9d3448e86feea194b444bd4136bcc2d12907ba3d501cdcd1c1e0649dc0143c8a7adf1df462e81c3dd759d7ab0746d939650d2d6f46e3d4040fe0b1df24ba7cbca089fc41b317b3704c805e7208843de7c09e8980ac2c5cc99133a13e11f7c1bbc2b1f3d33c527a4600e4ae4f3a99ba0a1388265aed233a907b7a11246c887a4a7397609df2960e2ce110c6440b0e45d20e676f67ed292d80f6447337beb0b9179ff45e09518eab9ee9c06b474bf093cd5252132406f9c097c72c85275d40c179ee22cbeeb2b92ad5661468485ba91f9e912d9764e9f574b49a341d41ea6f396faab4198b4e54c2fe0c50077355c8f5bb505698f15c4b8efa4b30c9548bdcbd9cba03d8ab1edc8d434a4c780b342ac4e6eef0dc73142f0200250d9637779162b1262ad0ba50ee2383c91655772c090013237ada6e07071bc13bacfb6d84c97f01b8b225798aa5983aa02f9eec5b6a8e2cbd8c906441abee91bd6a434ef3e53af8bc95669cbde8a6ec36ad772d34c9e7efd78230db6d0fb765185a1efd6fda1ba09846a16241e97cd7332900ab5e4526607dd553bc57b1e49fd01f156bc4bd3a899073b2f72e4435078e280f751b2ecf36f220c3ce7751919b110ad4ad73df7134ac70feda7370ec046714ee0c14243dba040ef26d85e077b79254abdd886a0b8002c79ececa3684aa6afcd34b726c60f6ddb73c0673a3ce049e1bfd337ce5dc894cd782adf6c1bbf489307a2d6343443412c78d6f58d4748fe082ea7a4a01c81d1aa937279bc4fa7239dfc67c647c19017ac54e896057c8597f725a5bd42f3eab8de9a2f37754e4e12bd0a75a0385e477c8ce95ea5d71dc8d6ab30536a5ba5bf1e8809128e78fffdf54dddb93508dbf643510c21671cf1bd90e528840e2a740720e33a9f132e1b5e2bd05b05401cbb4b8cb9c4d2c845df02dc042b90ce66093a2c78d6a4568287520bed8939825c5616e70f3a0c22c836091cef63c6f85116ac037cf9a28ecc908125aaf036616094ded8376ba27f4a43834210013c451e098b7d82cc5000cc41fb49e163f196c33a9400702c3cb24785b8284b9ba1cd01fbbfd83ce90a186f81163b0cd934445267d958e642e52c71236953481c0b3f2af8fb556e2201f14e94d08ec73e6842bb534c07f15867a44a5480c34b17587437a0464771e6b1a3e84ba7034318d8a73258e6ea70a353a01c787d999021ce9b70c2398ee8b8e0f904e7af6c45415d32b48180b451ebf0b39a603917ede17ed41bc124249d5d459c734209de2bcc376313ab574a7919df2ea4855d9aae1da532147cc3dcb85c9e8f516f0988a0950c133f419357f18970cc30a5774178f1c6fdfcfa68d2012f01a14f17e239ac53377725e75491fcffb35f7044ab1aeab337f90aa4dd081f0fe453e1498f3e61246126b7cc2a0ac19e1b93d17b128f67e145d824d3aaf268dddb515704b63de003daf46104fc6a6b728ad8fbcf5e7eb6bc3308017e83521e70c35620292336d2e5f2c0d17436f2fa4dac6657e3b3321992f19db817f19137d1c08d5f0c73562f901e061ae5e54f0e68ee6c54e5c91bb55c364c24cd50fee60c43e87c155a84972dc8545afc07d8fa200dc18f4d7e4c09f941bf2795fc8509c98061e87fcca6c492c305ba917b13d97e5a5f0fb544e56f5168f4904e3c21763f7dd7e2ed044d7ec036111ef1f1376ab74f2163828d998f49772c97bb51c868c56c71241e553e4f9eb5171c5a402a627d4338d687e6aafff21dff5c330a58a32d204fcc43990b094213a721ebc1f1fbb0d652bcc910cab28847237eb3fce91c1061fbff571f6cdfee43c0f0785305d443ab1b67202a0e6ff6ed95504a07a9e352d8b484e52d88492bcc939e76782e2c2588f7a5f9b5dd2ae2ad5e81d9024c4b1153b4adbe00741f9e6af3563fcae6680cb124f2c5a90fa01d0b9ef6059ddd44c449ed138f55b9b0164c3470e8afe6ca2ae20cb7b1a4e6dc567223190db2fbc3ebbdd55142dc9b9d9819f62c1be596318036b6e0583560a3712127b7db575264b5fe767d86b81b15b78c81a8c30c6233eacea27198e3f6c5990ab445cc83425157d594e02a59657b5a95a0502cd64cf6ce45e9f2ade8996f942641526ce03f7d49fdff154dd26571674876cf971243abe3efa2a680d3fa4a3d598010a7eca5140005b93cd38cf8429e9342cb62303695473d3dde819b1afbbe9fcbb979d2b28eb35f56cc05e657a92174214e6d660efbdea61e4efeed8c2e5662a88e2fab56a292dfd2fa6653c35cfca8e29053de63c443bc8b2f961172eeee7c96ebd60bff2890bd6fe6a9ff54174ccfa0642ce7ce8553e153ca7b0ec40d8873b8cd52126ad30b30589ef9c416fc41de55b8cd567044f4efdd15efbee7d28381c990125eec73afebb00c0336778f40ca14d5008d41f916a9cecc84765f943c8bfd8e40e080514c61e855ae71d4aaaaf173ad3d4bd9421bc5defa4b70dd8945b29d2805e135a4cdbc4fccd978d2f24b0ca2bb7a11aac5000c57fc901d284de1749ac20b0f96f03356e33368a9c434b2f854dcb4069e20a3b229e0ec3ae3c89e418ac78e42f8da20da40fa0d489c09028ac1ac508daece28d2f6f0daa6da8cc1c27883d9e3e12d421cab8ed1dbdc7ee435c01ca581c716108dcf3be7eedc704f6b0128cab9cb75da5a20e10b03101b7b82f5aff7d6c550bc6d3204b37072de6d4ba42f6e95e4ba1b360f3ea4d356a29c11934589d501ef9cf8c05e730aad3e86ab53feebceb5f21147a0ed14fce5e2842b067518555e43257e055121aa879e6bd851b73769e8a186b4cb51e15c028e986f0571a44ca0b4618b5765ab0b458b8bbed4a651bcecf778f603cb3b8c6c4e5e64a47eb01ada611c0e13e4d223fbb4daceea1797f6e3460da4ac984342c056535c54c1f61ff6bfc6560f5486d88af3dd949390d1e8f39f5eec378589508683b9edf464bee9afee8e8f924ec27ddb00d54276d6238221888562fac481cf2e543351a019fcdeafe714baa9c31f9645412e3ea4cdfb2ae01084ef0de4ea42e9f5a374d372f35e4c8d58dafd235407a0acab0b791f3a20ebe21b600134245bfa0e14f501349b64f906a8c88d2af8b62bbaf755fdbb21a3823bde543f7b62d273818e3111cbad29b0f5e4b3c5dc47c003c8b72256c175ffd9a5efdde2cb142bb2883229d50bcd27cc09d601985323a853097c71a9eca591f4556e3f81cbfd6a998fe782e72eabc1a804f27db2a20e1e551cbfcac21724aad42a11b166509b82848dfb6cd11f50c809d06e4974d056d1ddcb62eadfe32097bc883eeddb1da4ddb0cee918713878eb4e3eb7d9585967660ddf923615df8c4e3f90cb944a54e090269c47a0a5728421375ede2798369e67b58fb6c489604f988cf20c2afb2acf8729fc6795639ed6f18fdddf40b96a1e8f30ccb0fe822c78167bc9745b7cf0c7e3b9bcb293946d0d35c7671e4064c7f84199ef1464be5020e107ea81f5f41f3a0ffd54155fd63306c56c30a4ecd05588dfcdabe717b505dc5c660586e958ed788f622cde4b22506b9bda3f99db7660088ba167ea14a5ebe8c719532851f50d7b9b02b3915643fc5d307c9b694e50e3579a6d3288f10dd09adf8bb5d20d6fc2de2951ebe3ed2316ac5662a15c81d89efeda451112a96fa600d2f349c5c84427979e908b671981ec5cb15c9ae920c8b069ad4926270365a00ac9c99a93dc356c7ed10baafa6e41c8578a6d92f6edd561357b1ba781f081da1289efc72b0c28e12c0c7cf26c82b03ae493052be13a72f01b1827cdc23fa5a685f5f92063c20e65ecd85342d4ea16a161a375b9cada84958e754bedcc727dc6c8fb9ad46d64df1c0bf31009e881b4cfd5b3582abc279be818691f95cf90d0373b09d185ac5fb49a5a0e5d8b0232d2aaaaba93e1e2556ffcc319c548921a5eb8f004bcdf6377f5c4f2d4eb9c16e662fb2bbc7bdb1066e0131722b36b32b8e22a0602a01bdd4902b215be91d6c29980cfba32283fc1ceba4ccced9bcafc7e0a31f0586bb1a499b825a8e6775e621c25592b41d59be24f198346ed75e69f54206f9ba85d10af20c5a5c62004ecabd12485b37d60d7f84a97ee6519072a486dd1cf94fca940f8d095f803e774380cc2f323afdeadfde0f497d87213ad9c76cf7ef4f84f8d5fa140db8f62c15685f4a5a5932ba5f9b8b39a2ce93920b1facb560956d4aac8fff3069e32d5cdffd0a0cc05932fbac3bd24e12fb51c432b93747b10356cc620b1be14cf1dd66ad801cee6a36889b9854ee3d1e04fe7faeccde314614cc5008442b1214ab395c8408a473da1bac8d52e2d5c600fc7f16344bb71b92c610ed05d83c829a817eba991c84e2774929d0a1e0e79b681759e0041d78c0924c474714d3842522be97271bd46b45a81dca7f524946ef3278f21bc336f62dd3491a9d4a20982b1be5e8bb5cdd86b7871566915568b87c9f74393f77229880e2bec231f99ea94369cf9ed8d6bd02c6b8500413e7bef00fcbb97e557267abd912289dd3070089325c3986dbc82aa85d6d6a6c952be636558966fc34963bd4d8b199ec0aee1f3be69a02e57c3240e9658956ae20d1c30d4da233f1a10706bc15043c6491d12e680f3f9dfcc14dcc5b6006251af4e0081688f12dde3cd4a7a5564b024d49fed7e57ff80a65ffb3a62dc8fa288afcfad6538f77f7596a8487a26f2f0aa11da1c883bfcf0fae191a0729762929fec2c74f92a32e657c7c4dd4feb4eb8154a1f0bb72c54912f87cb77ade1e2b06795fa38c7718b942116e86581ec65d9f20ef5bbe0c28953e829d511b612fc66601ec9d5ae718f723c753f90e98b8b3222b8b6fa5a74176350bccd5852f56d9151199b03611a6107fb3b82da98bf0bd70b0cc88dda5f253fab6e60f93b0e8a60a88be95dd869dd0c290cf9a7939ad15e056a409df9164b2b38d00b81ea77195632d034f755fa68ac1b92f18dbb930eae85c0878a6c1e7d67d59ea5b4f69f0d4250dac3474f7978f4baa6dab14c065e4872ef5635f6c258978299025569ecea851827d286609009aa8f4703b655b5b691565dcd0986f6f80be65fca3640034d42c3bb525bca1881c0a4af8995f42336a4c4f49d49eaecedbfd592962b35dd02b123e49ae52470524ab80bce99d95b96e0bde18e81fa29185eadb8c06797fccaa6e4641cb4ad20055fbde4f7d39e679f4cc8240aeff753f0a668231d498b145af3d60d64e6eaf88d25ad1931967af5908e64fa2faaf18defcfb037425f90789a54dff1f09c66fed8b8849b4564b3447046a1883618933aacc5fde2c2bb7cab595304ed2c010110fa7f548ec0a144586589c7bbba72676c44fd697db966dafc6383502e8234dfabbd7aa771deed03d0fa80158aef7d9b89c920f87e392426b66b203ad3e4aa66637847500569ebf16a55bfb7e28846be14ad84458b427d10575d74f072e3352e23e942ba986118b9882be8b3528b311abd444b16382147d523d395b4c5522327b46999346e0f4545df384876920dbaa56fc030a0dbe50252a09ee0b49418b35d8bb2a1d90d9219f5a751927075121547fdd6dc732b9abe34ca21d1df2d40521f918c4d15bc155747e0d57e1396e144756ff77a70f54c4477272646055d24301eb1d781419a61a9ac658810282bac518a2a24c258f980bcbc4713d09ba51febe942ea88a9b62be3a139422140a52da3fb8c1d95d76a42ea43b615140c4c5bb410027c0806876763fb25479ab43a0356f3fb418a644c77cbe45a0816da58f4c514d8c37d0814e319dada5c0548e95e6392acc41e6d2cb26c2271bee188a9d3d79a2f272ff0fe32d93354abbc2a3f9b44d7e9417188b499a304347895a4b8c2f400dc0911574f8e32ba6e1d482f899786d983f3fe9627a4fe349c1a361b3517f6e438ee4f1bb6dab2118979988c815c0fd65546627c0df09e72e666773227240e616dbfb7042c61e83c970f7fff223655a20a81eec621bcb5e6fb40267c9c38a4e979e9a57538d0d9f55e1fdd72352e548646602da08d37a99f3bed62beea655452ba8699f9f7ffc4c663ed8f2298b84986f07b5634855831418cd493cec8f6bde7ed9a96cad51ad706852103dbbcf501d77420f22b8d0b120a95d1f3c9ef5b7e6d77061d3365bf733ea0423d681edc0d17ca00090cd97b7c73ee0420698e71655610e0724bba4331f74bf0311752929e88b6187ce62d32160c2cb9e4356226d4b31dd8881346de70427ad795fbf9618c7cf416878e46532ccea565f5bdccf9a7057642cb6574a472add8e4105f245c531b5aea355932799138bc3212fb103f4cddd16457a670f1bd22e7278f8029621aca9d86a7b72a8c0af6f32e954922131ba049854c55bb4e37f39ec5ce5db4eb9bda72f28e2446220cba98c235820729e5fb6e9f3a6599f674160abdd1dd90576591c417cbb7e217bb3e829c04393d9554366424844d2805b598c78456283c3b0d7d40b74e802962e0281dc5a2355809fdb5431ec7bb4925d63d987a524153868e965662c74c5bf20c1fb9f9f513c61801e73a43c59c30421158dc8c8f356c506dd754c686b28b015916f1eef37e291e142efb9e4d33670536eeae7866b759bb3413f55df6b449cb13315a4c425d593c5f4e0d11e37eff64d697a7fb6f8ad6bd67f1a25dc67688aaaee766dbe46687733d3c92480d44637161d10fb560cf7449249f1a53ba201a78cd6236e6b61f8edf53345b72178e4f589ecf89e771be174da4331f4a6c622ebbf9d5095095e4a541e704473bff0079de87594616063e2906bfc31acc6837c53cc85516a6268594379f38e0e2f84805a9805afc64fc0ae2bd42f83cd7a562cfcdf64b0d1a5465e37acca5923b1d1c720caeee8d7d138f854421de5024808b6c0ed8c95945e6f1ab6d40e92fa9c281fbdf7ce550b3b0ef4cb33d4daa842d9a3a0730b85015248b1c3e744556a9ef290b04ee8790055e326c7438e84239ad34739c249491f9506ad737856103f1dc9d61562f99e9711b08e9d9aaef5342b7ad062753431f7673a4edaaf7310ab0d86805d3903a43bcfb22674daccc39a690b551884254f596961a8c2773524c6fd9877a31c369f3804367b8bcede2b08f8699486c010ff7a9d5e2cf5be7af6a112088634dec09dab498fc9750149482c28dc339d74103940bd0460845dbc7f1ab91bc9d146f6eba4193595eb4b302dc28616a59def5d73d43430d79d2bbf403aa6b8131fcf2dedf26d168b87dfb632f378262a00edc2bdd02d0d10b1b9a66b9e97f0650ff7aedbf23f8c061097c9acc8367422ffba9d05395f5de77dcdc0e7e88c62990245e859f414c1ad833ac9a5f20966ca41d0f0c1ae624ac740017389291e303d13773198ff14850c24e036763a1aec1a7c28c77b74393b315b46f6ba49f063ad8bfa3798655c6d55d92fe2eb4159e402a4311455416c6c7ac2e9ae8dc1e2b66e39179cbf22e70a37e4ebc2f2239ba58b015099c237052ed5be9af61a2987b4995e12571f9141afde2f816e802688a8adef444a9907ad66a93c2cbae464178f92e7bd3797b8c8ec9a51a84afeacccf395d1e6afc9bbf03d5974087813611cafdd8a688706420726df2d69d0f3ebf6e7b2f53246d275b030242a37b561634b0d456fde04b6319ff495e6c69f213a58f62e998de65e387f2e03b1f540e42280567a854b638aa35c1301939b2ff3b756bea4658445b8bddc35d8f64d751f6ff9b62ae54f71dd42fdf4f3c57a94fea0804fe6bc2990e6d1d03a5dddd617996ab55953ab88c7751867290e2a1b017371a25cd6b36a5fe266c71bc483ad49c5c31469797a0c789fbfe9912c61abed331524cb074bdb520b133473969dd38e13c3ac5c7a21faa930382993ef88989feb6cee1f12457ee142eaa52bec7ccf54ebc51ba682f48840bcd019cf978985f862f349fc26c986022718ff06ba256cc69dd7f7810f1616b3b0b39d55c3e3301a15aff081a6a9d6f4722813439acf413dbe34b3fe59f7d8c72386af5d1bd760bf9bc3069359c7d9c1f483f64229305c91191c8188a942a1226629302bb6fbaab244a1c068a1b31fd111537637dd2bafd3cc7f4c1706d564029c755018a2d21c3e39ccb58ea79dc8892fdd5d64ce9829c2c9a8c04515e4b12682a52410b2c6e3c0f50e88db6fea4ee97c16e60c5de56a3c337096ed48efacdafd8ff4c6d293ba4d1d7fd4bd275b1b2b524c67638aff3cb2c148a6099c87ef0547bb09fb12fddf82774d51353125baf55d8b7c63e8ce3ceb479a439d2a6e266131f96e3eadcbfc684d85c0ba35f3ef91c2621459a3e993494189f08f7b5af2e3720385325b4e5df46706454ed5d1bedf7ee2522e6f9969bc8dc6258353e7e3e736835149813f3d5f1b5eecfd464e0e3604b743116231a32642a4aba721e9b52faa5dbb0327f8424843362151ee328d94634232589e47078fac7b541a74d6cab4b387249bad651d1a7b28708cff26494f577fc53e73b7dc4da1e8c2a9672b40b765a46c20c0d8f2b0488e53ce3758a9c76cb8c90198babada2d40905bb6e948951f58a107574eb1c3bfca8387e6059e2114f51c199ecb8fdd47b1de47132e8de97b6f770700919b8e87e5888eb2117cc47dbedf1ba880538248e164f60186577c0a042f3bcf8f151777d6e9891dc817df0fde4852b2a9ce224337ce9f99a0998143ff53d86885748bb731268160232f56ac49e0628b61aebcf035a8be55481594631c6ec9ce50b4b1a95eafb88b167024646a3839d90a7919167bd2c4356d6f66ff9f981df5b764850b3a6b6a3c5328d0622c5e5eec6087ec5d5e199dbe1cf2f80e7a9fbe9c6e3e2de2795155d30ff2d877ef572e6d92de052854ca0eed9a38daa47c1ed5f3209cf738848f73f81b606adc45722d94f9a38e986d06e99527010f221de98099325933be4eea6a7e9c4913cd126e66799b8642e7dcfa5e65d58a310a9338d5eb26c2f13bb28c02447bcc91aefeef06032ce871ac886070bc06a6d7d5473be3383619d9b15458f93f2efad6f1e6815ce59b98ddce2cc43874a4dff39fffccb59f600cc5274b37fd7ce4c154ff5a8066f4969b50d3cd7738149b7aa414b608103fe15b5d205d393461b58cf82ab4cf51c5a412d6113fc130eba416d23479aa605f88e0156c2d8a991ad719f280f2561e5ff12832f8f49a417ea74a13669e4528245a7924682b5bd35171c7e35299a63e8975b30471ef2482c10bb389d50404537d3e948bdae808caa5b506b63adad516702ba6e87d47a901ecf2ae21890548f936c7cb4da99c65aa0d19079b6424341570b667093f891bf69a9eb56cde96102fb84d210025b7a04cc6a9cc45c9241c45b4cb402d66c5897dcd05242c6eea3be3fe7e04655ddfc56ef32740660bf5c62e2ec7e9f75cf9ee3e5a353bc499c0e0467d2ebc59bcabc517788c64bffa61780f85d18cccf9475195e99c4a575b3d6e505fafee4fa786225aad61ccdfc3f1c754bc7c0f657c2ae4d6125777a1109bec35d74ff211672cad9d70331f5c05d7536bc934e6c9bea80a9b0279f5969d4d5bd3f9b4e04efb2a13cb155919bb4cc94b6a6ce73c152cebfa792845bbcf4dcad7cf93736f59c23370d0745de744e387c46ed39438edf9bd536a79dcfd4a76d1aa000e5ee7562b79319b313da6daf636d76def479520ada6396c86ad8a91cddef1b6355657ea487bef7fa7d9a2dccd1a3c363c65c4ba7f712c3f89e9cfd97d1142625a0335c574e15b4b7cc456fc4983f20a8d8394123b089f65f59a2e2509d9dc558d94d9565248d8b4ab1d681df248194303e2423933066ffc2147cb058af3db3164e8cadafbfd564c6dc5807fee967166bde0e094141c3244540c3c8c0a1a6f476a85bd3158735371ea8d91adb95cd55f8d07af747bd6a5992f110cbe78b15b189237d09cc1c4585521a7380464941e3b88c71e9a0f226ed9abeb7a2e53c3451b05fff061f3460f87b947eecd1de8e41a0590d3a9bde2eb7c5be3ae704dae962a2200aeaad30d91471939649a4d3ebf4345eb34218ff40dfcb9f9dc2ad80461dba7612ab74449afd21f9c7056e73c9c22ec4733cff98ee57b16790d7bfcf722235cb21d84fa4b17bddc37caf130c2f62cbe7fb8c862a8032fa3c8aa5c94cfe9da5d35ad48bdf2dd2f426c4fd82b55c230554b06606d479505d8bc506e9f70cf3ed176a9e2920bcdb4335a023e2cfa01280d11ff93136d791948cda82cf205ab33932bca71c50ceb7851e91a1a9c51fc4c1ca555ff47be78af7dfe2258032787cc7e7215935d0b13b936a992320066f10ccf8295c165e03be381a3dd8285e6dcee9ec4b3ad77dfdf81e307903593689d65770e7a65d8be59e933258ff6265621d4567e5a2a77053d5ef29d68ce1d43bd2a02fc5199be4d2664e8cd63b252e490517d0c7053ddc96bbd76eecee379d8204a02708e6258fb21adce26eb6e24a029a9366db421334a90a55f86f64da2efd4634537c5c14b62f1011b67b95f43f63336eee98216fb2fb2947d7ced14cabef66e8e14c63c982c89fd3b467854217c5a7debccb03ef51cbe73a7349e1649309e32b1619c3ed920736b50a0949ffc5cbdd89f572ab5e016a3acaa08e965c3f8785b2b9316a5aa264390650e904d6d50335afa4ec529a81882ece086985b10ec5d3a0b6b4c76c54f59ae465698553319373fe88d3119828343fc45731c69b84be50cf063eba0888a434e088818358031615f6d693460067fff9644b7d26d1b3cf31f71ce4a796b4f0945350cd63c300a19cd9a29eea22525273c75d52d09f3471b6d3f728c70d618744b349ef72172e40258133ef4d8d714f55a6c6862638ec6ce2af49ce6f3421ec54cd18129ab56987e828b4ae17c6e3c8650eeccc36e5fed573dc61d82de238e48ebf584cecfc79fd1de434ee0be39edf0cfca33b5ac12bd8f19fe8be230c2ba6af7dac9c7b5c58e7220d455a551015e621655ae9776b60c8f61e9deb60f8ba6217ce77018c8cf71fcc7299d6b2b82f2a1a1227e96face45c1533379ae66b130a8f8d0ff4e728903b23dc1a756eacf77920522ad4033d4e2a60230cb0f74e86a655cf083495197a5a617dfc2dbc96b78a6db9793da3b14db44501427b7db405e2f33f502f7165124fd6316024b35d627ba457aacb4afd0bc0810687a70a50576ced509b9c0c572f90feea091b9a21f806f3ffa0fc01cee90975d90d04afa57446f4e584ad5cd5e7e1e20f52e577bcce182027f7899c569be5460d66e033e334dffb74c89aadffe16418c8cedee383d09f68e3bccf058bc10284526a390f677067e7c26520745616b92f93dd9248e9a9115635798988cac860c5329c20bf4f3adbc3e8604ed1d61954ac8802124d041f75af82ba060a67fd30d64c5a5acb4c27c7f1fe61b7bfcf4d4a35d0522c90a4c04dbc489b8e207c64f2232d682e895c43e89a7d36757016ac34cace8f97f6ce52182f110ef494582656e1af5e7a10b7d0799c8ea316a90d13e0759005d44a11d01c910378d3be0a2e48e48ff1f9455fafa6326daaed16891108710c43f3d3452f20d1030ee728900c8ae719170dc9d1873310bbd8374029aeb163cffb42e283be311bb06465639156db34c1fe973040a7964abd3b98b60d9eb7cfd5a2a77ca9f4cd3790a1ceb72baccdd3277d93175d667f2ca31dc990455c1b54fc65ad12ce567efc9b6ef4a856d8dd517e945568c3d813f026165f58cce589a8f52ee7be9aa5d337b7b635cf721f591664208fe13c4feed1b69de08f4a029e7f25e3413cf319b6c1f25d36e9141f21a099ce3fd0e131c2355ef6977358f09372469c7636aebe8188d6507244635b1e96a3c9746d657ab89a3561ab56f5862fd28cb09ff7d7c7f281d80d67cbe8e569b13bc58e9cc2f78be9cd4639b6fb1a88bb6314cb6a0b06fdf5fcababb390be45d4d35d015e685aa3f5eab5bfe793e998df76ca543d71e9db892017e9298290eb54f11d6e6d526e6f6688f00199c13affc9b52e69615723aa7c47269c6361bc4d60cd20eff09adce0080c4d8a05873011b46abd507f5f4d45cfe7a67158499d64f25647e47c85b8fc46020ec7984e1963eaf1a7fad638e4fbebb94a49b6c0045f56347e0b2e8d9d7edc1b3ab26bd780f3299fa5749bc79159ed3bfb7fc3762c01f7302d69628bf9baac0c94a8f6c53b423a8d7df0d30e2b034a5150f5075c9bafe5255225ee3a1674e2a43cd6501f0803bf6cf17d790e4a1f0b951d1aa176580ca3e21e4d92349fc3b16ab97270a19f8e71f0c96e7cf285e253fd537e1d0caa83d445d69014cf989bfd583f5163598b5fd609482a580a315d57d5e1b8f6898873922b6394331b32597573e080b028ac069a6f338d956e812d457171c44c51566ea73b380e0b8e111bde859fda5046bea42896fa8bd901f446dfe70062635372c4959398c8d4fabe412c76b527b0a4cc5259f4d127ef0cf4dd17b7dd9b2218c1d5b0a383a8968ec0653dc33a651fb07785578e50c57693f4efd644b26b5ed7a555724a666d287b2a0b51299dd3091742b6b6db6fb7328173858585879c4ebd2ed0a5f1609da763a8ac71402d83f2ddb8305b1eb8a1b3c0685f1a5c328a8261df3cc377f09c07288fcb747bd1d555cd1093af325a0fa4b9d7237ee1df21a768ebcc600ac75280dbeeb3671b1a33b9bc569a1c6517fc43df44bb5d110fa8ec61ec476338a5ed638cb5d3d7d7ac9f98dc963d203d5d1644d66d7669265c90c013070986610594a6aebfe3464e5eb6b394e32ce7218b3ea1276c83bc1991cce9ec48e0b4e7e0f3cb5a2bc7a704c7643313838bd89c339f927e5b4ce6f08ff79fcb1b9e3537a5be9e927b090fc003212893f82aa3a58b81fb136ea8d4c751221a274c8d2039311c37448a01598dca446408e153905c12524183d9daeb762fce3e20a50c21143f69b6cbbe1a4192fc642ca214a855051cfd77fefc22073813496d74316e8124630971cf4dcb9d7743fbd9b0f54f3b910c672e6a2ca9bd5d7a76960052e420fc6055c8b0c0ca25060e685e82efc432cc63fb8f065c3e47555a434c6f1b11422bdc03ea04366e3b3f9fdaf9005481fe7104914a0415d560aa99a621a98124659325e8fae066d3eb6faa103604fba37c2a53baf5e215749576f5089c7c695dd3d19a7de26b4804bac6f8a4a56e05513c60bcfc686c6f803005855b5c4fd1e061b3ab65cee306836658cca29bec762c9cae547c1425a1aeb135a968ed4f3e1ff20139f649960fd2d5df998efb9b92e2c448539a4c1d3a05027d9126f2525a0f82844de3d9fc55574ae1603334c801ade6b1bd14c9dfde78b2e3e8a904b18461c1a1e6899a5a2540dfb9f9aff0f902917463910ab0c2873e0532dca0d957e1b52584e638b4ec35cccd043bb0b503313f3391071af7f2d5359260dd36b7c8d17ad3748ca9b72ab86d30900ebe40996e0e16ba6effd863cf6cf9938bb045609ce5463116bcc7d00f86e353c15306a62dbf01de74fe3feea5029cbfa8cbead30861216353b093908201b95330725ca1a54c8de0d142eabd22b31295632066fff72c550c35f5e63cc750cd5f53e27eedc304ee103ba36fac3bace1bc9bf200cb812982eecb4200fdbc54a6c9938a268f13855b328ae35ffcfc56af6042b98cf1ac244585cfb47078d8e20e8251414dd715f16b0c99a6be2bb04b4c0055d3649eb1b022785c307ecdfeba451f95d2eae09d15913c5cbf96e4aacfbb549791ac8762fa86eb47542173a77750963336a3c7f63cd1d5cc606e30ade5da9e2956074cd2e375fc4355736d08f31a3a617f4db7d2517b28152f9f069eaa9a25f942482affb0d2b5ddcea02c09e72f54a04149a9640e231ec56fa57335e642953430965f0de9701eeba6ee55b1676b274ed28086f66d94105b2a03849ec8a47439a0c242fb72661a10e5c0df19ecfe59f470faaf42d1ca8a15b603457520f41dd71a91abb741d2b54e421adce651a95ceb85fd804fffa46fa63e4c4717690b1788c68f05de328179b32b8e8b2c435fe99f1d3fba76594e76f473b69f190f7bc0361267ad765ed10172a66038734e9b96b1d3559cc8b8ab52a5ea84562ad220fc62aabfd5791731c4dbb06b6d911291005fb3eaf476a7a6f0ccf3139a496e8fe8a23acfd12fe236782a976c6770ef737dd940d6ab87d20d6d9032c2562294d4c7c3c7f76b8e40a0889b7a6b8c487e57fb1f9329e3a5869736662b0ec856bc58b98e6b6fb15ff5c5cebdee3059f7d11a7d7866c0cf6eb0442ddb505c085037ac3f9288d6694b2b64ffcea8e1088f32585b72e9a7f4be1b806ddaf813005c94cb5d97534c016518c66652c09266a2325656f27af9f9de295e44718e91a9794a4e0f454f531dd00860c5be30c37415a886aa3d5b839d5fce80100cee30ba45ac55a988bc7479adbfd6c50dbb7d9da65f605319b1787ac198aef66249670e9b39f6d10caa0ee077aef187d0b9d6d94e44019c586445769a91ec8e21ba22643a3ecfd24853f9863d8d549261bde3260672cc254a26e328ed56b92d10e9e4938272c0c80bd46628fd69e694a979d8a6161605c6202473b46a593b2db400f614f332dd579c469e5b34042061e725646efa3f385a7c6aa850981da091ea20010cf2c35922746529e09752719a6161448162c3ee35b27078088e6ccd53bca08eff3c44b0385479d3e51c99a28fb3b76f64ba9c4baf77832ce326acc296aa0d2a0933d7c704795136ed987bd3fe26e3826d3b0ec1dd2437bb9fcfbb0eefd75b104d9573270212bb25bd841760ff1e6f8005449b69e6f6fc83b64b88a3fde573bab5fc65dda0cea378a6792aa03f98e4e2556f06300c018ffe2b149d3a395f47904a17610f9a17ebc79c8a01317b22092d81c4347970a4d6c7670610f870db651c52448996e18c5161ccb90ca5bf60ce1e6afa3a41f6932bd21e28f2afefa75c858861a0b441d30679b7e107110226f3cdc4f38c79b48c034e4fcf70c21d9e806073da506614c61774c84b38f2ef4334fa04946d0c52e777a5920a264d8f13d63d7102ce8c0035b7122026d8d1319b0c56104f75865f7a4f1344c39cb1fec7fc5a60db078e1ea3273ef91ee6c7171ccccaa754e034ec532453a84d90cdbafe5890059c650d7de77d6b40db9bc0e08b5ce6159998931111d7022770b8de8078b87e303e066a172b11a7c411e946e77deadf26953e16d5f4255c0dc374fb1b0e55ea5e48f721923c11ad65a054534030a0b571a5b7adce04e331461a8b4834f325162173bacb1c87b1f34a5b951731f70cef5919a64b5d22850e0f13f608a4119db8da2f7ea25ea812715d941e209c18abb7dba5b1f97476b91e913a6e51baee5aef53b29d0b9042ca14e2389f715746defdc2f0b8538de86dc5e5d8003f345dcac5954fe1db319c1d3cd10c2d60588fa6e475968aea928ab73932044f08ae96c97e0e70341f38d8d7bd78fda726ce76081a147a7f34818ef957514a9c8a31a6196d3c95e34c7a9e179bfa68f45521fc340b9bc2a42e8f9e0ecd8b8b0b84b1d7866cb6dd5ec0423711f9ed41d7b6f482b1d3ae61c67ac21f00d0daa71bcd88e287cb659cbcff247d38133a263802411afa85fa97f397e2b215ed12ac5e2af9402f35d98eb2ba2ae1d6fcba2bc68089344d08fff820eb748e66fcb29b107ad37cf277b13011959e705e69fe984f3155b51175c36bd355f5697a19329aa92177c3045042f60edcd3249c781cbf6184a0f13752a4e4d7324b771344bb43cb19a27f0161ed25bf0edae1bf2edf3243d3b27fb7b0fb0ab58a7a03ef2a8a8af8a5ee8e7154a25f8a20eb5e1ec36057beb6c37b9e1bf27a8ec2fd58f33ab8df2e65cfa50023da3341329d74bb6fa2d215e41aa903c0b95107de3d21f7ceed87ec3a11528f933fb5a584654f5dfcffa070c673f02486e432387716035e96609c7a0809f76d26fb380dfc597c6d86554028d275873e19d0fddc9562e44a6e2f31514f48bb12ada41be514b040ea2068cf2bc622d313a4a8e54dd01a5b9b23c950d11616faf98937da5a8508932ccd17595cf0013caa59ec71d44cca85a638d2d4f2e1b6799aed24150503c51ec5ddf96b818415ee2eaa0540ac4ff0c3dbe315d1fc4a00cfc24b418ee47bc8b6a187175706e2d5aa7a2685c1039cb6e6d3e07b0e8cf2a396e35f0ccecbb817d82d8566901eb20dadda458253b97121e9908a714e92a6f0a3de7485928b71e4d2bd085fe4e381b02e6f5ee093629c921da8c5492e409373973c33cc2b10f5b9286fa42c5e44e09f91fa64b387b31e377a2db9b18ceae7a1b0b398c82634de00285ad0f6dd15366914dbcdd84bd7842d6104921d49c3680bdb8a78807d8c5429bef0ef8bfe27a4b1f25349aee68d7901044479c42dcf714fb76a91940b0bd601e67c614ab0aac2ebc215d3e3b4ed99ade65e2859d4968e09b5d85405c36ff0b6fa3bb0b90c897124b461269bcd61a310130ae5d60f525226d99fbcfee37b90f65edf0b4f6544808d67995ea1e0fdceac1a012358a5a6af97fc77a0d9e8d8177d1543ef9b335756be51a4d01fac1c42c1d849512bd35490b360f80325f37627e85e9397ddef58f1874a39e6c0b6ccee626db08878e440fcfeca34cb834c85319ca49db28e432dc2b194493d13dd9a248eb08d6a8b67fdfac711a97dd755ca0f3bc07938e955e1773e7a7417974221e707d695ec7dc2e0afe9d40dbe19b1bf4b38c85833a078d6f5b359b6797a83cd3f622d919d7d1d8da72a78718017fe4183cdf53f21db3045d5678e88e1c40f4e2e024b38c2d28f51ba1dfc4bcb43d0ffbf4eda11ce0bb7188218edbfc296c94ea59ded91199dcb153fdfb795163462f001d8968beef3efc5617695d70c03374991cd11ded1f57ea5fa0c950f7a2938da05a73e1dd140e5de74eb2f658a0656355cac6f99b1eecb96fa5bd59520aaa4a7d05f5b8d803e0ceae1466ebdd4c191f7bb3206274a2f4d29adf7192608c35dc9b07047d695e3510a61d2f6bd1736e67d28e2a9d4adef2bb284c56c9e614c12c17207caba32e2eece20626bcc22acfb02ded1cc063d26a83b46c53a8b967b9b4456bcb689dfcebd2d20c952f5b738087fdf6bce9a215c91f3a2d27d9c763d52f786b1c7cca1b7d00c06290c02b5bc591687de78f1adc293a8dc714eac246102d3050ffb98ab3373db5051ff26f2273fec9f45610eeb2d1c53ddd49a11e4114f551c2838c8f1a3a8a2b35b91e3701badb929d305822320c7fcb88bb6732bc2270f73e043bdbe1df6c0315f8cd9835ceae053c89c4b66ec6d2fadb77e2fa938984c0002fbd63478940706e63a50ea5bd3368473f75ff6632014b09af72b11f81e1e488bd760b0d2d816561aef6d88305fb479042569648f8a90633a8a3d3205d08b6b4553d099d965b0e5567c7b596121b46110851c0bb64b95b4ef8586234a68b0b74832781ce8df6dd4648921501628da0569507a3b515e1099ab0a49e06cbae468c4f47cf8422931f16ea8c66ff02d4d31c58db38cf85f50ec09c83f5a94421ddae2561cbac377c306c733f54e6ffab3f0db4fe07ce87754cb77df0714054fbd05027d32d0cab155041101a4ec836c667e71b13bf781e110ce4e1e48c09669f4149adfe2fefe56b1e4c52a3bb2c30a39dbf73e84c2aa50ca12dccbd85fedc3922094326d4ffc5e7db16602f60fc0909ddbfdc5daf8f456ed09614c8f8e3c70d0b1f35b4563d414a07598617ba72462c155dfd44579c3efe47d42136c5b55f8d0d63062e914c6a2a3747c195b950447dda9d2f0ceee910581ee0fa2093a53e3d489993e2eb3209a0b01e9139b06273508673f8e7af3e90b8bc8beb34ea93917f89ae9c07298a29e545479f596901babc36355b4df8df79a3751ee5214e297bc92ebb770b2bbaa240091f05ece0dc2186f91c3252d9a5476ab8f5124db469d1d83bc8ff71c0a6d3acbe1d4a05ee7f21a1514f2b6d70e5692dcc061694a42aee38f9d94b7875f8790cfe1d702318336383a68748803eaf25b895d9e468b812792be4a93f9d5ff36d681f47182ec2043e19d4b053b726456ebdc74344080ad04af044f860956291526bdc535a4cb832aeeeb993acfe4a1ea04cb6c60322cd78f470d2be3466edc028699f48726297f0eebfc5e3c64ae237deb49cde29d2d1368c896fd129c45c381d90db552d968b281da15d1638e478fb95617a3bcb60438a97eb18eee50f2c35fa5290267fb22ed868a0e125eba4eb3226b912ce4f99650e4b001d396d5a9eb8114be964d6cec46575ba8c0ae3e38cd56fb68f2e55cca5171280687b37d4166528f3bf6c648eb6ec26c170b311ee00808fab62c30c0481738426c21e14fc1cda58097558deec23574e35b0abdcc2ae7cdd8f20db88afb4af31dfd8f9882a2b70d43a842d1c8dfb8859859486b1d7bc2bb5cbd6ae1a3389402cfd2f41ee9b377071a0c41fd1f6fd95e66ddcbf36ab9f66a82f4f90ebad97b22ba2bf7843ac4ef5bb754c2f5f3320a2e16c2eca0a8bb1c6f36d76bace539b27cdf2a63a7fcf60cf49944e315d91ce8274b31a7c32e807e9cb272245d44909f10857657bb3ebae622e471f3633995352dfc5b1b3333aa552a05c45ae6bc1808ce75f080e6bd1ed897d969e3185e9dc6396419b5d762094e931243c55a1f4a63d897bdd0303d99c38fe5e356b509854de78f0a3d9c0e63d7ffe2dac27933111f4054d1d0db2899a8bee0e132839c6f600662948e9b5a0d5e255c526a70b5f4b7abbe988b5ef982d6a197f3f206183b401dd6a5f821087cc7905dd3b38a36080f17ba8e2ba73c58aa24826ff58420bf99c0380307483cbae627307ca6fed73efdea1fb0e9d9d91551f29839c64b96f93b60e51db991085240db51769597a58b1410177463bf4eec2b75877ca3ed517aebfb4cc73a7e04ee3c6782b445edd5dc51063e724b8e9fb6608fcd9b00613b8e8f4dfd281ed621124003aee231f657541ca0b147edad67aa72af08c4229f9a1dd075f69f4c84967ec947ed097d34ef5d35cc043cf2d564ea4dcd82d735143f64b1c7fed0193943c3236f6023f2fe07a3355fac4aa43cb8c00a5a4c65c41c8c8102a49a8a9ef567ba8a9fbce8187064e3e20146f40c8bf99206f9f554652e29402a3d74a3c12e96340768fd3e54abd82720f45e9f50e6b437bb139e11f36ea1894d688395c680b94228de357b782bd35809d1fb456a41854e9032ce09b2e5cec62fee3df8aa1fb914e8ac13c44e788dfd7001b72cd63d92b471be2ced617dd8c68e403f818833fc4dc9a217dfdcae92944cc497eb59777a32c3e2ed31068645aac8d4cb607f7f243a4be02103bfbe6604207c07e24681b35d01ef0cd874b205d22bad3eb606c54dc22f7804f363559ec265d38dae1af43f39167915c9f5dfcd89bd17d27d48740376211751f424fc3e9ae687f9b7fc7df0dae0cda4ccf6a130f8aa4d634bec704853c3b059af581a438270940fa5065e8fd44d53c962d22242befa10a323490249a4c06d8e27f9e1c5f84cdca0d0a5b1ee901aa053d8a6e90abc06cbe9fe8a1ed12d616faa3e81be6f494117fcd32fab583a243d3db6e4571f5ec8a8234ba3a8c3c6388b9565d65a05fdc717d8324acd64685c624c6ab51823b1db325643cbe77838289979e1dd73477298609a57fad7138fbba64a2821e28c1ce698e1e1b56e0f7d49c5a199d7ceac2f0df2d72cab8277fca298f155d3ff79f2c488bd9f6bf0e8a167a6af417462257e8712d7a1481487fe05d261dd7373827d55f7698d7a07260422886f9f249e21c0ffac40856c1c971a1dc85939a3d16b3c0c7738ef28ec5c0af03cc842440409f139a9d5dad0fbfa7ad04febfc454acf009ca1c1841ac0aae48747c1df12e5a7862d0ce7daf5b54b39c5173380364218f5fd84fab4eecd6f11a637dfcc270411874a08b4ffa2420014bd3bb31186675d3e73f2ac70f036030f23c8a35741c97de987828132d9aba47dc12968edb3b47dc3d7d0563e16fed5aeb48c36ff7d643cdd15f5faf7f323941a4e3b24927062c612559f333d30a73cc050b71ded36a3aad0165342d6cef54a73377866ea23321ffb26862c92a4a14fc35019bc90fa66f849247e3db39ae93938d05a5d8384a2b2f741ce059c8d869552dbbb17f09ff0632f86ef851c8eb96adc664108cfa5e30fd1e1b91f9fcc3545a8b4f140202e0a451cab44a43fa9f27c637ee2fe012585304ee1d1162916037977f0b8b24f3a33795315bc16156a65133c959a1b5fa48399a269ec6339665dd713a1b22374a5dde210659b9fa1169584d0aa85d742f6de295f472d08189f37f20c329d06fb31af124b6eef31f28d2afddebec34e898549133542e8883326dd1090c276cf27de8b4de12cbd02f58c270bc74a239d270524b58ec5c22364ae1ddb25abf55d1365a693574de300aac2121a4c752fdbe561f3bee8dc92c0bea65e998971da0d457d840838c57068cce62861c77f08a1f082005237c9409ad6cc27cc3b707e6f8dbc727d1a8b4a5d468d95112b7815c2214605089abeee0511dda7d255a60999dbc4622fbd4183f93a7750b4ed68a632e913ded98a66218092cca10e7c7743ac9e2f4e62e97db886f719632397c40d46a0b69d6bd84a765bacdec393fc645cd64e5286e2df9c437cc8a2e55e4f5d6a86a4f1f173c0322a95275f719c9283447c66a67fc9094f86ccbfdb72b9811e3f4943a0fcde23cb773c7fec68bf6064aec001ac4bf8eb1824c00e4d55db8c9c9483bc038a626687d3b7760d3bb0b3950726d0c88e38fe89f8185792135e871fbf445d46c857b30a83c761dd2bb7e5b4617618790f81511575d3d0f3ec8b7dca1779a4756cc10213d36a81a4107297bbc7b00240cbd8201e1a6899c0b4f1950b2942c2b00998341b21b89acd6354c0dfec77dd053c473befbc7af875f980afb52df37542d90965856cfb72fa5e89e5a7702a779135dd7ea7e1327ff696da5bf3459cdb836281703d833c0f360762d37923918104a94017f21e636f4880912da782793fd1454ce5aa41908f9098c478fcb7a9af859e3d483cd887070d02887149725c6e038baf84e09e2a446b71dc560a3cb616b61ffcd22d1ebd98fe6fc01d7d2506e0d49e2c71586ccc665cf2686d6a1d139dfb86e64c3fc2c8fdafab487ade1eaf09b14c184253b4630c1846eafb40d2afd3f0b31f99eb2880ad118a425b34cf2cc6c27c2af632fed2e78320b9a08b848f86495826021c968780f00d227887bdefaf3436d100e5302536c6ba580f80d0d640a584a78b666f5f3bf7fc3fe5dfe8e208f18e938f6d539604fb8344b6a6c14e8ca93ab9d815678de1eab0073e4b6833cdd37222af09035cd6acac3f318561c716562fa0cdd569de6fe9c615e535b53a07961af24bed13c5ee13a2a36e4ed0be58c614ecf511ed17bc9e47fa72298980da14badca65b86a4c789487127f2323c5e5a670342091418f7c28c084e8593b0f1d29c6effdb7197f4b16eb20f7933ec715e2a6a94034142c0a5e01b1075383ab81405b9e87fd423ac6f55e0b96ffcede6e1ed4e853c7f2e38929b6e2d78680e5096763510a0363a6135823cfb3c453be1a9baa32a1a00dd23b230d33bcddba4f756e04fc90c17facb79f54476b002f65d0fd5f8265ce16f750308211ea6dd3d2996fc448183d20baaf15c0b140b42a1188aac8435a8f4bac33d7193b267090cd213b19f32145a190c9f9381d4b260cba99cf1b5459635c1dde0f7d3b094b6d20e3686a0351b0d3d142e79c73c5baec25aa15631bf8daa290938383cd1d62dfbe105c86bf5841cd7f22ce397b86208209f140f6f2b7dbec37aa9b8268f7cac4424d62d05cfd59393afbc2cefa26cb593c04f2e9819f4ba1664568978001a18c06750c45d67fd6e640a7400a5083b8f62a8eaf0c5026095256863bae3b6d50bf69332c3c1e19b5e0e32211a1e3df89f292037e33c94a76ec97db0209538b5649090e317859e9e9ebf4217bea4bffd7a9d89d5eed8127a486fea657e69573edaf02975ee352c3d152d85fd622f43894a9ea595af438b59c75c8c0807131f26742f3f5032ae08d6d11f6d3c081b6022ea92b5b62724b4c33d9f502e04b0106da94eb21657f4a0780b54590aadef145f2de485b532956aa78fda24f1b52abc9ecd673694a5ec742def9096dd9151f6e73ae1fcf1726ec5071540d0cf85f45212236f9e872953ad86cf8a71c942b03ad63443af02faacceb6e88696af9be2148fffecb4a11177b0fa07eefba45e7a6aabc7c17032d6316767bc796f594febddb76d0121c71c251ec39b7957f3401e61e15f8c97cf183bdb84b2e91ad5ca36f598bf8c63f56d930147babbcdbe1b416d512bddd4b62bc1427921e21e42818e06d3da455c80110090c8060b0fbf89c6a3f2613dcca051323795857b4cbdb19eccca9c096b621f6f18441f2ac10fc3bc76d3f5b69c8d458dd335734d5d6837344afb34046d00aacb0d03074f9f3d5675b9e8db227de2016af02f313a680676c55fc11f20a441e2be104c2a2421f0721a10a519860e914ef5f0c416fc3918ed76b9c387881376969f95c635275b38e4ad296e67c32e9b458f9be8802ba41a0e4d2086bfee0cd52fffd1287d061925f13dbfe60d27d476a874661a19cb0803b884f58fab96c9d4d8505810f9398d5c9a86b6f0343e809f266365231089b35ad98a43358df5d980e0a04f1511b02815bf6c7ba2515368b33c91a1a269939b48541883e47f0d1fc3fc14bc7228a775a2dac009ff49cb2854c8879d871d9fd3726da95e12c15630760c97f90be43cb7f0bb005080a8df239a4fa1bce31b551f7339407a991d8d59141edb24da6c17e91a309f2db1c168a35d79963aede85a172aaba0c00f390e56d6664acb9a81ddb985cc377f6e9411bd7d6bd14077fa4e69784e8aad6c032bd9b377a69fde8ac86fd74e1196b04da679ec58a7950e5712c1cb9faed98498a58f9275e7a0f6639680564624b73f8f15e3206c6c2665c76068214f073ddd8d51dcb00316a77fd0478887ee6dd81c0b8a0a61f402c0c566d920ff4cb36bc193e12e457f77ae48dbcec6bc3a9535726a2237c0a56840fb801663c38b86a2bff52a1b726e20f8079d71b698945d0cbae1c51c58e0b867ef31a9c2eef20ccf35fd3c714195eb202f561c64b0bbcd44196fb7e1765a876f8e6328151e199e83fc2196b5915f0fd18548bfee7c14b61b4e495162f6c8026afd931bc913a2712af3f1e8f741c2b8506d79486b2e3aa39d9faf488c8312de632119472ee3ce369fc5ec0fffd2d1597472de6dedf16e7dd8db7ddd47cc01482f1f23e5b38c18944df93a1c1803c795db193eed054a15204574b7db4ba2273e51a0f3a94877eabd6341f6972b63d6da859c0c79aa64021d5cb3fd0ce5f79e7d964e27b7ef70d1a5ea40d5c33ed2b6c79f9680a184dccd8291b7e39a503d959feefb2dead771c7dcfe0bc5ab0040bb9ae479402a84b358f37d2dd932f9150668f3a94009cee410dbcf1cb5a13a030a8fbd646c78857ca1fa860d69559c7d54bba84ff362d615f739e558f68b26d056bf234b52a562ed1a97d75ca05ecb71d0a2767cc00e4e7488b23d67620e8d64c8780ef80d79ba8d6138d1d96112fca95bfcc8740c297dc0f7452a9fd4708735caead6cb092c080fbb4a2d771e91d1e5770d8ada8a893551c4a639f4ea7e2f80b014be0111e9b497e40151d16793f9017da1dbdcc9031e8d4f6091aed98169a266ec4d0a6413a7761105d92043ed2f69ca12696d74c49d5bf590e6164113625173ff8b17cfde921dbdecea315e190449cbc27b26ecb4bc5387ae58adeee56b929c61f50e5a2c0c8494736253d1b7e1f95744e766c7e9070a3f1f2c1e678938c0d1586bd1a2d8e90094572c199e939418d182f8df3c266943c40ee5cb727d18329e45d439671d1ab92d3b64ad90c0e319b7e0642916ae4fdc91cdc3ade0af74f66cafa662ded1d4a8c4293835d30d86e38f2f07b27500b3f5df67762f53043fc721b6a1eb410df674ce6a8459bc152772b0c354454d9a330e3a9617b8a253c715791750873dc3bf25afb4e0339c5ccebe40167a8ca1b59eddf5fe3a7d6d3385606c8708a2b443df65bfb9276b31f935b828454763a3dee56c25b4ea8eae1156858e36949c2de9808eb3f75db2963cb2965da341455ce76456776a5b8b4561a8a07faa601c920786f142777e88ace6a0d865c57c2e803f3963ebf12ea34c164ce2ce47a0c4e7454967d37da1e10b7f54a42f63a40cf3d53a25c2778ff899319ff4eb0cc151db772b4853dca057fb8f1b6885363dd83351b078db4157f2a0d33e71ff48ff87675347225b769e6b0d427c4169b348c4211b1b3dee46c6e245bfc0655888cd230bd531b848fc5825b44260ef6d0b98cbdaac6e7f2a23ef3cd62c32c601d32afea163e12bf6931bc3c830712c05ead10dabc74ffe62723be957ff58c2f87e0d41cd6ebea94791c4d6ee5cb4998ff19539b3bd0fbac0094ab11c7fbe44e69ea85fed224c83d64fa97a38465debc8de75d2729b6888f92116c5a45c645614002cbaacc2a2f604361358a8db99664abdde8d1cb23b447edcfda1e92fd1d6a2169222671d017fed0159fb73a5172bfc9a35fb11562aab5759e2af3e2658e05bfc6725a92b978203e1790beb4e7bb24324488ec972627e24a08617235d3223654baeea98eeb01a85fc52c3fda8c246e10a76f0d399d320aeff147f219f652163a14ed673a3e414727ce1ebe9131c0b5dff69b9ebb95d7e62a2688a84dec2949643f1831a98ce71c24af968773d5cae20e7365cdf2d3cbc456ceb904734cd4047f81225fcbaf637ea446fc8b963e1499e9109095699ca9305a4190f360ad7d34cf0a12c3cfd8d3f6e1cf368d7f52bb0d42657725cbefc35352bc9d204f0e8f50d1ac670c2feadf389268d7a2d7c382d559e83e00fec750eedd3333e50a3dfe0daa9d53ddfc70df3c96d283171bad9c846d834331ce3c1afbd63b9a05fd119e5a3bc8cb97b975c4b52ad0884f7f0f33e39eedaa57ffcf50ffe6877a410cb4302d804f75918ae38cc387786148a5514efd0e530de7bb0f8ac8557b1e3645e07a1e519500d94fbaf649e528e6520e641faa30e34ca5784dc96e918f48ff8043ff3d861a278e5e6bf92d91f5dfc64f70349e7b4730fd72a5f3e7c9b04adcce4cc31922239fde14ddbce816274b2c5fefd0327b4c0ed355d88044b63cbde60fb4b83d203fca99de614c6fdc6ce1ea0ccd4c78150aa8f8c074370938e9884e71271f1ff1ae2f66aaa5159f9f786c5ddae5fd14bb2f359c4c9eab28054d3ec83e7fb6087321f7c42654996395e38cf9a2395aa86df5442f8ce83ef31c8cb23ba3dcf808ee5440dd3003172c036a0f783394cc156b119012f67db2b8b970e6d25651ba6a0e6f505cfd7c4f6c9334dafbf9b4cf1100fc8a7a8592391304483878a9ad3563ea9a4dc79a8d8dceb920ae17aa8905b2816edcca19478ddcb8ee8fe69133322ff2aaf71c45829f7ed11ae73304cdc439e086f2a67b741cdb0fa950c5ba5c3811c49c088bbfdd77ea7085b59848784a34db158688a3842cfd5f60005ba848d733e67f71b62c128c3f3c32c969e4ec562cd3cdbfd9418c657cc0ae8d5c933eb30da53070425b949e80f223b5139b2eadbe19b97d72f55f30cc24ef73fcf124b6058f62e618b9f6c09f9d1439dedfbeee5c59c317fc7b3699d6d67a859f15321001df16a3d5065bd280c0941ea7cde474322cf8bcea055339c17b45aba5674fb8732f20c9998fd000b1fcb62ed99675173cfe94a73b61cfe4dcd1c6db9057aa203d4b167b2c25e8f7d69b435ccd46f869f96373be6763830b0f8cbab4f9d8ca921f79b909d4649ec649002069eedfb3c201280c393e73025fcb792c88833c5d1c082a8db2fa75a4f30eadd98f1e73eb6c004b994166d66ee6761efced89fe84f649436c1ae135044d132be03a5c1b1541307c266a98e0b326b0c2f5874526e6922f89c89441e4799e20a01ea3d77ea59b7d939969af8433d2b22a627076aa474f61a196bfe231cdc978a766ea6eec9e2a15505cfdd191590b597a77d50879566767715845203ed18cb3203caa93ed9ddcc6a2bbecdc1f9663bc54cb24a1e12b8153e5da1bcfc172ca3f7e2a45870cf678b5d7534291d8f89b542dd0fd2c0acc1058c0b035db3e068e6c35be48e2c95aea14476302ff47c8480c7a08e04392183321af4b0157ef079f755f9d696eb05a33a3e97fe99246b543c1f34fe7abfbcfeb73793e2d440a8e7038ba2c88ebb6b02dafe359e76dd8d11d6f6554379cd76bd8411a29753e2935f2be3c0d00019525c475454ac77e02869faa8bb145764f83335ff1f3fb0ddf3bdde1f95f2df734cf37b2c9bf0f98c1f2a08e308f4f7270e74545bb5096ee45dc3b68eec7c9c4fffef85d12fa0bd3ba6cecc7a1eace18e5e28e57ab3caf2eac10a2bd7868d45dedad44222e2ba990bbb6cee4a93d917c3905f65c53737acec074ab0b1c63d6a94f4ef8694cc2929fb9dba4f9379c6b1010f5d85831e60b0348f65148b6e13c116379735ac8d2f93dc3838b2af7329a25ea9e7d231c582592330123c211489da6dfbc4abcac1f3d2416ed9d01c3d526dd2c438a32e2416ba9b27fcc9fd524ded2577a290e7e2b632ed012eee31a9a8453acb52715985be198bb8073bb14d27814cbe21003c8fe2aa0104383634e8c637417117f458550bf283823fbeae90e923f4a352d77f56adea93bf470c0298153c8535f7a6d234b7ce53e536dfb9a1108871b8ad7569ae61c3516c3d77378036b519e1c6f1c707a03cedcaa3bbb7a6b0e8542fce2f682437c14d9572e2b94bc96f429b8c7130c4eee26bda06cb7e00ea11e5ae04d9c6c06bb7d9fef96e08fd1c3b70c3bde3c74a6c30cfb0cf961aa88b3dafc77cf599cc6375173c426bcc5a60211af1ab83853e5aee8c8689fe582b2e5741973c28a2a40226ed378919a65ad99670e838c5eb6f6bf1bbfcd7b96b38842c5187ffe8e2fdf665606c8292078e771b0c413e6728f0a0995848ad6ee11bc052e471737fec5a6dc603a641358b7bb082f8ab559376723fc81b8df2b6a3349214ad3d1a8ad166d3b9a9e8771580913b3fac763ac517ef4ccc5a108cd8aea3edc813f09255228a0c299c9d190905ae33c7cda899f71cffd9fd52c0033b8fe361d54165376b1594b04ef84bb1d66052236627e2fd85c52946f3c7ce6019daddf4ab710a5bee58dc981d030a245da6f3745c62057cc37bbeb7a21ed1041b42e54a013aab0bfe1ca2d7d57dfd44e47ffcf08bc8852561961cd9bacf58d5e102ab6fd2a1af9fb69d7caae78f587a724447ac892c573f750a28887f12f7677785c7e367e64cad7ee70e188bb2d9237f693b04b9c8e5c131b9dcec4d074bf420d5edb15967cc41c77dbf0a4e50eca0a1e2bfcba77a838e7358b7e312aa139a7137b8fd9980702b22ebe1b3df8dbeb6e4b49c255b86a1887702071f902f9981c1e0e2b5af3b9783bcfd49a7375aa7a1c5b0dccc6e6864c68696cc624b9d4935eb897ad60c245216e017e1683df5ec286c93a4a551a50db6247f637f0295bb42caef430bc686e0fdf9bd59a148a5daa89c531f08d1ec371e95c55f0c30fb2c4acebbd91922a618db73e247a8e81893b249647db5585b7c8e559eb3f4fb79ed64d4eb9e595d8cc31ca41fc6e1ea8faed8d1123d49b91b2872a24eb4513d55217b36a435d4f4a1356aa851df4c8e4958afa4aaaddd50235b4450b28eff8e6b2aa42149a9107a6686c4477e6698bb9569c3823e1950171ae7f202f74d092a8a023da80ad8b832f3b65726649f1848ab5e993753d38f553c618f43a87af3c3ac4cd5fcf76d0058b4bd4c9e1d5e97eadcb96984891d91b1e93b4692e9e29119f349bdc7110ca121e5b769ecc05e7f454824b6c6115b5eeb2d07978417ec620590ee083b8bd39eae49efdaad97dc4ff1b6709176ceb3505f6139be73053f0befa90f1da86f2572a4fbf9d552ade2f2e40014302d98947029de5c786094f9b77d9032981cdf71668aa9232b447bbe713cff9d8b2ba290240f3cc6d0c63d183ad2b0266d4bcff0207ed18c682b0924c98c648c3f6db283c80f5cc559462ce292b782e72f18a74b7e560873bbb73d269b80a50a43620d5961c88ed8f553da9c712c1cc7cac9ffd83bde4770cd4665ba34c4a5682827d12590db2cb999641a3afe50826995dbcc6c82705e7ffcec6204ffce7664d7cfabfb3b3d9fb19a63850526e19dde66a096aadebc6466f55391c2b48d3f2eb4e54f0f9dc29f3a96008d41361990cb7d5f6c6890cd21e891f4730a7c4604bb1423bee94a91854f7f94c576ec87d4eebc4c864a9fde53311b8120dda6b5cd00b9567a5595fc6d4b8303669ea32ad692740c0d1f5061529b55bc673a3c5ee1461fb154c736396fe58a6154fcfe48a2f1c680251c9dc72bd31f5ccf0cf2993b37627087c1b2c1c2df60c0c9e485d9828be5952cf95798ccf1f6418dbed2814fc5a0bfb5ea6ca494f073c00d912714a73d371f5daee947c47a629224811e908de26e417e35e40fcfe974271621269b651bfc9050de8ee5413751c46eee356b5068bcc54e4f6946d0b00b92633fb2925ea47f81fbedf16899210e0a43ebdd3165cba85892db74ccf291bac70a6b2e5474c9547f076b3af2cae02bda513bcf9bccac351d84a471f39b9782f394029e67f19f6e663942243e89241fa395194e314bb5ddadfc0d349283e2249666b4de63dad90de285586c85e227b5a5f321e62b2ab7a003a2fb84de0889b8008231b348fb64571963b75ad866d8bd7a297cf56f51139efdfe2f5fa324aec3f07ecb25fe03629e5ca42ab03f5fd1a03c284f0f683fb3a2e2921cc8f28b6c1881142c707ec8eafc719d46164ff71d0eae195f34bf3ba6d801c2ac5c9a767259c08f0fe5a09d22c4f676b96e6ffe3e3293c22e4fa92dea986959512d0036c080e1fa2c669fd76e2776ee7b3c5283f5740b8b52d7b4df0f45d1d35b794ac86efb741d4452e68ccb728df64a9dd87dfc78a8931f7c0ef3bb7f11daee8b7ae11a98400ecb423830efd3d80c6c4c010ead60cb6292fa0b93d88eb587f52cf39fe4f0525d63e4102cc3b0592618bbcf78ab9f938f7d1c316a68a76c9053733fbd450903dca441b6a613286cb94c1c574f4408a406ffbfe6dee778b7ad60d2d60c1b3d13e185c33a646a3f5fc12e48ab12dce34f948159c10dd989d9d3e07eb4c7e4dfc6920d77c835e4afc80d1914f2e4cc2092f73aa5b6d5c3cb70de340eabaab07907207936826aeb44f3b0bd8c4aba3975564371b81ac569b10033ff7d3d374a2fc057db4312c9835dbf9c26c0c5ab734e9505f1c47ca0820eb8b99cb55a3c2e0df10f5b16d6bba650db037ce15b1b73d762d43121d3e01be97b876463c6711a44330818fb74052da5c190851503e72b138f1b517fa319c416d7d1b6f9ddb59f071440c5b391184cf83d635cdcd8921d2538587a75465eabad2b667f4b90bd12e9927dd4f521a87cbec31d1a5f44fde190241e80d71874c02d12d682cf24b5c408813fa002883b5d1d2b412fff59484e68cd119466d5b84d56b2f74953b718686941bae49a7e55cb79808bcfe11711079460effc26cb6a01975f4df1346e8191d892858e3313fb5ebf09c26a915eb781367ef6b6d8eddca711af90523aff33604672931d134529fea1454e091398672e1202b1af8f91e0c79a162a7e2d301aa622860048f560a801aa70609163fc8113efc428dc61b8e2fd8f9e659fc789bfbc019648a37a50b5a87643f693ebfb49524bf288633acff16bcbfb3e2a815c2a677e22a6d5119f279324a0ee34a44c87d1b9565cb2f67a81026a63442f9b5ae5975b9442738db074cccafdfdf62fd8e4fb2686743ce4272f61c35bf7708f3585f102248d44b8ba96df4b13ef56dd1a8fc62db28da844551684a2991caccf6c51c7b057341afc92d03bfbc58183b5e43ebd1efb2794f9610d966a87515b7ecb895ba99005c63040c32ebc23ef6e79f57fd84d8e624eee2bd84f51360a96b7d0f8087aa00cc5f8ffae2779c5f6e9b5fb1bd3be15b0ddd8fe8fcf704c654a7f7f8c7593f1bf9655d53054716bfab105a10e6bb87c02199f64d0ef9f1c3caba79cea4bcc61ded97b494f730215c2629ff6b54dd2eee21bb6f2e19a17936c3ba9435fcb9783db79a0cb24b16a3f56e21114297e97ac347c9499f1900826b3c3fddadd356fc13394d9f4fd6aa66314b8b19756a24a479b61d1f5a94403dfc68711f8e150083712867fd846f2a8bf604d6b2927c951452b5673b310eac22238096a72a29751c093df6a0613a146599f71d2ad95cc5b7524c28918c041893dedff43298b9a9192baa9ace9b50cd906a0b73fe594bb774068c83840e10771682bbc593ba16621a1e0e360f62166f3b887aed65bb00a688bc74447341422cecff65f0711d796d26519628dc3ee081cadd37744fac6de7f39a4ec105a31c17e03da9fbbd4752762d1c167a81a902bbb4c21480dfdb5052a8192041de67ba67cb850daf043989967c36eebb737836afe48c9f7f5ff41d07b7c56e61e26a5b65f6ca8f7c56b990b6fa3f79174f0f0127ca088abf12037576cdd5bd5903e60d1d3ab46748539a13ed8328980a0956a06161062a8d35b3796eaa580f4420d7bc7893ae930b82a283c4f865aba48032fbfad65453643e57cb06b6a95bcf416853107a42c71c285ade0b991d49eef98f87030a2a7487fab744d8071e6c17654c2def85e1d9f9d705ef0e4c620ac41f5bd5e9170cfb3cdca9ae62f3a80944d5777d4bbdadeb44550b43f8fb654ce8ff20aa9fac8488d55b4909137257d17e8adada5b6387ded1832e7d35263b97555f0c9c98e463608ae1ca9d831b40cb5534c8e6f322660a4e7e89f839c20d5dea74bca016653308ddbfee37532c86b4a385b61472832e5707acb7c24d82096cb8903dfb197cf80b9a8a69a0af7c62aa67f0719e9900bb3224bcfad62faa1f4dac3738c4a9e7bfb3154c805a208e34784cd88a1508c139e3a411088f2d88019a45eeb750903e35293853ec318c69c6caa1c15873f89a89b03c5a9ec9ed5602a9a8505a28cda6e989874c7cdb658f92859686e313e35be8f7a430da1fe0220ce074cc01717c03e3f436e73afd73f32208a200a234ab3c7655ca94d9c1370f035805aac7421f59d36bd25d8ac30cafa1c9523cba650d2c7f2713d4e1225cf20bae6eb1c81687bfd52b9ac7e01a5baaae34153fe67d237a2c821bfa26b359012811e050a98c127935ceb8f5dbec0ea2f0b24c232983b328ddebe63f81591bb21595488898d325b8926dde6daa9ac82d4e2fc2b5462cd6f6bb6038613004b0dd3d72d19b29f11a770117ff13d503fc8c4b7323d3ad6bab4a2c259287c6b64966733ca0ecdf6714a0f42f04d1ee199141aee2f561c4ed72ec5222de1d387d8a0a320e22d5c31b72f66c72cbc845007a99f9d944182d425158ca57312416be04fd6d503489e6d9038dd923e9497092c23ab74e19d7ecc633be179f3a33d157e5df39ced371f52ecc7fdb28faf7072dff811a67a9e2db9db6c6fec2840c29a4f3c00bc85890f5a4946d152a5a33b4410fc68aa30f5946ac7e9932746c30bd79670634dada510300fa68e797e6668594527653813dc33944f80d64abf49c02f26b6c204fbfacfd947ff782e7fbec9d4710031ac8bb8ad02570dda06afb83d1ef2dba2bd6b397d73e97a6f342ffe9b1761eaa33cdf07371bfab72136f33822fa5b3204eddb5623159d7838fb790f6db9664b91c08f183c9560ead853db8336da4327f27f8ba8dcca36ba440db1007aa1c54e1b73fafe6b1c3e2f7fd1a01c693718455684b5d69b467c76e26827a6b477794e55488302bd44d3767f5c1765c79ccdae7195c010b96bc9ad725310a8432ab11766c1b383a88f2800d1897dbd53dadfbb87370f6fd733b25af1ab5fee0c5358bb1ecc1e58b46286a4262fda0ce706071e84cb94431076047b39456c074d530bfcba1c86b68f72fb6b45fdff5767d9e966d3abed234181ee49eac625c5c8ac6d0498cae1e76b3344c74e809c2ab89bccef82adc4017a742b561e3c2f3482306c3d23ef6123cf19805279d112340d27034662c08eb9c9874f762dc7a96ffa7a254347b1e72e8e1fa53f08b1317c5856f6e2c61692a4f41f613b76aec8e7ba21b89a3b45e6fbead0f95c05efb4b9124c40a933b7a955a97aeef00d6a28d4a51a845acab49c373ba4ad253669b188922f976373e3b7df375c476e5d19cac663be047228d8d526c13d0f2ba122c2f39aecaf0b545c1830f19cd324c38fc9787ef1fed18ea48a62b29b18fe34a2e7c17bd03537b072b831d7f94b99769954de76a9f4ccbe509a0b5a7096e7f1b2f795a1dfe5a123b55132327030ec9c88cf5af90c07be0f9993af9cf92015adf5aea1097559d1e4fe7212a0c45da3e3ed9e107662aa98f6a9b82e6b2f01892579d0eec68eaa0ad61d48770d3b8181521528d8a114d697a09491c0fde2a8160c2266eee57055a8007a23977f13b93d7e35a3cd16b562370cd06b7cd8d1f6fcc451fe121afb09dbd38f20b56a6601d5bf3c21230db18dca70ff4c113a7f1e228801921651a43c52b07f2a5d08afe681cda236370576030eb55b421ad115e940380df14c303ecd0ba5b831897cf62c409dbf1bd5bd53974e9d8ac40e323b70c09ec857596fe80806f661aeb5af6474e13a14685f2a7f72ea433c81d36eb1354ea9dd33ba480c075171186aa1ce59c4de6b778e14acd40aae7f5e4f0f434c11fefc5ea3b4cd9a8ac0c41753307c7da25d4837e006d190c553dc965ce70d7d5a6ba91430ae10c380ebc37fdf1ab7602cc448465fa9feee744740422c7bbbc88b49e2377cc2e61365c5240b2d1bab3cfa012258dc484e4f161c8f28dea0043741c6be79bdb6d3eede683206421a877447ab8469054bcaad2522198f9a2a33259211179b4b8d29113b27426a744924c7077f9e6b3f7abc626f22c90f6fc0ed70404c8063e7044b64dbab84da332da5643b855fe5ee1938a44de44def788c3391e17684ea70963551f843015c2b0d1a5c0c92a0f95fa96d72cd368be964e81ab42660f089bc31c1a107de861b1fd0020619f8e4b1f300a97dd40c6467dd058aa62d5c5cc75b8cc85adb8e95120fc85a74f936b6b84c813b820bacdb9ed01cea5136129153c2f15028228c44ad0cd168d2aaaeff7a1db646912c78e737ed1ac96452f1633a96c7bb075bda064387defbf6d586b2baa4f91a93795994faa7bd06556818d729650bf9f4d10d81a8ef3c41ddba8df9a7de59e4711fe681808fbf67e218192b83f337ff6fe5446137368f4ede04ca6f044868d3ed31e60299435d57d8e83b89c78ddc9eecc7e0f3d7c84d00cc89c73bd210369b2a06d43f4da0ceb6d6abac9807234160746850c12a5b2c6be76e59d18e4d300abde64ca01c24c25c1403c37332c811703427351455a2ee5750583ab53ea6f92c31c61a507067db48bec12ff077f0815b6c6f2e32c5aff10b2257dc10e1eed7468b36466ed53e6497fa21c006a96db4d1ae9fec4475029f1ac5127d7c5abb1cda7d66d41d45b03f3798aff0949840f37fa4aacbb386676370750a3560045d47ba68d5abace46e3a1a389f20c9b9fd7c7eac30c935ff4a08cb438c475d8ab7c748d68b9039a2e731d5dde0c1712a7997babe73a865311f7f3fb90fdb40147826242db865d9103a25ba422049ec65eac5bf590a77d512c428ee524ecf9a0b73924c3ff6326a7dc35bb15b7f51a87f0c4dae5d433863e7c337c9c4863777117d344c1487ac723750c5ea313b978ee5391925eb3544521e8460636de76689ddf96018d508987275289aa87f9b8975e096c906a967c5a371dffc299a7e83e08583cc36cc403b146c724c38f01fd07ab26d4e83e8e2ec71e847688c67a331ac4d2474053246ccebbc91e0471e212cb71d555b567bfb8e657c9d1bf3b5a97da49f20df09fc9c78d1ab3fe150eec5e70c1e95ee97b8a365f0648252476dbc35a795ddee66853c99ac43faa126c18d19486e051aaf17f3c5e55c02d75263d31c608637c5765fb081e96d81fde4e5825b90ed7901bf36b8ab84c678d87857228c156720c43976216761edd3615566d154cda7b38da9d01b1616b339364076414f759bdb2d382f5d720b51e9a77982998a28c01eabb248356403b50b26f6718e86350713c0dbca94aa0063b6583479ec6fcb1831d831cb9b74d37b559379f7b4cb02e42d1328f389d16610ca765f5dadac4c8905c9dabc183416fc00d1d70127f95b7584253b5b2f66c6bf63e637105314b6a1fd3f2df7ce513658dcf48fb47cd55fb9adc5496f755e020750290810b06cd002625c36888aaef4251d0737e0ec21e4e99110500c27ca4e586bbb5a07fc1f32f8975b81d948fca4608f64dc3b3753fc0330faa1e5d22ae3e634a51fdba18dae63e59fa0744c2465924a76cf58d1d9ccd6b176d4bca0579f31d181651670790a0a0abe9d58d5ada23deab692f2f23f0b9f9323daf49c56db1348085e8ad0a3b1a645f0a7225691f456d7478a577b5893631f8808ba0d28c731458fb69bea41bd25ab31d0529a8ff2aa40eb147f5a20434a798ec412560413911a27265f5b395642dd13306a7f3006f86bd3c21456de9201f0cd6197b13c55b197779e316e8d1ffaaa390b0ba2fab3721c19133082156cbfd772d2bb463c6a7d239fe12809795a5e0da774bafcdd9597448e6e7d6175ac65b2a62aa2b3dea7c17755e3e18985b4de049e17d70b83004ccac5992e54fc0a4cd8c5a1e86a67ae65e36d7faa86b4d7268b0cdd88c01154e3a8808de5e58adfac280231fc938a0475768aab1b7c47eeb15fccaece252e52ba980148a2f2294a8565338dfad6e097e241e4889c14f13785b392663e1247e71cbe3fd0fb9665c91dc64bc791106abeb41efeaf0af9373a76fe7da5c7073b23f212bb62993944c36c31c72b7a2d3281a03e7b995046e34e080aed9765ad89956c413ebb5387e21daf34c7a2df23d4d9894fc6bbbc2aa4cd4e6b9fe081704b12c812d12a2f0df9c1ae18e5028b7a1079a9f0c36d222716902e6c6450c642c6f0ae236314486ee441d426f5f6ba701d062607a0d77a04730eb5307b52f6bd3fa3d1d4490c021fd52ba768beb41e5c609ef94cb93882326d481bc00adfdebfb6e402535af9bfd224c5842979963bad09ef8919fa6d722fd9fc427ae0b840d075826c98b876b36fdc247fbe248a9ce58fe539c382f7e8e46ae239615768d3611d3638ada4960e2fffe876890bb43be62238d0816fbb5b3962f19101b2f027a81360d8eb216ef047e1dd6f5430d95a559ac7eb77c0c24b11b7b4ec8df69a9d1420958854de184a5d1d5dca1c4610eb93463d71757b5377bfbeae2d29565cec61da53859d87e58bf7765a4d7a35d1de93ddcb65af9886db8c2214072b2881d1f81a16013f41d02cf6905ba5c43693458b8b567a515fcbaf0867ef473d26231361f6002cc677596185bb137d9d876b3aa6ec471946c322ac1ed63c44bf56d8375a49b0fea0a0d6d6d674a172637c95775383229a51de05791a1054773b0febcfcea002b24fa1fe539788b20133aeb36254ba6969a87d63f0c9e77bf5023336ef9f5ff4521b926ad2bba775602821e0c649b9d504ee33212789bc00e2f0acae15636512af5700b82e0c97dd57b073ee0caaeea8c94b1f1c1c5d4814f07ec2ce24115d4433e5586da413245d981661657063f7c3fe4a4f904391355b50ef340f2244e4c420ff6d746fdc0ce31f626ab8c9cc356b796f6e7e902fd2ad3476412ef6a8e87379274815487724a49edade24a700fdb15062dedb98d63478eb83dc4ef5f51350c45dd97c505b13c2424440132ba30d3ecacffcab550ffba70789fe07f5d6fed0cd6fa8ebeffc1c0e67f2cecca6b563ead1590ba914d48475a41033dfcc9eb5c22593c45dad6afb87d98069dc8f43c8b870433c218631031d8e2a0274def7ed73af205823d892e0161742fecbd0602c272016074b618659dd2316c298280506b278317161d2fadaa4a3fa191d1ed877aa4b2be552c28056e0b3654cbf4429e6241d47b3c1b1b8653e64f5aa5bb08d31d3a4f653c86363532dfb7a96ed9b9f8ee5f0a49bea7f377ffc48ac1353e4b0d8893e587694eb0ab24e6cf2df15ec369d45b937d35acee3d0dcfe186d8b5a9c3737670b47fe0c10dfda54ff307e0d6ff1419c2393b66b39328637255d64ea2889c2c24d9d17883b4cce0644a4ed127e24619c2382c61e84054a225087b6c433dc720b8f652cfb42cfaff59c5358e9e642660d703247b6f0815ff1f7631afde104aaedcb3e6c32c75000b9c6389b86308e4539d1c1a8f51922d45d2eff0c0d741195cb6d302f89e7ffcf51ec40745e087c11842396cc4118c832216e03da89611e57818c1067e361db5c0f63b07c34c19f81aa48dc7b3b1040732aee24f55433f482285307a92b4777364b7cfad16959f9137d9a9e6f47f4acbe66ff21dd0746e9af6dd06b3b795793673e6ffba82c5399c506bf647a0acae74285408a1e22f8881ba80edf9c3a619ab727299265417d8b82afd90e87f45c5fdf5a7ffd807ca84c13ad2b2c6bb3714d1625f95c7aa336d8a52a6d00978955616fb794eebd5acf19a0fdaf568c757c68e61a606e637e012b03a48cf57313d0ba9002d1e3e24ee82ec73fa56f34fd9e876587632d843707618702cce7cf34244e6eaf59cb62bf279e3046744aa308d748650d0d25c5171308c5133a438ede787ac3f202fc21f3f8c387bb2873ce2a9ee8ef97b5a3e0a734dec07a85cef5efe9788c3cf582d1147f1609070198e5055d27dc19bc4477df3a56b02355e457ee02c2711050022b759903106cc688c675a34a74e018588651ce4071d386ebded65fe9fc66b5a69037a68143413f0e8300826c518fd85f415942a95097af87bd3664ce61274db5340a14ec750b8df771dc2cd3ea0c989a35be4d3103b168c8bce625b9bec15b44ffdb2875b1f0f59728210044e771eb8bd2229768e4d88fab097c0dde01cfdd98e08c8c23618463c374b60e083214cc0412d35ef30358f462cedb30fbafac5829bbeeeb8e4a0b5a97fcc9a246b45414502b47785f700633e9e22363d548ddb7646be22188893f6a6a0ab4c1a09695b14e44aec8d0549d9821cc49231b190f4c86fee18471fa848b48bd5ae08697ac0513ad3e7e7bb542a8facbaa0fee6d380830c179093fd323984ec855c4be77e11e38e1a1dd0f05618b137ae6ed4726d22ef5575a5ec601391eefa9d92a2c8f3c541d9351a87350dabc1c38dba67a8342ede71eb79d7a2ba8a1de4f40643ae4783152e24f66be539ad5ce5e758e12d48410ecbaebde5e02004bd9cb0afcc3e1a8e1ce729c08e264755da0a32d6e4b0caf748899c3a93e8047bb52fc3ba977386131b9c0844b69900827804222ed7a9e0147dd1bde3487d32d11d433e9dd60b8a0ec453554ea258392e7b5296334a37d763e98d3a1b12d68e3560d08de7f06929b1cf9da0b07022434839920a9ff3599a58e62d75261ad29519ec5a66c20d0aaf0db89da894d9c214840673708d26ef99f0e1a50711417d5418d3425597f2a747018df5765f95f96207b612572ae9d9175475f2c41a4eceba6af4896e5446476156441e2a3109a0567373262e2aef4e91a69d4bdd57e13fd22675573e94157b715e72513ef9d2371295b07cb7524bcb4f3441dee7dd0aa806dcd2d4dfcb66d363669218bd114edf7139f81e5e97e5ea7502adb57d152f195e693d3679402b1e2707d49fc240f5d63033a67b2b7429cbe31ab87dac994489523dd0ec1062b848b5b9d4b3da8dae5f1bec4562dbdc1375fd8685b8c5f594544aa0abbe1c79acf234dca43eabc92f251a86b48fccf0e3976d09bc797da87e5fd5be340a2181cb278a713ab1e64ca6a13b9d4f56f335c853daea8b187635eb294c7e5d46693a20f0684dc6c2bce5e7754010eb55f5dd7f522174b0668ebbb9872eb2cd1960af651aeeaf0eb39f21f7dd1bb44512d4876484217a6c1997d7df31e94218b1fffe7c607220b9d3842576710409217a1ad9a44851ace8e4f2ec5dd6274d90c5f9e486ff1e97a23b03b772921fdf36c0e696053a4fcf48f1ce237c2d17c61387978ebf2cc8180e102d37bc8d3a3b9ca58f07542fadea9accc0f9625bb68613f7837fc145340a4d82f8e5bca917186004e29dd430cc2e7444838b268bfa0c0d582e8183351b3ae65b6915d1cf27ad50f902144130066738e82be217077a675199657b15f16673faed4864159b73455b36f53c50810be2a261cfd402ae25ee9b303504f58b5bcce553f9373072128f12d2eafd6755560f695b2a9770db24cca5c8636699a34781cfccbb2cef8d2639580919bcd1a7e39ffb454ae4d239d7ec9cbe20d9618a24c592aa79d4314443119f7e14873aaef2ad83d00c23d34437a762a65935bf147edbb9011e778dd50763aaad280b48ea987ce3100f62f54ca6bb2755414ac5d835e17ce046ec0e032e76d6dcbc4801459b9728716c56352f5aae6b94ecf055ab4fbc57c5c60fc71672273f1b9ce29e6167d8b8d0d7cb309a74222526d2cd17af9b9fcf8cc5df9f8307db51419f1b76ec7a580c93d3b1016507de21dd7318437ef9841193010c64e40f986a5c8e5d68f9451852b95b78399363f33e5856f01929fa40b811d3cd479e25db6a9abf049a67f349500f912f6ca6f8bc5c78d9f02e24f99e615198b51dc817b7a22abf4590320b21a1534b2bbdc4417132362e4b1c66b10c1fbdac8ee00c48d865825fdb6eaac71129acdee7a3d3448e4cc0d8a73047d298c3258b8a0c6e179178c7c0ab84a8c13c7674ce6048093197678a60e69e06dbb031ed18209852469c12354a13f6ed3703f2c9300a92207b8a623cd3b8a10b93899c39459b07c13d623186bc1aca689ee1b363792623756a7ef2123b1850624fc452ac3e68bc4247142eb81c3577c0a94df512b4367b5fdcf414d3e6a3d7e0c6a25f4f7ef34f945bb74cf6a9ab02f01873ca84db3027eb0e460a4dd23e21032a4e4727981143f54b2501b5ab436ef1822b826663f98d0e43fcd680606b3516aa03eca2b55e37382fc7a6a86447494dab49f04690e35ead0d938cafaadd515d0086934b6efda33b07b9a6199e7bc71b9971f96fe37e6e4e6626ab2d23d4a98804635a2c543da708138ee9d6cab4bc5d8254bc05329592b5369f2a43205e70947d0ff26e4c426e3a0d6f235b5a3c021f3f8c3bf2845772773e4cc47caee26fe09be88acd6f3afcba46d8a209565be0b5cf155670b2df4d8c05a103f7be75c13d0b54866324de9dba1883f770fd3f81015bf16c3fb5225669d251ac83c35254bf1c12e8b06cb0818914658d7053c8335e1698303836aab29e2fded253e19123079b07eab413c2b0d5272d34b83f4dc1307dde29c713b37f52028e6dc468167187636afbc47560eea947d682f77785d662ca3a6391840405c3e2ce21a290c05216ab45d5114dc4879107a02cf0bffb033e6aaece30316752a01acca143fda62cfd7ce11e9f86d0366c117e3fb03ea72c31c3892f84e0528bb940d2460aba0b82ed430c723502cf3d3a195bd185c8ae023a771552fd1be05d34a73dc202221b14a40ad6bd08ecc3f2d367dbdfbea2deb3c3a6b60bb63f07c6e556c492e5eb6ea0afdda683b4d7154ffd3cf921126a58f3146fa8cbd7dc634bfc7c3ee417792a0b8dd8613d21aea607cf211681584cc8eb6d5adc3c232c93c2ff8880f2a5dc29e05e69189ebd176e1f7446f31f9c46364aebae1aa6b03749eb88d91e2cf22e55b608604d4d216bc708dd6b75d7607c81ebd3bfe7fc0609ea9cf9d90fc3d4ef68056e43f54ba2c254e1cf4f4534621348c8e688ecc5a29c5b52a52c5dcfea33aeea73ceda80df9347ee9d0217be5d0ecb7c41c41859e3e2be6a5c999f3363e43a2e9f5f5599f4977a886b1c92d6862b4972e3c29bbd5563af4d1aeface43c7949fe3493247203445bc07b44b28707267c1950b90f897bfc38c50baad75fbfd84f4dd91f87ee47ba75bf514a464943b0e3ac88b910d65ebd9cf3b7eec568ef0610741fdfdf037d3417d836015ea6fcaecb0f2297855ba5ab6e8c2767a5190fbba4578c28b1dbafa16d81a24a583e2cf4a28a0af3564459512a336b54c8de2a58af650ab709cafe48eb0b51319f9fbbff3b9e48ecec71e3060e0ba05163fbcd7529f4479958c628ea8de06f42e34fa83fe8d387fe14915d2cbe6a5db69d50aaa5c6f9a78b00cebe88b8da6b407eb71b214b9f3445d752f2032bab4829d74d0414ae2a858da91457c3e35e124c3fb99afc9b417a2123e4892fa327839cbbdcb4a06ecfbdd619fdabf40a7ef545afafad10308475c53804caa3006f5618c2d519b6bc989744c25b889d81b16126423b9a8767d10eb3f36a797330f2078d47d641ff7bd5eb426c871c1645c2842fe8a97e50736640ef20d0d7ec5809d8c5214138d46cf6213e65017af8a7b4511a99c9e1b4cdaf2b67867adea7188175de91be80959ce3f6ae0f96d12cda470057065829b58201e5fabcecbb81426abaaf7bed10e289cc15083a05e1b7ec6fa7592240e0c81459dde5ab81d4b01c000fdd318b60be42f95595ad906020e4ad805295db7891f71aa53e6563ff0dc0a4a2db801b170ebb6e91ecb6f5a9d8c1de25687ef826390367d081ed18f8e6b74509c8a22a5e5b56b228a537e506d87183ce3efd075bf54ee4302de20c31b63a02de125dd903ae9882d39a7dd317262167eb2b403e0d217747f799b08b4c89a2f50e23a39eab026ff45e5a902c18e44aac2a2d9317de732c750ed7f00bcd0db591c384042d96ed1a6cd42d796dbc9b6b1f790b48288abc1fff571f4aa7aeba7d0778868f04f1bea832187644b8216382f214381b12af447efc5dca0d08e38770afe6f312a4e112a5afb65db069a2963468cf5e1b1328cb1dc7ae3524086b43c04a1632bb9d59c48c84c5c9123a3d92baa07a21efc4c50332e5f039510d34e60805de6f17631c47fdfc7edd057e80e389d7496fae5e327c91c8f5b56252bcc85d8e0cbd6f403dce6978ed714516a4971d4f9756ca3d016f37e4a4fb107096c448694e6f364a38cdf9de26df77619acfae994f46ee25e81bd89d6c9e7449105dfc4935451e65c150837212f50d400feaed348650b5ac01fff230c748fa7c5b603b6c1697f0e1496a1a7bbedb51189b1af87f0e1be889c90c697e0b214509bedcf1e767fe491bfcd8f33328598fa4f5ddafd5a82b4406eed58ec8a9d5bf56cc68864462da86345154b7e1655e71d7a47305abe86b37be394072ac22f13dcf78344ebb240a1c77f97c88113ef85bc8587e1dcc984eaf37e189620b99c0452e32a8a3271953ce9285b34fcaeb3f75a83a891c2ba30fec3daec963aad5e332929b772e68872cb320b21823c63288c31dd322b912cc2174e769db3b01f70808925275a849fdda0d98f37dfa37d6af2d5d71465e4978550471dbed08fb447320aaaee187b0dc04b900af2207eb90ec01efb5d5d2365272392a52843dfe4cd469a4b8f9871cd1c9314f053b807c563b07a00b72f00d480ac9d4a72399ff33fdb4f2da5fee15c54b994f968519b0f3e679e8a5c9e32aa518c49f91241c37ae1fa50d7dcde15b57fda202ff92cf0d3231116b51be06db2e46fe789cfe5a1a35c4f51746c3afa680ffe5a83434026d31f890b3c1763b94d543c3396590344c7262302c2a78a5d0c0621155672a549167dc0acd2a099e97832d83220dc4c31b9d4b5de7b991b2d67be8db5c2c2b66dd40fb411669a680c2493fa6567932c4f22804cb8bf83cd4962d7e2e82d12f59e2fa0c7d834cd9234b4e076e85e86a08448cf93d45ea901016cdd762b2ac435f06a99bc782d7e64384d9313141b37e0be4cf5a4e4c5613c28937953a53a1f486b42a13a90457f0012fcc0473a36d3165b7827f5c17e4b121b5eca13768d28855759eb433fb62c7f668750e3210c81c326ed285bb5b34fbeb6d02b6e5846a07e8279f744fd3507a1fbd672df22601bcbe4d29986394135bd139213ab3262d890535961b6c333761a0a4a00230e5168444d296eea02b172f6530b551c0b60113e28d0041ce1b9da7413eaf0a91c59814658c34f34cd760ac1445345400ddf90898f394f411a458283cb1fe1ea2216550785733e9803706a480cc29964609cd8791e3fbbd56811978b21829101804bdf097e9b799a72cb1122ba94c889e07f4d6e9abfface02073a59e5123698dd0e7604282e3f6686bcda7143a36ff49e944b3a2240eb770d64b1cc64bdd6dd3cdf2f2321474c8409e7b4e491448d010c49c23691bafa67a6fd80b5101ab225fc50acd13e29c7da3eb7885857c925cbc6fcef289e5600573cea9b71e82a965725be4f4a255da73677fa060dedcc3a4924bb3ddc5ae93d256e2bd465de95937e5f1a79c1a29b0e011fdc33dfc91420b31408c4e97495bc81b8a7522adfbaa1675ca2f1f5d6add5018bc6788145b18b73440048a1a383462b6460bfa4898c456519d0e8f98171a2fb7b0a424cc13c1b71dc9c500a45e9cdf23262d28384c0856b574b4705297d2a23535d652e365439fe1deabbcb9e1b7cc67f75669d0ff2664006be7a52bcae6c17c9aba28717402578f879567825c9ee8178f2dc04234ed4d31762aaf56969e0eecc0f64c582c4030aae753960722eecd71de0d55771ebb3d61b7cfae755264c6a3043765108fc9d6474aa415c7abeae4f4952edbf2ee10372a130a5b1282bc9d73e299d48168cec5c0002a1038ffb2d705680a17044a0051f216de7faf70cf3ca7ed2b4832a6d11216683501021a1545f7e268cdbfde654c5181349851e331aa1e4e5f88724b00e9a5a1dbfb1e1cd122c080fccb058b6222268cffa2e0867460263797c39c1dd4281e9b9cb57e61efaeafa00c5d595a074d6481edc09d1894b37d190881f12418380e11d9cbb209e7e1be89aa921bfa7b1464b24ce1101c1f002ea8d5e7a5c5c4352fbdacf89499febb2d41d6ea4795e50796aa94e4e7d5384c3b11dd20ed39f45573287c97c346118e2b7227b7f7818c145879f03946593090c089369eb09aa5880048e3f0cde39a1ba5fd644a55662e48340359e8d89aeb912c5208f7a917f9dc3e33c2ecf5651cf0f842ec49cc450c236a13fe4092bf44a495138d12bedacb8285ac3de32fd1ad794dd6b42db7663a359b2af79d7e03e7a5b1326e319392bbcd631427351c6c48475ea17e903b3a4e2e00ff9c61bedb1efe764612994f6f19593565384596401eea109f4a64245a972cc5706bdec844da6df4f3499cca718422855a910576ffe1c7671553767c3dd10c9ed9307fd1995430c5fb630354ea96ac06cae18cd4be042c0378fbf9a4c4ce62de85f8206649dbe65c8d7abb6ba680480c848acc946e6a3949ef7198dfd68830923f4ebf7fb3ada2afc24303c9b34a0f90f1a5c6a9b46261118011db73dbbb0bdc14ed43cee04c5a9b689661f02017042f199f308841b6635ae90611696705229c5fc68ef3795629ed8770256bc422135c4cd503a0cefffa817caf5d60a23b43ad40c64df6ce42bf73f9496fe316b6cce503f4c8732cb6fd005a99fb9da9ad08e46031d1df57f86de6f8fae399a9eec1fa910d76f24716368e1feb73ce38010ebf93358c46ab019c20405bf24ef052786c8ee2ef36abb65ff61fe8dcd8631869df5ea1c73493d1157d52fc47af1f22cb5d47ef02519f73461f2b58faacd70751f52b3b21979579578bcbfc5def68b34789ae8b4044c84172c94927720402bd606dd5adb3322c650e4bae05d72b3e18438a96e136001affa5a83a4bc716108a4d4d8a7392c7e0aea7d6944f5f23c99cfa06062f5ebde4708d095027ff36ac0423d07e8251843bbd6047c03d14186e143e3c709a2930c146d5fe887c06a2d64e227530790f1c4c6d96b0bf9269a235cf93b214920e79a75fa9f396eacbb9a9b9373ae7a9e9cab256c2fcfb9660669a5551be4ab6e7feebf32e01e30a6397eab64e1f21e9b1ecb5e2f0a2a267f7068f68c7ad62863bfc7af500d4c8fcfe1f69140fe48f8e045dee8bb1447c115075bff6cc7eef8365767996ab3193553023faa7681f28623fd9f9af2b9eb86e14e317b903147f5892e9dfacc2a896849660b9c93883b98aafdbab9b66d60951f0909d1f823fd39cf0da3f6de2d007a9cb248a661dce3345bc948a19e0f08f020925cc60a70948d16e8a481b466a381884c7b7215443f848e91591bca8724a2627e6f9a97258cadd9f82a97ae135a89b850099cb02937be641e9c0cd8fe75afaea6411d1c8c06e23e4a12b38f12abc9f3a9ab9142a2aa67bc25a883b75853d91e48b7635919c9f4bcc092b634d16c16ee27a215e57d88fbc70503e6983b3833d27e9e15144a2d911db3e19ca5dcee36d3ac8624822fd491c71447ce61a8100b62a0503a64a9527157eead45c1637d8a2dcbc2bff23bbd83d1be58078f86266bfab6651f50376cde54137926fa5255b25e88dff0c2133426ea8d24c3433e53b5611450be28b6b3ed5e557fc505dd6be2b5311382d0dc2bf7ab9305ab3669e13c79b346029cf0b16bacdaa41560003ce7eaa0e41cf6358d1de64b03bd917eb77e6f8ae2d4edb72d5ad5827e06d1aaeff003c6e8d5e69c12e5dc243178fbab31e5c57ed029d98a2154548a62ee4d9c810b07705200f1a55cc56b6b8c82685045a2bba97969d9591adfa93f1898380671051f9008baf0c062db7c17bb1c484a18b9d3244dd6ec6d09bf696010595eebc7ca7d7e73ec441b5adbfa17743c83b61dc7e57817b07dccd2dba1b7ee1b8dbaf281345bbadca73cc08b4a83f972476708c20842bcbb4db41f620170caba1bc118f761b2eaf4e1ebcc88643826f20031883518c2b098e11f55054bbbe39faf359588d7b24dd6c5e58fcb3b36a5ea140304ccbf161e109935678f9f1cf49bbd896c9714437d2292b742b3deb96852ca9d8dd80aea9f336fb4bf8817a02ef1a0249643c15c32b8f1e7eef5113b96a022e24f43f8a004c54bfebaac514e207e1f1c2f5d4073f6d24381050283aba7c67b2914f6106ab7b2143914a5d8688e6408d4ecfbf551bf80018a9cc8118ada4b29e3ec7f95904a9679abdbee9be3d0c0671365a6b926e81d8391da928ddddab8b59a0ece2355f4f31ba2879fd5a27b6c2c30259f5004bdc721d2dff4ac9219a9701becc74ff5454a23879e9217b2c3cf4996aea6ce185919063186940b32da6f1efe21042f5ec6cb0a6beaef310182b830a300b931a383a6917b7396a9036e5d67580feb4be08c71fb371e0cadb75a1a91452ad1e148602f0232f0f39ae723eb7c21df971f25229d7c1dae104bdf72e36cafd16de5fdb94ad714b8241e29d1e3836d800e622b306c26512bb79ed82c48f42f8238b09b43c6e33e2262b5e458f06d4b3e982e19c931f697a5efa6ce0fbe59b46bfbf019f807d5fff4467648b9121bd3045b832e842bdd0706c24080bfbb33f9fe21cbe7474411b4d472ee9f7f2bce5f16df2ae0a439d957adb20b8bf30b1e2e79c8bbbcdea7e1510cb1d0e7447a31e285784dcfe121e06154c2c20a0d78119794f592b6778db2a820f236e77e1ddbcb3e4a74d765811ad8ac025092ea76ea38192c9c358910d6dd3e5ff5bec9d7149aba68a45afb01bfb2c0de7bc3730ed0cbeab951d7960ea1f4c15410817879c36bc7d8333fc5d7ae3bcd91b957b727826aec21a70b2f8400e929ca40976da0ce7514ae3e3fc5601bede3ba54118b9d2db9090f1545580b96b2c2e155293f8b2c106c0f7c8790e6ed12a459caa686faeefd487b124511d9b6547b8adcf6426f22031afd20967e1d8fd2704f1247faffb6fda4b34ce8ee184664f1a61b537fdf03d8539550bca28c6530cf25aa833fd395f59f065670b695027682cad4377d84feac5e386d3daf357fcf76cc3c72222b4ddda63547d52a18b1f3b7982285ef5998bf770d3a1827f2c59cfcfbdd3833cb1bff4459a0ab734622efb2435e3ecb5ef1a0b36ec197e792b5c745401ce337e5830fc6386962b934c316966ec6a5de1f96dc1544100911058522da840d120dce8ecda327642220e406746a20b260becef1268572ca36cabf88048da78662e7b85dce366f7852389713c533c53319b91671b804bd7f94196c824f641d3d7dd852048b840270a27efa31ffaf4b84a23165f5fccaab1184545427fa34ac64cd3795f565399a2eeeb69250862c32e390e8f56106196a2828a98573eb40d454dbe21f35b8811a24dfd5ca22f3f568920aed63370ae3c5946d3ef17af937b8e8ad850b9d81188ff8bb7cddd6e8db0138eeb59d6420f4761ada21fc63166952cdae1b557008b3600e2667a4651966679ae22812dc056770d50f4f437a341cbe409e3b4f9b26505fcba8de96a33c6867ab76fcb89ca46979ff9d0bcd1c4ba0165385b2715e04329de4d0c8851435a9bd7cd1168924ceba20258d14fe3e4115f80dc373c1c7a5865590c78158c017b7776df8e548088bfb6b2ed360411e29f4ab3ad6b6ed8bf93e3087da7d4b262ba84349486300655d7515e4b7a48630cf727a1abce8d668fe54e853fcaf8e9584949947c0f4e5db92ac7c4cbad7fd2d37591c282d63225b05cdc9bba50dda6211f713e8b94ebe3680b8d136dbd50a19370a9652250cc600fe050f3d3cc169401bddca08a508acd4facfb77022e91f175e5bb6ab6c79f5a56503bc43a41a4c01fbda883f2864babe44759731614dbd6b4e6d09e3372dd700754b2346148c3fe6d8903b911587ae53b7e9268bc0ec10f5c7959c1986b4396062ba2bda4ce52d76064ba8da62531a619e3de381adeec5973a3ac3bf283cbacd3c621c47d51cb445ab0b6a641fb009fc6ca71ce68c522b614e9b64fc296fa443c00b4a70d4a90f302b4c7bb45f3619888d6b6ef81724ddfbdfb4dc12726a7051ec1f94f9a83df9e4723745b612fa4a11cf046bf48b405d245aaea0c99fd270286288fa52c8dffec18cb8357bb731d050d101e56e312b7c403ac13964e3d9233ab9d86bd16f4edd86b7f41ace026b8b47b139b24f5b2174529cfce2644789c7bb7629437e3a073264a27551da93d71e5f47dd47ef4795ebffc8c2f8c08179a99d8d823e91c6122d82fa321954c94e36cce86710888e691bb857a979cf27ef861f7000a09215555246752b31e2c77b19725eff59fc8dab8036a986888a767effaa1fda588c55f53bc22fdc869e3b000f03a7daec319d78e605b6b64e543176886f1889c49ddf750172d29c2033019feb9226f9fcb8ab66e836715048cff5c21b93de0712dda2d688f10258cee063952c21ad0575c60e8e2955d8b19009f025fa70e5daf54b99ea0d003ea333af6166cea61553789ce3c3b550da5ae5f813ec84608030cd2e30863043ae414ef656eadbbc939e1af4b89f544b1a4633b48e4a2b234829c1a43e33f95219e2933a60a4fcb32b417237f09834ee844d9bcb06f836240b7fe300f021e574ad213e109ca08558a155d7af2de4d070a7b313837b8787f3d2ec1a04b6e1e495cc92e06f231aed4d71e462dc598b5945b873441aa1a4b4d29084954f644798a52a84b91cf607d503bd976787f38ed2434a13218be227acd06dc308d4c8e1c105dc4c4c708a2a7150bcdf6400ffd66e4f1c31b3283518ab44d010249951901856c52dcc252df6c03e1dfaab360b6f170b74445bc35eea5f5bbd1a3d588d8b866490337431376bf3195f43d9d74210ceee95dd8e03e665ea4b5eafe6d8f5c4a853f7cf6659ecfd840f522bbd8a4649f41a5e6b29cc6347a0e43b77326e54f3e884ca974a6129bd8eab341d0cc6a79b21d1229483dd57b15b3eae10eeb3b9d97720f5fa769682954dd2bc429abb822bf3bfe90ee37f57a6b9c81fb8f834b0eb22b02e881454696b91f1ba4511050880c748d425e7d872d0e3e4998b516bb84f73d51c49f42bcb820e05731288863b596f83006f8eaddd845c3922c330e05939e910877f82527d185c2634f7917522fc5ad1da16e575ea82506048596732ea901472aa26eef7d3c3f880a9eebc93a804fe2a79cece0aa18dffa8661a02aba78ba455d92ae7f58c8c32f5ae03090f394fea32a6df96c94a5b2a17cc8f08fca47ad676e1d20a065a0d531ea3796f97806ab38653595e25f3b97ddce5781dd1d842ffd0173f74515974b464a7816fc511a0d919301cd853805dc88f87a180b1537fa640d914b3cfccf705010e413dee1e8fe2f08fc0a4e5cee795015011100350b9a0212d0785b954c603a98ca15e5616d6504ffe13f2f0140414f9bd03acff9a22b62460dee481792096d18156b7d0314cfcd7fda841fdc69d004583f194c53acd408605678f2c385064e8e99e0c9d5437fa34a302bd8c093cbbf52baa43f799839d105d646691b9f0ab1b53dc29f53e81148b2fc1bcdf74f0d11e607a220e234296d34f79f12e81e2216f119340b7302115b06e11b5cdbea0cba681241636ad700ac0660ad343367ec29e7e95406fea1c247cf610e264c85af6c5907736969b62a064dc431a4f808f844910beb5216eb2940d819fef05a303d6d9194a1ef1a51b7912f86851cc9a202589449dc19bc42d43fe826c3c954a286d4496c03894bf8d0e21f193b8f698d35bad486bbec1cab471e8bfc290d4afc3f34e9842aaf71ce48296de050a8aa35d5e8824b4a69436fe99b650dfd36c1ca6c67a42c807575b731341c5f540aaa3df20d1e677b39d4d98c158011e0884f1e9c4c90820defffee70136a71ef880340025a529fc11ad413135b115d338af3dc4490297d8c9c115d1b402998614176f57450089cb4efb5703ef813f4a701b64060330cde8d438217369d0fdc4be279f05abf2cddd0fa10207fa2997353829caf0158b558b15778ca100d554e36a3ed8bcf201165190d48ff6240ce532b5391b43e8d259fa8010c152ebb27e83bd878b3350baadff342a4f756932de51959c0c3b0a4954b104fc183ec9067c9ad21f38678019698feec43caa03abbd75b758c93c65b69e363704916476033b49017f60d1d2c531936848f06cf98ea6ae1659da31339f007ebcd17c562aa3153e5249505fbaad2af7d9d23d0cc16bca4f0bcb1ca082a113348f1bcf8965b66b4bb3375d508d285802b29d43aee4984d4c6a05bfebcfd8b67b1757484364d334eab58e1a541da68aed141f56c6b007d12f8036c9afc2b2b8c8d316826e37015cd9b96de1bef6dff491e190a933b526c27bf2554169d1777d920d553f809c4523ad9d65b7b8cd6036c924104aafcb811bf909a0add6ab62f28238beabe35cc59292f50415c641b4c53426c2e52408bce94f08a4aadcffcd9d0c514ffd7a9c854c1735304980ad915eac87f35306b009073a223c825cf96c8c5633845227a5457798d51cd8c5e9e48cf01847f0c0ba4ad257ee2a6fb988326c81228ea06d58e544eb0e9738655b7f7bf106dc4c3e27ccbeaea2eb7202560a1162891029cfb4d18d027b77fddaecd4490be99a08a9f55174efa0f0442d8db2407fcadecf504d22bda57eeab34ef01c18cab6f1b6e20da9de437e331174d3f623419460c3445663ab705ff2fc8f0d914b63080e95285ed746e5a5c3cf30aef4760b8b116b10c0e4e6ac76ade2b1195e4a35e2ce28c5e7cbdda57407bf92f1b7dc2b1019a1f006857f02b19aa809c5e7de9418cff9a4d52b004fd833960b8e0c019f1d877ce71b4621274930cf0c565adb227a29485c39c72f6e7bd73070615256fcc86c320f9f36b60035988fbe8188da262e6b48af386c965b44c0a527388081d55b23cca86ab14a6d8ba92850e82d9773f97ac48fbf14db12fa45578e77f4cb37a3430ad7add116697aab79eb7fe6c21e40229bea0660f5f03dda12b32c2aba6d2ef1daf21356551bdc5a9372b456ab7ae37627651a4f8f19f48f34e1dc9e1a2aa2abc6e323f3792567aceb31cca295fe59b8eed102b1a36f140fb5dbd4b604569058fa122491612c64a60c4c6b529eb0025699bc96982d97082826c6688b931f66cc1771a3e642795ead89e0c2978cd7f420ca1af9d4d640d45a4b4c9dc6bfbda01b24e316f193491478f2ce15a562aa22d1e61dbdea8caa2bc5d707a7302b4e95fcd3f1bee6c57a182533a66c040a30ae79f690931649cfc566fa5bfcce99dc40a154da5530bcabb8e8c20bca34a5f1b156dda26930c9dad020d61ee600b7355e8474a980a43a6ab93ffb4144dacf60f16bd26d94c54d2aeee52f259136b049866dd4fca35115ee03878c2b683127533d55bc197c9cef646cedbd9ebb55ff5ae0efed2d990254438975c586c6c398cd056e884b4e3f4f0950ac26bf69d49e3cc10f573fe8c3a956c86379247b1d52e36c0ec20bacb328e92ca30b2c1c9f5604f68f57eacd518b421a228dd389164b6b1b1b2e2574fb77594017b1db5e7b7d4a2345d6f2ee36f17f4db0584e834d31be55e9560239feffe3e74b5e835c580c50aca768b4530179f2fa7b68f6aab19f16e7af98a50698e655baf62881a6c788815f71185e2043100b5c05b81da8e56587bf8f39e5ea6b186ed33773b8a64c1fd05c7d68c7157124e89a4a9aee811a1e4213ae62e230b6f76556768c1a223793daf416bc7a612623240c1556cfb85ab74f825b035f8e4df4f83c950d7a810ede15eaa69adf8b624dd2952578340c6b07caa413985652edd3aa835a9fdf4262b0c0003d2d1a2d833b773afd2bcf3b41982c2d012f1c37b52d8fdd8957da2be2643285f3e42ff0a0f7ef3522b622f910c3c23bb1bd3d926ac4d8f2c7c8a2488d92c596d41fc4d9e5e71d46c54a8ec21541bc024ae4bb8cc1d5f89ed9f3f5d3e04341a513684810d3ba4d7173f920ff92155761016c0badb351cd2c72d601a6cbedeee7eb23ffb8911323ddd6c66a3d230ae1c425be6894cca3346d4de5c149c2168ca0f22cdb8400f441d257da9cc33b25eb7a0d21781fc2e31de188a48aefa346afff8c0882d6616c6964ac6b8695da6932c4b93d511ab4e3a073fd02e1af02bcc38b19cf077d832659864fe85c60d97657d5acbc6f95adb98850d290d681cc28ead312f77b7f839a561a168a61109178e7fd1b7a88021c1d211d758b7737995f9bbfbdfa9758e40aa39c0c475d9d6e483ab807002b06536cd816ea50d4ae46590ca227b45c8d755d42a64e313b58e0ac134d6dbbd49042b1ab41b838f406f66311be29f181ab432e556e60e6b4f0a74b325261479883b3bcf89dff61f908af05afae9a07d747a1278ebfac1afed0e5f4d1e78a4d084986cd277c30c52031746b5cf7c235f45fb4d383877783372f0798fc9f6ae1463a5052f3b05a9978547448a5f5e0c0588502456d6c7a5f04d93e1916f3ee822f6587f66ce7625cb1a4ad6fbe7650d2a8f6810e1107bbb7e912263f2d667aa399e491eeace2bb741b7e297438d831f228c1721e6d02faa1844924d15949f78ae5105251ad6cf9c05000a1fd5af0038b8f48c27b4964dc39c7aab668d1be4ef059e3bc31f471d7a78ce8b4f0e7bc7f6694bb02cd47fd3edef1c3a3fab4fc75a94ffd304e50f0c31a20fb6743df978f0fe3c805e9ce521aaff0bd548cfe41b570e83cf2249a77e0b59cfd8d94de2a60db497c864ee56268af43ce5d4d12d6e6426ac1ff1a1b0c0f99907209666cbd972064de64cb8f76ba0b583cccc4b0cfbd1f655eeb933f38c30adcee7fdb312572d0abaab60343abd0c0cd0001d1ff9ad11dc5050545a56b9df675b29f526c11ec4a3c5995b6d4f9f8c6d35c4ead9776f767bcb2e11fee333db45e68ed5e6e1a638d8bfe448290ba5541615ca7bd588b151b14d7c4ec92de3c6975a5dce4c28783d7b81184ab2ddb303361fd23f976178790f5b89b286fa3d03a3c389048820946e4d9bb0265257887ae684210ee086ae911064ff145bc47cfbb5ac586a45d8723e4e466a2c906eaee3ff0ec5836f78aec4e97f3ff17878346b0342a898dcceca0625ea0d509181bf337b26f83c3635e2f09f250492b37d94a0786a7063b01a0070f41f5472e6174079c7f85ddfd62f2a2bfdb3b5914612fef1350690a92e68a3b89c0cb407524b590136b88ccdbee3aed224d483b14d246241825bf10ef56130e9f4823a5ed0bfbecdc3cd496e82a9d22f2a2061ba4625095bc09751e9a1c8b4f33d66a41ef4745dcbfb21edbd88f183e4e3d415ab1d6084ccd1e512d8d734807614c1df32244b78038c2cb7ad22304d9c6b32f6a6f7181307a3432587691c586cbfb5f75de890060c9e83487e5fb3a7982153c361513b9aac0f5a9a6f0db4b33ca49f42014d7e23a799dcbdc10f7b47ca29bdd3b5e8762c044da4a189b2ab9b70d385682053a0ab494af55301052cc949e8cb8138c00d1ef1b5dde7568285c7bf2649eb0283daff4b1a15e68cc0d93300d831fbb007d86e2497803cb0f8940204e948c613d5a75652e236410c9646a9a3648c92e86fde442804d6bb6e33b76eb48059c7225c97a265ed2cb91be4e3a5dc49e19eca73d5c45004d9c74dca913fce614bf05edd63dc166d902fb9b255088289d2694fd5339760815ce82e764702be9c74f424a4e0a8e1d35204bd1cc257ffd943123eb83ca4d134c4a8b854281f6bfca17a220bac26c8ac57bd79e0bcad72f5edf10de033272306b4a58c0d3151a9e4dcbe4a1590973ea9df63c64a5bbf52252230af3e16ec716682aa05c522b002da2ca64858cbf0802eb3a44b6d5c9146d57d97ac1ce8b6865bea1d3b295b6dafd0c12e2f25292db06e8444be80080ca740b7ba863ed6562eff9f7fde1f681244f6dda22f3e12ad7a902bb503c267b788039b48c675f88515abf1b7b4f30f5592a4a9ee22b1af3ef43a80a06fe1367dd4be80a7e857d510664adcbf3bf803e9da036de4600c0965e33b234b1950560b00224b339c8084db411b2813c0459ee02fa5c6ef2f4598ddc623fe9511a43833c8df17d4bffa765056f82c235b2b4f1a45a8f61d59cf00fb2c4745b77509fa453ac2d6386383c320f7241c72d6c18e3d184fd44e0cdf90712f80254068999478e181b498d5e4a1e504b4ef81157e18e7b7e81681495ee48ef1075d1a15f487277f93fe5afe22bf464a2b0e05731988e22ecae1ad4496bc2bd11217fc5bb84a9ada169c27038a3fd5d6aa0775157575980d08aced3bb5da3aa817a65b45e34b99a30633892c89cbc7aba2edee80e842b0ae803734604636b77399816a3f14630391ccbb9517f28eb81b04c5d3e907f3ba0044e8f6577b607e1b0b3197ef092b1cbb7c55833c1bd7485a57a24171ab61c4cd9dc7d417048c58ed6f1a266dfcbad02548f5ffd82f75a8464eed66cb3d9413bd2b675a2f1a765f17afb4c86d455f2214b70f62d19c3f2202e5e427e2d0d0261ff06f370335a3a2382a7f0365096bfe15deec0f765c8a5eec66098e6f8ee03f37c17d8354ae5fa2f4d3b541a2a5763a28c252354fcc595f9ae6f3520d317a4706f30b8c1b0bad3f72c0598934d149fc9c657411e3e1ae81da6d70de0cb6c1e4954391754f3bb902c93b44483614e7e53e273b129a61eb087dce4ac13a559f638df22f3813b352902625230deaba74ad42aa94c0f50fc5131c6a4789a8db0b2d306b2abe4b970e697260e1cb0256d849b8230d8d5ffd0ea49ee41400154cc1604ddb076b5835483fa1ca1f3fcc0c5ab2d4cf1cf5010f9dfe3bc8634c1e185c8e34ae14bf44313ca45c3004a2fe181737d3d20edad55d0d8b732dfc98a613584df27487214dbcf69f330be026f66ea3d30d3c30d6a92f96a4cd47c1bf074eb67034d976074b96e6dc1a3f570af35da67df34847af300113df74b0f8a5ce933ee33eeb500b543ea5b84e52ab5aa9e9d64a574c53ea5345647c2b9b6c33f3d0941b0ff47fcf31acfde18a438869167518cde1f837192f70c56093118d35b4302f1e4ac0b34335c5335cfcddd2463fc93005a1ef73efdb87d2bc15514ff4fc5e249dd9e98b8b29dba3e54496ba8955686119865a0b0686d21b074c235bf4205c4b3e008745a13187291c341f47985633a164063a847c7637622c6bee576a307e0e3a2f3c4438668441de4705a1740f395ef9fa4bdf8f413a27037bf39cc84f0dc6e0406e45d32e33352740e016411413718b9b1b2496ac78541962d7e109864eeb0c824751e92ac80f051e362a8fd05789a8e616f50ee541bb5196835f8487bdb1ebcc74c3b7633cbe4a179fa32b48faf811220b78ff216bad92c5dddcbb2f1a709183dd7cc20b1cfef095c82450b5cf88e58009064fd44c58e09e948ee2ec952fc128aada97544bbf253699dc1506ad3c373687c12ce3be66e9d51c5ec9735808b0664c88f94cbea079aa8b037ebf7fc4aef3fa63c5f18ab8170488984bd6fe90cbde7e9a2771733634f800f4a1d9e2bb63212de7092927c1c44042a448abf821886c4df51b2953ff9d435100ee185e4b9240d4b9223685a4b1cf1c08ba95c01411ae924ac94688d1878756c328d35c02002316655ceb78560e84d8703a617fa37bf72220640b776aadcdbe2f47fbad8768634a74602a747edc6b04071c87a71e8ec5c4ab6056c32086709a6976e9090e6653bdf5790f2421f2e894303f8a1794bd83197c9230183d02fb706df946e16b4d910a271092e2d86d7f50a35fef6a3b27faaedf3237ff6f6e8b5bfd518722771df15a519e8773c1978b11634d4a9cb799fe48b3bb0e51be8b79f32109574cf62f65a9618712cf973ce1202dca4faf806122f06d1d3b85e0ce81b60eab3484ba57c5cfdd29d3a42256014f832540f9e17dc77dd1ee58cbeea6e35a200e8b51563f766f498bb174014914f7bf1fb2576231c0ecfda42f418a1a263dcf43476acd9f87d118120fbaeba47092aa607c2ee70dac4f94233537c9aeda25e94f2025241c08abbdd9ba47b809c45d69a4080fb9d134ffbc492551a2da04c0a11d86b85415ffb42d043384e16224688ebe5c249aa443e356604aa14342dd5a4ef848ac8235ac9127452cf6acb90fc88144d00483fc0ef8f14940aed2d29ca7f5e6d0d8f61ea9160c2a1ef81f8d737506d35710388eb9eb208a3fc9f60061d9ac4b3cc0a4b3637bb3f65cabd6e691e24d384786ae517680111d627d46e885bde946093b5a2e71c5b8de42a4aff5b312aa4aedbac1da50ffab871640265772efc249c0641f36e9e2285fe95e60ad847e3cb2ad7dfdf6f2e55e2210bf032bd67856f4cedc4133a32bc9d91a1f828a3bc57e2455f0e0d261a9ded95590aa62f65804eda2341e99b5d313bfbd106ff491bdbcc24ffab4e4d74e4cde38d82bb5195b8131c6b8dc5b33981c624adf00eea6786e0386fa236caa3ebc3d082511348783427e6bb27aff07a2e924e4706cf6a156ff3436d3df59fde7c9923e372f37fcf53ce41a9626b714d16747e612d2f658b868a8f02725847e547b9c1c80d4827e4fadcfef6de2c413acc753ff1874404a20b37644ddf62f82973c6964458f4bb34a7bce6f00c6a2beed2a333acf614145ca9702592f80968aaec133205347b4215b42367c915f998e9ffcd3908f936b8b2ac8b0b4c549ef2b1c0c8d4f909f1908c4349f0b02f1779a7a252cf641c924ce411975eb0d5284eaf56b9c691da39d69afa4773e242dd9fb7fb54c3a5e91d6120adfda147808873e8aeea1239902f2c7356e617c34612abdd2f5ebb116e2ab552499950c9854f4041b02343121bb791a5380d6e7b8fbbfb28040b6af2893dfee18cdba4772aef86985c9896b3a7710e2dc5e52b05ede4063b88eb77a259a671436dafce09940dcb2196ea0711c0724b0f9204cd520479c4b03a8b2c5db5e1b3d035180b2d9e8e52c549571628a75e79ceac14b45f23790b5cb983501217111f9d95c08e7fa8a5470df1dd2726d8798530e05b343e1d65dd69bfcaa8eb800aaf6acc68e49c2bfabf7255e9d3d6b008317c6546512fcf5aa266e296138c1107565faf63d4f7091c6d02d06be048a0dcbd0cd63ce3b29a864f95a7ecea4cb47740db472f9092ca2929f14642c561b3df44310be54a4ca4d8c25c9884552735f0feb0543fabdd2efe87c6634b50323a480aa9c321cdee1bf516fa26919bb1f32ef70e26b9ae8d7d6f15a0583445437d07dbc063e3b5e16487868306ad0e23e390c8417851460253519c222cab650341282149519606aafd0469a1ec5f8df91d8fa9145b6777142c74beff076d820bd2e1f35f697052c5bdb75b931052f40173deb0711a723000316a8d15d7cd36afebb3c53b57ae0b35510a1b67d2a8d80a57813988aeb7106643b0bd721490b6efb3c492f744d8fb2a3d887d07cd31421d41cc774a334938518719f1a37829d10d6bdbdfeb1c3dea660725ca32745c635a1d5d4117a454897431e6fe375d9a9029e6988aa93f2e435f89afab4f730b851a2a7bdc4db09c707a66bee7a1949761833c38e6ab0c4b62517faccd9dda4b96973790d1b3774c3e2c3797e33fdf344468c0517feb3a52805d114152f0525b78e1c7ae56e7b32d592de05e293fd2f9f6cf0526ca7b31a05a650f1beac619f2aea424fd8c488968cd92b42e753f0cdd73fc35c69192aa2b29233b2d5fd2bafba72bef45e1a8a8c63d809542cf3a74f5e6ca957dceecd16465d12b856f44830a36d0d9b375d500e45fc879dd79c6727c1091d0e62375e2223981186d0fca8425b9d46d101053cf07fc0a89bf1427e00ce344a6cf128fbf97e5378ee3f07744addacd1c7c78fce6a88d8b2846da32d2e4c1efa512638d9c2c86d740c0c20f12c3f7cda020a0c155340b2fcf693b885279d54b5f1f7d51918e35480ce4960ab0d8af08881715569583694d56be5b4c7b4e948a0dd3ea7c1bf10ba5351cfb30ffa8d94f7eae7cb319c5f5e803c471639c20f1a4cc3ea27019ffa9c6e536ea53c11ed9d386961c8c6d4418580d1ac728e80ce21435274228dd3cdbeb8b8c54f40ce062e8c2c2fdf71843394ffe142b2c92da16ef9524518465cef650d413da79b1baecc91fab7fb8d67dcea06e79a0dd1f49c297bde2916f07d8dfcd3a3615b5d8efa41b09e6bc4b87b0200e172225b58b9a720a27cf7f7ae47a83a3bb26bcb1ca28779d7185669c1c8edcfafb97088318c6e4fd18fb8f6fb30ed65f3af8b0bdb44a32b237a7b3af9bf9966b8a2ddf4088e7b6ca818e67e6cc40dc3f5b898f505c71d7dac63cface34d8ad57a06d6544f071d6bf727a2d84b58cc0bf48d122c6457c36f1d700bd2cacc8df6e041869292f5c54e92df3c6103a3914234e956fbade2d51751086c64342cf012f785c9e9243fc94a576e3e417a1d7ca97879f1d7704140b213d15875f6f8ab98923cb0fe8d3e430a8c6b8427bf057f8be528feae7a3b554f58e052a029a21fdd3abc8b41b8818b4c5b7cac00d8c7e6c1f41b412b8075875626eef4de3ca6810aeeafcbf80d7c8837324a1257ab0536632e53102b10f9331aeabc4fcf6aa4c948935ddd28ad46610fc334c676b75ebdb7ecefcb1c4079c502639971e2d54376ea1ec6b943638a161a4f69b1467221674620d2a93474f0036c7630a813ac65cb295d300b8c0160045e94730e93a5297e321fd5ec32a08c0918c01b0ce98890d3981eebdb8ffca60f26552dd5a1f3b4dcb8778e65730cc2420f75aa9b229f4c2ab331fd8d5d871d04062ddf4ee004429cccf4bcf04dbef69628ce580ae312f053386c3b55309a74800586fcbdeab4c18eb62fdd0eb0d97e21278129e1dea1f584c1fbe29b467a232c70ca1b4852b957ab8c935928e0adfa69c25af27594a0b24ed6e2f919a6fbdc80428357d43782746ac982b03062b3f8cee441d8027fa1b9e7aa48d5d208b0b7293248d71b34b8c49a9a3fc747833b926238770c92cab76e83160d87c5b5a018501861f4f6bc47581b33192d7fcff480e540e1af9e8cc07a0b1e53eff1538f41fc51ba05e9759865c2ef38cf8d93a9b31fb9e971c4f33e9362476d8e82e2380968739ff9de55ca2a36f0479eab26b7b8407a40c8a34f1e1f5401fddfcea188898d475762781c36150158372875451fea4a6767162193c3097edd9067b7e4c1dcf11c538c103f54d8d32b2fa241a4a880ddfc5e22af00ab111d404c289eb80a4f4849a54c7a6fcb4f79d079f650c9a71ac19bfc4403f8682812a910fbfe8145a9b249244d0a6583592f552d5b7e6499e0cb74796a5da112b40350020f927306cc8b02f222f6735e9b2eb65807600b8751282b5434c40ccf5fdea5d7b8e6c2ccb58372fa79fa60117573564cba5e532cad2861154a4cff36324feef34e9e722fed8413d460e7522c6da5f8f030c9547518cb9d5c5f55a81bc321875723fa734c34ce45fe2dec5702377edde902da4f34b2d5703fef1f0ae02388663ca405b32dd1f52d13612027e31e102dd732eb48820b1411cb207c38ec6092cc2411cbd0d41dfc4b59c75a9cec0746f3e3be794ee07549fa6a8eebb6aefbd59f8aebdddbbe23fa3b07689ea7b0daf3d52225eb50d0de8998fba6c991085f8bb67e4a92faca61ad489c5c5bb9e7353057663ec7194dce3274df97e75911cd6c6c8acf93c1740a184bd7ed0ab9003975d677d4aeb6378e4a4b1f0043ba7d29281b7b975352de608126afbeef30c1c75e7c0df8fa0dea80556c264490a59f579236274cabaf01d4682f2a263ba54e54e640fd44561749b4530bbbdaae07a65fa20e5f3727f0d92aea1fdd37c9fbb43a4bcb2f1ab2d4fe0e0380bf310a9a518222719aa956c4d5168f058a275a68c9e899aa7539b5d689ea2111c468c212cf4d3b2ae5e5ab08a2e18c6353558eabbf01702cb6e348b682b9c9c0324e5fa8a9f7d120b6cfe80ecc2f36309853dc44c9a714c255834065432210a05400360793af3f59d171da3258d6ec3bf6f254c1ee0ee126ee1d5a63ec43f7e7296b00e14ef94cccac83beddc2c5e228bc78f69a683f54e8a8af0a56289aa468d1e090854a277e11a880ab0db465bb0ad9023f58c15d014ba31e2313d166b8cfaad3527601e98ab948ab3552e09bc359dd79cb491bb7204b2e2ee1b8828009fe8672c9001724d7c4ea06816adc3bce0fd4d8b8cf34ad5a1283ca43c3c79809ebc2e8ca5b7b9079008a3634661ca97b5cf60adbef099255248efc4018fdc4db8285c8e7da46cf4fe48f7a121dd2bfac376cf380f4f8675f4b4aa5e038db133507e899b64ba3b43d0dca5de5df2797b51e7a343630064c8b87932f3921ffaf871f6cc7b3103671b46525cbd8cc30e10d6b16c4e611bfd99019b1cd86e7d7f30dffe3708c66f64aff8fc42af33e67f1460bf0d3b0100e93dda914a18eed5dc408162dca297def71dcd4c8845858da504506737807879b656df0cfcf1c5cfcb0c3e74a9164d8500221993c33dd08d8393c0983a7a292687d5ea5b14f32738d4df77be91bca969a761fee692f48d18cea5b04b35097b1ed12a99ecc8f25bb2d165e6abfa2a3a847b344352ec6ac37d4b23652c50b5268b0e55948c9902b4a4b9f300578f59bfa489162c9d3c357da71e099517d615c2442c82d73c83dfaf417f7a4e383d0ea2517fe4cd979c4f789a3dfc53bd958bf21b0d5ce0f30b52d83553400626b0a55375d85dea8013a98868ed4df67cd83883c459f970d28ae8fbb8427d32a4f3205f24e78a9bc69a8013cce1f671a5527b5bbf1363865069ee988cc6ca80d61607cab4f764116c5706a93fc8d1f0a15109217c5f1016f760e851d0360aca0bc74d174068f7ac4a0f488c5f9ecd35f080887c0337723bfc52c2e1cd12df5da6bfe9d419d03de7ada41ab34a24aa7015871e2cf456030e5a349fa9bb2e521260b06f0c4f07aea7dc0eaa297fc4dad58023bc02b5e866ed193b0d149b6db5dbefd7930d319bc068a294d21054869c94c2619708b93b39030a351029efff70cd868187a017cec48f87111563bd8659f36a01bf9fdff48e7a8e02dbd69126138cf2a461c87e9282396158b4db1cf53254df85febc6389c2a796ce05d18c7c1812801d399c1f9d362045c40c128fe0c341c016ef2bfda81e38bafccf7df308151eea4af0e622274863f381d1432940d323d4ca092f5693e0a53fbcb61d66890a7737028cd7159f7bf64abb73487c1bfcc42c7818efd94c227c39e9f299f4414048d57fef7f7587ad728174d15051036ef674aff9db06cf7ae4f31858cf6e716d8f050967e5d4dcda72a088ef9bf239cc3231e342cf248c827e3d7c8c26d6cf274ecfb34b3fb0ea999a7c8b5380130087f07a62f4fd8f576b88bc7fe3557de9b7197fbf3ef7538fd31a18b28cad16c16ec7108465b87bc47124f9ed6cd27c3679eec251dd59fb0845a6593464db8caac7d3c538f7036a93de4e3cebdd379c9d21dfebc62f36ebadd4e524dc8f81647db3703676231bdd0ac31b16ca0eb9f3d317e7e7cb8ec3af92947c97df7ae26382b2d0719ebac8a3499ccec4ee1551fb0d54fa4b86152986ca506e051c8d2a0c5d2c046879ef593d4f90da2c1e6c13d6612eb0ddc8c91655fa61819a0b3d2d9a1a81c03244ff834fd61cb0d2dd716193fc3f7d7c0ce95e66049be84557c667d7f2a3c6a5298a35d1bedb3013dc8ed7e92c255a1a43b5feae5e179117b53f570d170a46c651d0716ea6992033423bfe6229bb9fba03fd84281e087d2b0cf3fa26ae4b4421defed83f5db44de42667085a44e7939cbe070e70549deb1116fd2656e91332e5703cded6a9e3026fd9b0d011a3fd3eb10215eaa154290a7ede855e1152fda15f3bdcb5fa9f615f615acf68690c7f2d6477ff4f61da6682bdffddd8f4e338c0e14005f36a8b88c9dc9cb1f63a5bad9a912d2a9fabba87dada9a7a86a84a54b0f88f025ff338a11621fc506be51622294905d42ea97f58e5fa0a4d51c278dda4a0950970dc7f1caac6bd05e89c55781c1d0e56ef32006b77af49dc425c2379305d4c25a3906d189f989f2a5b69bf3d12dbc08dfec45dfa6ec218b47a2fa8f7359876827a25598a65975cbf3bcd49349c52a4cc355da43dcc9546075a2a7d8064939774a87d771e6342d9c81f4c2c521acf601f2740f2fb06ae2a1b8eca50aca2c4e8110ef06e752239900ea19e1535f0334257c94db700ab58d26f98e3531dc277fb6b94939a41e1eb6972905e6d195408322a1bacbb4866493c184c61c2780251764d1df66106b9974201a0b8dd404de8f1ade19c5787ecb8971bcff27da672d149c390e8bb26b0fd2371352fcab3b22ea6c2f075249d3edfede2c673004ff39cac1d04bd6617a79567e8b64855c1dcc5976dff1b15df0fef8973a7048d16329eaac0b8e9fccea8a824cc702f07f7dc11a59f410479366c29d01550f5c6c3a63daa6db4223fd3046eeedb664f8b9ead15293cec8c3b285703b135e766a24992002d259b5574b829cb96211a8eea9b2d028aac0ddcba0e347d07b1037b16919731e66bd1dbbfcd288bb6c9bf4e35bacbe3c025bf5a36440543911be1d01731f500d9bb17c1314f2c60b92cca7b239e7ffc4aced24aed9c10888ea9ec8ef773d01715e3911b1c89a9437fe96bd1333cc50bc95fff528f8cc0835d7649516d47666f3f78ec10686f0cc3364d85074df5ef114ce247c0e98345ef96822a2177d314d576517adfdeb24ad705be2bad8d4c450e95b7c2041c653f4c1d523cd84ae2712c6b86f8284da26703c75fe178e816b3421692c73197232a285337720353bb6033ef114b22662162d23d36a3c9716057510285c651a38783017f00ac9dc760721349470c08b7556c020107c4ef24c672edaf89c88102a48a7620bba3ae3fcd248f6a7afd38e06d260eac85f491b9182e4d7169ab36bc70cf0f366401b8daf0c4b7cc732557e843d569d4e550f8760e52eff4e353bd2c22a8a3a6b0fb3443c2247ff62abd7c8f47c892c9e2cb289b26e5d3a241158d8e308bb1c723ea0df99e25e42cc8d2ba124c2d535f21efcee77946269fbc77f5efffec86c062d3a171bd32424756bbc13fc2a665df27cb5336495f28f1291177c4ba23589363e423b040f51e915f60bfa2cfda4240f477b2f5c6e5dfc68d73608d89e2e82887f6e63ede81a9d073b638fbf48f6d0ca13e3d003b4d345b44264a0825b765e55770be2941b226649901f5969192e84e08d0b5d12450faa5dec71aec1dbf39392f357f09fad31731d4913f1db4353af8c129d8fb052f975c8c40c574ac5626a70fb501da4f2644c3ea5a2b96f3e7f5976193e3cc13ab60d96d20c2a61f19a7166c3a2f1c2116a8e53b27fba5170e60363f1b00c312b5ce202a7b3ef49ac0a92ea5bc24e01fc20a7455a986c352e0c494e30c382b6a48a88bff4025be9901dc1d2bdf75a77260ca2b7340a6d215da0c35ed6beb52be11f8b294f234732252c8a0fe3e4d305a7111109ebec805c3f069ac288d69bfc0026137dd3ce3aa741f613dd4e0236d8dc18c61a8a84006e66320098e92d77c56d8655736da0b28e68936ecd1ed61947721ff85ba48a2ed8950464b16095f6e249f6aca1e850153374bf9e2dd8ccdefabf448c3d138f824f9d2d45f7e8a7891c5622cc02df2f8d6f68ce74eb49ea98fdf914406dfa79c5061e62d73f2651fafa3089b03d3506c3415752a25b75b5aaa3e461f09531d16f1d90227ae6b9ec33bdd7d764f76abf7188c43e636763ffe176210722d56b08634f3c2ee7bbe838c89fdbc8e375214152e2be26147f908ad4389e3a9637f17885ce946d6bcf83f0493d663b89d2243d4f9eb2d4fb7881cfb2e1190e4f4ecc5d8b357f91af489fde1238e1c7a3f60c3a325dcd87b16a4d461403df2f59fb1d29bacaf13ce0bbaba9ca1d170bb47c68b1ef6d0b580a1fd9372d8efd276f2ae9bccb6465f196510013a85290f460ab81d2e0832aa38b0c42b8f534ec2e7e26d1b69592cbdd04b1e7798d238e9383168dd683c091d5d39acf935bef1d328953e5394c2f7348fc5523687ceaa6b5753bcc43a5ae458a630f4121843c13303f1f673f1fd852dd0ee32ce5c8602bf772b044a93720e39ce16eddd752981621c9f7af89af4864cc2b4443988eb6f3e2e629fb311597793948b251fd489fa203147ff23ef3a8565faed223a9ee7cadf6fc2bf7241a100586f5af4171942c74131de1a26a83ad47a78ab024ee9034530bf027b979d30f76f0d386732ba5e12c0b097c5893e8357356ca061bf95c556d31e77be019b56a5437e7755e5fb5487c2b713f9ba3b9d52cbf21111ed559c2e2db24a9d37db765102250ffcdff9127c0107b81f03a85f40f1a5f65cb1388502acc3ec0b02cd8e6e27a7fbfbe008f483907d11f8a96af66a9b009896518175fb613ee73df446e6c00225ac2c70b879f4162035c3cf26af99f7ea6568607ccb58984d08c485d789eff71e16618a690926c1c1d5d268917fe0542e5acfe372f4ae597856853ecd86dd4ba722483af59e1a90643a7bfe52bc0dd848454c5d1cf6e92bd173ed18a5a31eb80e4da25a94072c83ac7712c9945b79696f449433ba63609bf09caa3e32e1962e17f151fcd96332440f121e8d0fb8946e67c0885f9c645e70135624eb9ee44443b9c48560264cedd2619ee2efb7022b7adde9a62fe4a6548c3242e2e10633075ea8732b6c20f958e166919c07c4900bd4b344fe1ec8b610b6bb5275ceb39dedf2b947f77e1b9c4fc44dbc89af82863108b8fbcd957f5bb67a0143b287af19883aa7e17af3814c7f506b3eaca5c925af56474c2fc1a120a06b9fb6ea72cc2d5fa34e15d7335c88af309bef36bb5fd668a6fcc9bb47d760a0625083b11f829afb735dd46ac8cd488b7012f4dab57359e82c4e68d6eec578304f2383ad63a6e671a0033c9e922e79e683ffd4601f8a0823058d95cf2a73677b743bbe4c4d9cd3f9e5b6e2667112210bb21e22215b446a4670e9d5fddfe3f421e58be8cc21e1aea586eff4436d36bb66c46606dd3d2407bf997c7a296eb5c10db0704b8d26b66f26b1c364f7f600e301e620d945bc8eb74ca70cb2661b7c24b350b6ee142eb7ee2ede81c2704a878be1d42aff65a5ba6f6e3ca80236a023ff90c4ba6b545d8873e592a26e5c024678bd13742e2c5f0df65e85f8cc266bc232ff320c32b59dfd4b7e130f462bd53fdec6411df1fea959c4b5fb4accdb1ae897f1f5cc56224acbf217a18ed0fb50f66187f4bac6343cc8450f4b660d5f770f21ce640383b0966c2a813f6d6aeaaacf3db6253cc9e4fd8744a973c9bdc06828bad62d60d5e5cfb0cd53540e71e1be4c961d04c9f5cbb3e8b7422abaea80b4564f45a2c1fca84f6e6e87defe89ad584b3d11c18f6830289389793c63b1d5c9f93e056fb2064489a2dab65448e9f0fb1c0f0ee54ebbc78cb48e2f0753a915ff84740855f3df54fb63dd2b22f09b95ae82bc93c6b303c1ff8c188f73960a78ae5b5660926980440de6fde5dc0570d973504bd4322ad8f890dedaa7fb9780f0ffa7aebaaf02898994d4f3333a5abbf99c657480824b67e2aff427cbd4c77dcde7a736ff3a3bae0b268c8df377da05dc198c04347243da1dad4418a5dcecf843b9bd10c2d89cb492c9705ad183211b7e248c111f05cf833110bace23f9ee5f472e1cf41ac19d7950cf73560b08b08a98a734667f9017e6d9df5a055a432b4f9be26d77056260aa4159a42ecdadb2042ceef008f056877d26a3c1b8ec4f714c9c7c8d108b45438e9b39e98b4741fae14fd42f4f9e231fea9cbadbb6806298871fd3f659698d09a9b2d6726f8576b113fece8c369237360549577631f329dd724581e8f453e190ddf86babb525768c0932a4926eac228644487883285baf1d01e0780d06e5e387c920b9e751c0e443987949707174c5e46aef7a24a2795edfa69aee9f766cf342dca6b1d6ec0aae0692fca3b514ed85e0a270a6b5f95039ca0dc37b6b31f2a43c803ad63d073c043c237f1a11856f2208f9add3b2fce9bd8ce9eba5f98c388b4b94b52ed024e48b5adb2599ac4e60b0cbb73a70111d4b185f5b96a6b78a6c1b2d7028454bfb73e7a9b719ca789f92c0e216f2bad45e35360cc29f314fc659126b4d3fa49347c6814290298270a8b192ffe022d6d6ab9ff021a1b4f43d7fe636a0ee8d7653a5f280a939595c4f9682b53f8f370100cb52835ae80b9799d115cc037755fc98aa6186e73604eaf1c9ad6204463ebc85cb459a113a89c78d9df1b2a11c0a9a7282a5fdc89b014d80f27f405b3bb5a97413f7dec3b5ae14fe0b597c1b1d18dae6aaf1cf6e3699e930ab0652fa1cd960e70829707324fd66114e302d828372131c4c932821bea480227a64616018911d545a27503a75fd58919c18a46e5c38b674a299d35258a59fed574614d1ee36f8b81a9bd8e34a3d24c65d5328410ef4c7bffb1e484901cd253a7e80f39b5bc03effb00747165409067b4f39c15ed1a9720f16a858ea146fa9ddac82143649eaf8636abbc07cf06208ff6ee0dadce0b5ca44c8f337cfbbd9bc513ec2300efff6f6fd38e48335fe35f6a08c83a4d9cc9f95f5f5160dd0ad90a5813b9913b8d60302d287a27ca23ef6a50e9657945937a54c1b51f588feccb7a5d13152541d0c842e4096bd949235161eb74b28b58a68c00b96e176976fde6e950af657542c38f94aa2ec99858e205e86c60403b1113c8acdea5932056f13346bc3a287a5efd28de172642de9c78ca263e1357ade626a223dc3f3c835bf29b2e37f2fd824bf3507beeb299ba49cf5f995c7591792d9240b24cc1f19eb3ca8a9490b9055b2978571f67cfd26e797b7c2074248d986aed57c1e1b061407991c8a87041fc9745e58c65096f547c34b21b3033f0ffdd0f7f8fb7385101bd3550571150805ba51eea42e6e5902d6a1341d1d607fee0c11039c4296b7dffd385b23475676452de5e29b919c03b106e541b4d47bc027c982428986ed5404da592ae14fe0a9c709022612a8ab9c2b57914642071f281832d5749a1953255da0b1d1acabfbdd15cbb1f324b8c87090d92147e7fb10b5a2724e570648cdddfe0f8ee2a18677a362d8d2f90b2afc9ec20dc165b5b90246461a369b8335a2c0f4ff5fa13a5648f869855651773cd5865cfde34cb31adfaf956d9b456a1406d63bcc67331c93241cc3cdc2472165bb480d3a94be54db937ddc2db8668bdf6618cd1229955a7bc5c4110e515b03528a6ba4e042c277d253a92b585895843e8a11c76183eac18bc4f6f815b1d98df4443942ca83e712668a9c7af4310510c0a85954c7a00a7b6fd740b8d33215039cfc36f6723c1692a9c85f6ae9152b6cd3965568cc04cad6c00c6ea4a3c2d8888a6f078227e9b57b5597682944e723afd7dcd20bf49d1580fcf25b8a90b44edb4a2deffeeed4815451ef6050e474250d3aba80b6ce2bfa75336759df9a0021ad248f74e0ccc51b50643202bdee1fd0ff31a8566466caddd7d1d8ce23cf669f62a06521a5a997afdafe547a7fe4f09d1321cd3c166ccd823d36e3e07edfd1f60c5d8a3a615f0154c37df22af9ea8d73440a72a3eeef773a19f403ac1a93b6609b2eac4f94e73771a6972aab4dbca296601d30fd836657175544e92a9d13f5047c765d38b060a0c1698c13ea0ea1ac0395e53b38a1da51e074862f1925bbc09ca3b19452c8f656e28b380f8cd2c64675068171addaaeb3678ed9894f4886609aeafe914e220b8b4d7228e7e7a4c34f5773ff16df815ad844c4ed320016b916774237546b93de91cd65512e3f87d2b01412ac70dc111dbdfc239d5b7428a3a43755c099fc60dfc41d659c2ed5e7c38d733e9772e2cfa668d0a285acb0ce6813c3695d0052ee3544335867d5829bbdb3497637485a31fa3183d739ad5767cda08113dab48ebf781ebec7a592bbe50a4a9dc47f312f0c30ec4eff3413da1fb2112bf0d77358ab045abaacac337bd507d31a626e3edea01a48d377b9da8f77e5d5a5a21bcfb2adf623d303be463ae4729e0eceb8b507bf3acbf2efff9ae1adf0f24295a9f44cdf16b7e01fcfa446f79712691679b4b7bac281d65a39c65b9b2482c0fa2b5ff94dc8ae1defa873fdfd371b9066855065a2695b896ccd1338f430e041a2f8e13622e444050399c94b4877ed00be1af9c6eb76078407255179690cc5653f979c389fab0e1d72fc50a514bcde30125ce51a3f6aad9ad7dd6be9c832f0e4fd7d25eee3d91f1bcb069183f325211e24f6dc9be9c75de12593d5ddf0d315d7677ab048f70d8cd2534b1ccd172bc91537022c0ef64d3944cc434d02280535f4484f8115f5d69c4069a8e83ffd6a9a01b13f43f69c11bfd6703e5c55942e431f6e5122ffc725997b70cba11a686d0960d99d7a5bd76f9e1a15e4be0792a6e4ff87841d0d7050db49c5cc0961af8b1d6ed2f16bd757db77198c7b236aaa01f2694a7e4d017c1e305c3743c9460e0b8201c97a2496ca680b8d9488027d9b5f42977abd902bd1fa987b794241af3e0f635afd32283d82940def771c9e6fa441d00916b453eda2c3f0309974d56cf457c66713e39fad2c3c3689aa8e7fd3619694a83b606ede17eeb333d7413aaf7229a6011729f33e1d6eb618a7b3811b62a761b4ec64f00f0607a30c6a021a48b26a50502dc7f331e9cd0e2c7630e87024c6e43ed5b313f649d481854237220f85acefc1ecb59efba61aa39dd84763dfa55973e59b0f6eb97a39b3c43597f967dfc781a205a5d83e580d4a1b630d18a8e5c352992fd76f170151e67030e1c23af9a5b3c940395946eb17da4df43e09f4077055563487f56fb35949e5cc35af1a3e62c2d2917045da8e3ab68c1c295124b40cb055b947f9b9a05f2fdc47a2e982f9d0c487d5582e2329d397fdab47b50aa27220d10b7d42215951e8e1f4020000adae02a320cecb0e2bc59d3b73b6e03a459e05d78dc98d07693debc1b71723b436fe3bc0081e4f58aff315a85692726a8d576717e22e1a1f3d6fa1691bb4a68aca84ee83442d24e34141c9c1d2bccf66b5ac71c60221abdd452325f825747582f4b4ea4aad5f01a58da7ce3f0c10779097bd637d9102860127594c6637448a20ad0410d09f9928b65dbde5d20903e6c2bb8b8711a663b416fa75ff7f3a53c4db7a15b9ab4f12ab371a70aa7d584abf8b9e80268b52e7173a6757a31330a87aa8f0b72e8121e28c3e957e558286f31b37e5d4e05e9f18d2827a78eb7c6b21085155eb952a961d418eaafd6003b8da989a0de764cd676ca224c1d99c5db9c61f8040ede8eb29642199b3a8a241bc55ef75a152225543dc0459cc56095eb919b3f0bc7325200494ff2999269df2b4dcdd0ee1fe6174dba4a5b135665db483737e20a6e27208550654d936ab38bd254e2e2168e1e21a6358aa4ce4980a4ec30a5e4b1c6676539dc5f4e1111fecc83b91780e543550adfd49d77b9a3290b58a420a990d7a8762f30a6b7f7b0aa5fcfd258b94977fe187a79c65e559a4dc567112eeef5d41097aaf1743e6f48899329e091dec7015229fd2bff091e17c6c51f98af83c5ccdca29d2445fd7f8410c42804edb558123a44887f38ddf19b5f618ef0e3102d0f95684fe49846146427e380377cb3ca1a9607cb6cb50340c62a18b0fc47e81ce4c4dd07c79679bb9ca319f0a7c86972edce39a694b7557ff0bb111cac8897e9d2e299df8b1c8fddae68f566d3a78a50db536dc750fab3191acb13edf42ba53517ece7663a5a002715f98f4afd99774c22421a73a05844f119c879b0c568cd48e1d4c682c8024b5dc807fe35e102f02edff826a54033068dd3e6dce42828e76e1318d7e9498a39fa25fa0683b61eb57db6163d7e30b1f292eaa31bd241d31f4b1f549b826704c02aaa78b33e22f2077b59c1708f32259aa4a3928acf86e0021ea40636319ad77aa89c78ea136bb0c11413bb35a4ed964ef5a84f4f18e2a30a541f6191be581bfd9ae79bf0ec696b6bb7a6d65acdc7c9114949b8ac37c19b947dee193410403786bffae5e73709bf6fc537872cf94974f25bccc2f889c37a7e05fdc58eebaca10faba651389e26dc3bda704d278a75281852cef23770a60c2f068c7fed1f6e66afb617dca74ee9a34afe08936efa13e426b658a93de7d8939ef3bda557d04976ee7fba5fdc1bb096be2339d2496125308394ce23c39b3e84489ca0f27c12e90a901ef9c344c88739351b87bd57d57eb51903522c61e4dbd72bc6ce1542aa7edf02ee0992d6649083b44e5be1504b2f7267d23d1b051d8be83b349ac34d0fa48d2c075370427e3aeb41ff998e16ecdd01392cdde0c641e1b2405034cc677bc5d77971156fdfc30dcb14dace634e6c871ac9cfc0af4dbfa6ee768004cb0ee1f402b2ccb22969c5028fe310fdf200960a514be36261214d57880f72d857f6b268411f630166645daa03efece627078836b2fd3efb1348c17892a233d45b323e727eb38f2ee3c573e067c4ccf1794f5bed3a1ea7a4cd2734edc011ed73c12257f4a24d759fb831f4e90a8a846b1e4b7a5f4a75d29cd5bd8874ba6bd738b1c03c5061cf3496ba21e3311e33f7c2175aeb4f23d911fb594758e8112af7003baa1ea6b383001aa8d6ee46901ea2bebc249b6e4bf88a38a6850d23f27f45cb9f08618ada18c0c2c29b27631d02d10748431c6c0b99cc2bd7b23e766fb4cf08c6484a824e908cc6c9831fc7152f8250e0618bc922acab98a2e7bf94bee3897724a3620a175de304062fbfeaec6878749061e5cd842b4a097fb40ce00eae3927cf9dc2387b58e1259a110b9a42737137fb127f33f3689f76a2ef446ea94b5c175055e82aff87f29af2e6f02750700871ef11512d53da010236f194aac8a07720f4bd73d65ca50c6a1f394101261f2f7b020da746fbf4458a922b95db7c8ad4fbdb5a7a92a72a2297b461b5d389d54c33c4290c5a1705b6e414e986849412255466a2aade7298d10dca2cf4926cbd55653f7077e7cdef95f8948d2186244aa020a1dfe330fd956e3b79614db3ac5dc4f2c06c947802087afde06f6f8a86cf26c6c2249cc05dd35449f5501f27d80f7ca2c05e96539d4ce3f184db15c7f069873d45235487be9cfa4e118c0ea7169e49a57d680a91c469484a040a0cc28917fb8a361c90162c1af9321e73fb6b3e34f348751324155bc5726c25cc5b2445345a792efc12db2222e10be15ced9bb23a0203423e2ff3df18dafcab9e754231d14f8d50db5f25be7dfc41fc065914798159d4bd3349edbe6e9343e2f4bf908e2741cd8144a155ec9ef8c6bbe873ea9c936a84f7549f730d1998c133735ce4f21ed9d3a9dfb282dff8fd4283f80494e33c677f06102a45efc655631b0d82f1f6ce0ff9916a5dfdf1cb17e615b8892cc0ef369b114fd39618c65b023cb13f7a1c500a74e2f9a94d666ac427ef1cb3f2a98b570fce472d615eb140c05f2abdb888ba835cf5112326d9b8f71abddd593ad0b4f54afd0acb5f8b0aa39feef2a9cd4a65425b5460ffdf522cc60f349b549976491d93825c6c0bd0232549d63eb3720c31acf5c748eba7fc18138c158f5a61608719ec6e3fb32c774bfe00d81ca57554ff8fe8bbf4faba62c5961e57980a87614d460e104743a2b9ffa3741878aa8dce0b4def176e5b36206b6f1b06dd986350597ec7f422b280cc0a78e9490fc91eefe88409e3ffcb72e448e54196c779dbf41e997e3312d5cf72dc13673ac6a70838c021f9351a5dcf23aa03c55a0ad5bbb03d1154db671a4096d072582d08fc891f8a11005627555e139395cdc690ef64add2d2a3c90471445d9599b7ebb37f27f49cd450fb035b1fa43ac4f0229c4e638b144bf12e09a63ade8b09c4f828669e53a116c4456a3578857e96295a471a5f63d87a89d462eb1c092ef8f29c63f9f07a5d6b0a5e7fc4e24d63522e15998dab06ac42b21f6e1747cedec7a499dd36cebd14eb0684c8b51e30fa033a81816bae855b14fc6354b004a6563867eb316ca45985517eb69155bdcddc9b89fc84b3d6e74727ef101e5d121c8934bbdf72f2fff22c400194157fc701817d9154e46dd5033babdbec4a300d39ad0315d166c15f2638d64a52e33698dcd0e7466f194080416bbeb0ec22141e62d5a8828e59bda51c422cfe4ea9c97f9659b6049b8b966c9755c798ab4e6bfba9f4f08b1fc9cc94e59d12080cf1f8fbe5cd10366eff162bffff045bd35bd0c7c7819861894ff41b68ba179f1658c628f178496ff707835a2b766292ec715dd705a017efe2e82b9dd0176d2ba4c71ff8a3508fe74516af2141905cd89467c5b9321f33ca4f2d41520eeb77853d298e5045daada1b35ad9524d6363ac660d5355a329167bf04ef92c898cd21b5dafa69d4ea8cbc6d2db3c8d27ebc7081c43ae7bb039ae99cfe92a24479d099883fae7bd96cb220ba4cf46bf7227af852c321370e840f228545e8711274b4fb7020233cd2ef8f341910fc03aa19a275ab2413f2045cd59d11763bfa6dfd041fa1b212a3dbe8c9a6f5a49afa3d204e6d6df62c6b4ea7d2f5a4af58e8d2134cdeba005b6a290ce1b8115ed2daccb17c800973a1f6de6beb9866f6df0bfb22aaf4cf3b50e7f5b58d362be8ede518d830e2bb6459a3a981cfcf2121d33dd24ca09f3c605af1f49d8e14273626a6cfb614c3e8900d68382ac91f02b362ca913665ba56e7410a9beb597f0a2112c412fc4531b5569ccb0f0920e5479d291903ff08d4c86f34adf9d6d7a33ed60150514fd94b2eb2560246f48b6eaab0d619a6867d6ac5f73d076e57f8a8b90e8e79613e2c8855163d4b9619c12e73aa1ce8411f28b0e6b1fb3efec81bde6892f0a3116db3c237b5beaf05c08ec65e44792bb08ec50b71edb060e0405200d79559c7a28ac485356045246f4cfc90470e672d9772c754dfc8139bd4894ef7acac6269769666396d050af060c8a9466d9ac0a8623d8a7951e54d61a493b52a69ce583af82a4efc20f5f7279dffbf520923fc2fe3bfb38563d7ef208df5426bfc93c250f24e3a6748304b3cf74422280b5ee678074d5cad1cc33facf99b56163082311f99796b7bdf83521685ed5ed174f26b458de389c34a74781dc450446ade4de4560f8287af59c33bdc11a7b58bd2d6d230b057c066097ed847ffa495ca6c4f636f9f72d1564fa7242f59a12504e63bf72300259d5e01161ca8653f638f24cf1fec019b81add251e13d6faabb42ae36bd2a66fe2fbeb0027d547c447672c46eef7a131cc07d3c3324bf6530de28e4750df412ed9e101f53aa19c7070dae9df97851ca8d4f9488700d0b5eb5d0191b5a01073731ba01128418cc8d6722d7207a893ecf34491421e00a9bfdf8ee165219d8741f72b1b1e785267b60e4fd7eaec6a176a533da53d344dc8f3e8e3a7611dab1ea49d59373e9825773919eda883bd6b5b9350970da87e730af44715eb01f85092f7973c5134dd88b6eb4f60e28197de7d14347e0fdeea69cc44335ab9a703852b48f8047eac94c785657223d55e7cde9321b04a1421a02abfaa118fbf7ca86026893af12b6f17ed6e8064f0ce9ed955734113338841506819fb6669a63036a36b0840b4eef7d87b6f44841a8f6ad276e8bdc2065e2cab70edf6220c61a560b782c5a495022de71da0e4890d5580634ee243818ed401192703881bbc15c594a4db82a4c49236c4a1411f1bcf38c1feaa3901b0f92fb53553b7517265809760fc9e1f023044a1fb701c3038b80144b8672233f5cb99695d481421be4915e88bbea378cf2492d22822c3ea1ee3bd734a52d83d59b0d498ef6203a9b7ba09ecb30eceb18b44c0d4b885379506385c06aca49ad3f0839d9dc538821c6439411d0f86e023f8a8e6738806a094f2a791084c1aed5539657b69cc3d5a266eb54e9f1093133980be0e93b28f684d673a5caeae7365df668330e45c659f58fe33e8ea1689a673363f4e3e84cd45475a713b2fc04ce7acca1b5ff92251abe1253752a6de38355cc95939e9516bc020a871268dbd71e9101a5847ec473f680023e107d1dbe648628e7c9e2e13aecf186cf9c1f6dffa7eb0f2513c5aac288e8d5ccd73793ab7f6a5d4ad2171fd9e5e9490f12ce8b471744d1c73a492165774c23a82487995535ee9902c54cf9af1f34cd32b7577aecf6d216cafaca2903d2ecd955fc72868efefcb0803fdb1c516e9c5fd8b749b4bb03ea655468964b36b2a16222a0151e9ceeb0dcb8a8a89e812d15a57be775b7053ef4f953cce3dc21435e131af05f90093ab489fcbd2aae77b78625d0533d4a814ae68a73681ddb268d335074ff7713510323b75f769f6c213b80454e89dfd18b89a1bf74ca76afe1a6cfe91327c350ddc7c4f3dff7b8ee4d6f9496319956dbcc749260e14f8d7c01932e26c3b72091c5593db56b8f08410a88dc87c361166ce3203760520298a623bd8be367954a9a05f31482edb8be098eb170d0eab2dda8aad1415cdbf2026b44111faa8fc62dd89ae28e63c38c86ed887d81b539708b8996af4414be5256b7406ae539d950567f16705aae0788048a31ad376ff0d87a8560f053dd684f734f55705f65a1ad07f66baa2e7ca0fcb23323913ca1de4b8f41be9a0f9cdf110f8884feb4bd811292fd48db925ef28999c1cabd2e3fc98fd111802670d4458501e705c7c1b91cda46e83a34d8498df9667abe8c118a754589e2282261668f157d30a2d98fecbb0982c0cad7d272acbfbfcb280f28da1cc1566c9fd0d9dc3a717b4f5174b71f9d0d0afe786f9c318b81574ba3ddb884174332145fb82a995f44be95d669462aa46c9576fbe6f45ce2ab13da05cd2cf8a45a551915732845bd5a761be1708b1c448f63c1d176bb0662352bd2e2b197ac537c1cf3547708bafcabe425b020e4a54fa3d9d26381c37b853574798e34b017dbff7aba9c74e5be05542ee2e14e5ba1e3860a8c54c92b72254ff159e06bfbf505d34bbc1266f4bc506df20154b1278617350f682cad6e027c01e15103d8edb45bcca82d44363d9c212bdbfcf6be16c0e4a831e88a3ecc2da90eef7ec5b8398a94eec8723e561f6b9c64acdc40862cc8119700cb904dcc96bd36da7a0ce4787a4a12aba09b598a1686136bdd10973ec3b5af1e0ef40dc56cfd0631c1d0b1ce1b590ac4a5b821481f8c7e1e8ba153655d085585d0d8c6b57d9b8d76c6aa56f26c01c453afe179b89ab980c20571471f0bda1a5003040cfd5dc4a43f8536a7aef0acf1937115196e288bf34c16c32775a5744267c230f34897c8109159294c52df85989056e7cf4bb1fbac3e8dc1765f6e6755d6dc4c7684146d9192a20aa0467988b093cb6a1005038a83f96682a27dd4a164bdc3f8a272f60d6ceec368e129ee1a38cad862d674ba0b012f2645da29056c5b1b7d959bd33e453481757984e8e6c49f20720a87febceb0a991a6295c6acbfc867d59aa49128b9712e52fcc0f5973cb20c5e45c598fd57601ad890dff57287d77ffa7cdda993a0d91b0e9635be82cc59e724b711a9dbfc380ed5b4edf6ccda1684d4860563d7f8785581984e09d881013997d54e8047c9612e71e5df3eb0809de4bdf20bc51c6132b89716e657561991e79cac8c6ce41a842600e04dd84678ebf91b647d92704a9ff9ff4a7d814224311a64be9d1b94c06f21a450222d68f8b0e2e73159b1e2fcef37ed5c45b881a2150a1ed271f83ab0941a4e00150823f4544f70d7f48a70e76b52569047032743b2eb95c4cf75d8a2e271ecc72b5c9266815da554fad4a874fd3aca44f895c717b8f3e516dfd26a6b235099c02a286af42e76d1386acbbd1477ae40882b860ddf4fb1dace973eb7e9a873d133abf80fba870bfd43bffffb76fdfdf39c86f555836bb42cd57e736ee595aeca4506a02c9f5806f904f4558c8e2e29ff2d8b38cc71c291613595071a021b93e403ffa3b0d90056841ad277e318cceba8fb922bab685886c9915a9bf29c0f0d9ae439ab3af19f9c642d6584d278539ebb02acbbb6e82955a7104c146a838889f932497e006e91c6523b7482e21db5090cf7ca91ccceb2e8584f2d0d93e65fdaeaa11dc5cea3b4cce9ffbe0c98ae26088e7838fbbf0877b39a6ac584a307a85c4c919978b461bd82c33b9c5ae536afee685c14cf3e3415a227d25b1b91b9c3704df4b3c6d1f81f57d0289e1801be3888473b2b5a5d595e71fc378c2a06b52fe1aa0223e7ea3e3dc4072d02055bcc65d747f0f5744a45fd532837682aca3b16251a159c0314782d376cdb4852e0e939075864204257df4a0ab6b3741add1470ae41f48172d92ddd0887bf1415ab400fcb4140a4eaa5397a3a63ef701554603bd945221f7c438b058686f7c5ffb4f98aaa8cd79cb96679d1edabd6a512824d324a611543edd79fcd944e88d36f45821bddb1415b496efd991bb1e5b568472b03973ea198677694bd928edb15a6acda30318d3c11bce48fd31d5a7421877d36cc609c914b5619e1959c9b21f6e2b295aa7910877f5537553a1439f88579f43a2cd2bf5557bbf956a1e6369f91d93374e61057c50246a233682c3ac33ce8196b367dd3f20e8c0190e173128e9e5664416aec3798a7038caaa60136e41b1620954c1488114fd12a639e1beeaf1cf7ad81e10ad7a64bd426ee8ff980b821c125ec4cea21b15fd9064d912197743a6b05c0c009c5b76393ac7059e51fd30a5d7254f892c94555b1a6552c43e8d7f5be2c4573c9f7dd1d52fb9c9d361f727d6e6b60a6420282d901400075362a5363507b7c72ec66c8f780eec0d3d4183bfc3b37827d7a1f864b36d31938ee53340dc7e20291fcb7e1438a77a0819035a78b39d3247fd7aa13a9a4fa0cbc157b49b71c27421f72c8dd99d09a960ef331e63591291c2ca5d2e3abf2754d8b45fd74c8bd8c5a52206e22757e4610e712e1c2c78e3e84a91b88262653d76d281ef60f788df24f9736c03810bc6e666a5cbcc2d7dc88eb96315524bb234ea8d559c336fff8be6277023fc3e2fb00a5242257713951d8d5909eebb18edd7587d14d60b673d183ed61d2833dd02953b889251fca40ffa1dc78235d965b8fb1d1c15f640b51b6196d44763c4050650001a71193738c988b2268e497e48f4cde095321eb885e80ca5d1cf466aa5ed6627f3a786596956f5620b75c13f0ac498aabf2641b5d4e897796db21b075144817b6b0257328b46c00af0b67fe731552e663e48ec752ec715a92f6c50cdcec1492d21051b3086208bdaa9aa2c68671874b731a4431d490e8f3e33fd2757d3d33b7f00624468c9037143503931b52178a40c90e3eb19df102c7eccbcdea3c1955308a6a538011a6d4a83f70f653c1a5698aaa30690ed495dc2d966af24b8fda4b19f610b66afedb90a15ce6afac9460c161a5b6bab57ec8b69bb26b8c83c682f3992cae58208f9cdb2c6cb7442af4510660fd595701c40969099ceb3a88c3fc646a6e4c01b2e0fed9753939d59a659eb9d408e0c94dbc1c107e4966425ed6384127937b9dbbc01088508ef7f7f085464e144c687fe0587621f9f35d7fd1f9c149ba3c3304642e2d81c264a40c0f85030b541576142ffac1096e9c71a385252c8c1490e11f71763976947fe2073695980a427062b6a191dd5a2289badf61db0625bf4f1e88b96550efbc86146b517297e2374c8f100a89009fed1994ad7aa8cd5843d3f208e839b0ff7413eb1a58a15f3b58f38d794bf4950487b1c448ef73228b514ad495ee5f7bdb299532dad24623bd7b86ec5aa027dee23e6f42a767d365ec5eabd1fe45809d47178b06a459d27caadcfecd776cbee7310ba7a14c21e7d00295a8863db36a230a4be1e6121dc505823f9756dd334543bba7a83bd05c63e17b5ccca6e96af5e6acfab607792ea5c6df1456a7d38aa8764489e5a64811375444e75c0658b54b9e3e5aadb37969c4b849102b285f0345dbee3828a3d5e7725264a42837725a55c30247abd2d53416282123c93efe6b6710acf7787b999a80c699eb74f3ab8ee8cf143a7a79e36d1063686473b41962d56fbb7e93c526b8cd58cfffc26588a4a7f26930c8d20385b30d87d646afe8a8e0ed460dc3321d14d56d38860e0b89dadbefa8b972926dd6a22a7ec0fc7fd5ad3bf81aa5d0dbb7fd0aa48f2c37b09b5073ea7bee01fd3bffc70542c1b8421f9bfc32e2f56d0d8b0cfb560187a7129774ce87332d2b3f1fe915b36be974afd54013aac79dcd2170f4ced07de3ee5b3e6b553e2d5eef60279fc03a405aaf6e51b7c4bf8926dd4df06f66653f52a498e583aa194103af00f7d94544f9fa803c4f52f959dd4adc612c56e1393c0adf83ee7839802c1302b01a147723685b7299e1c332690cca9054cd6124f1a963c10a99be845492521f931cbdf4218b8bf853cc1b7426db79db8e5374abfb39ffefa8b7cffae33f460fa4f9e953b4a9aa92b009e870fe5227a9972ed463c7ced3be56aca91a76b0e47130024d12a48950070f4d38f8ba1b1faa74c90c23ce2e9dd35a2e59ae41108eb555f5e7e5dc12515d2c091c3353915dfd45cbadbc0d9175758881135bfef8fb8096e3f7809bf117e6a0223af81537f8ffafb88392fc2962b001eef1ff8d969e464b358ae210398d51d1c5675d3bfdf496ee16e300d4f052180ad4b1fe27bd058761d505f68d83448eb6eb1963f32729154c03a7df6638a8eb1af8912889160a9f2c4ef2150d33b816922b0f3df52cf6a927b0a48705f29ae25d9bd719abb113f5cd397f646d87b78a729c6646523e55e8166fde65125eef6f65df1fe3d81eee93c79d69e0b8c1ceb26c1e5ad5d3f1db62a002fd9d58aaea4d8bc49cde581128286794b2e6031ac58dfcf28db149ed44d23cd9757d1b9da43693f64b6d4824e9ab4931a3433dc31274b32f69908012ccce3a53bd6a95caa02cf074658126e0a1693903d622cb0b0184123fa04d009b3e6aa6a3658882d89bb4f72eb23b27ebe4c11a2deb12adc2e30ab585b6d6a8e7756fa38f7c321576c8d76abe39cd19855a74437464e60698e2f8321e3908603b123aa772436def3a98eff018604c31eaf1ec1a8f7eb536326c272668fb84668aa8fbc8173c6aaca5835b60fcff566dcdd07826db33024ba6352fa42a4f15b7f97f7f84a8d3fbadc6d518d8f81e26a411ac3c55ca1324ac888b22509c9b3a2be58ee5e41806fd5307191c6372a6995dd8dac221b331800685abc75a2b49756c62b55ff779425856089a08348921db3537c6b33a8c147324a2b1c45fc9f0e87cdddbd1d2f0b9a7effc7016b1e39c8db803c98a75a764e4bb8db2fdd7dd11aec9de5a62255ecaccc7a46f560d294103f1f1be6c7854c5541658c9493da57ad3fb94f5fa6eac2718bc8879911cbf51309d0b46b7b1f4a705f6dcf923de239927e6dcd5a49902e464e63b0280bd160fc205afe4c3d0124fa63b8ac762de1f1aa163fba4c998163fad73dd097ec65ceeea7d1401c9389a4ef60998458c93f9cb007a141bb2dac19a3a81a1120ce9b15d39618770c68541c580e7d97c2dc4ecb2140dce75fec381a13510382bf025e56258ec6448e464d07a04f92bad664999f5ac0ba221283355160c7b41b7ae3abb41b0d980bf282e8a26b4d570628be3407b7b56b35d71ed892cae81a6a2f54719ef75353704ce9f288d7451088827a352e45f2fec9d6faf9465e842895e7ff143225baf60bf9ab9d9039553185a1734718e1b745c37db314ba1ec9e867a3156b36778fb95be14638f1ab55003d1f864d4edb575130401ce5dedb6ae5bda698731de7776481ecea251db04e372faa81b511bf0dae10c3add40f42ebd8b562742bf71859b34b9acaf6dc8a1e07857de2729f78e16882e2f355566911d28242e1480124d4f1c5593471e51e415e3eb6dfc0fb130a0532e60603e1d1e28e0f785d430762c428bfc7c0a182b2313d394d93e818b1eeb2422e27d1eb94e1afb4e3d40c93200e08022e492a14f63334f4c5385b697e5d766c3306d2fed976b6015350a7f64eb0e79788bc7838330d0c32dca995e646d874ce82840bf591132e97cb5eef6daeca67ffa66846383b2e73601172eb8f0f9b0628b1896345ab87199e843257b2e9633ebcef8ab802dd0e148f273c255ceb8f6c072c1d8bd45058024654a38b18fe91e0ac9b65b4ea5e51dfc18a5aaa3c6fde14febfcb5bf3ab9f20d699a26636c5c1120bc8ccad95584020952646d54783b1ca623cb304c52080feaa4567bf9c21e5d325b735371fbd181df7f4b62b3c5dfd49b0b8881b716b9e7b113de1013c793fb211d82d140ac5222823544c58ef82c315e781e8ca93a463cf053acc32b03436bea02505e7fc8e0378cde8732e9917acc03060f8d22dad85163cfaba8e2c169bfb31c0ab57782a3b9c7b3a4fa7040e89d598b0f0f5db2572fc038cf1b2746a0059538939dd9ec85d3a3213923d94cbfd2cd9bdb33a51908e31a16277a22710ff9a237485d18fbd091a0e1ea60847ab0b1dcb9de6514673d8f75e1878ef26fd90e14ff5c6ba9e884b600cca7ce1eaa9231c9f341db95a7636c23a8be4db6d5a9512b3dea1297d2215e9533f4ee333c5f233a5ed1384d56da14a271d1847a0c01442475833ec96aa6aa67cfcaef1f0d13240404eec779b14fef832191e6b428d5b32b42b9fdd898c630ba8e6835b583c3345030df8af1874522aa15dff72d1b2a0fcb9b94f85743c7101204eadcb18c0027206b27470c24dac4a3af2c2c04f4aabdd6c62bc4231ae3108e1de91ac4fe00ec36400ba1d0dc46540bcc9821fd0d9db4671b11cee0e5943dca0ad8424c71b50e2112a04bd932ea06a22b183261e7085b224ab743a9e7c61dbe494da53b0fe812e8dbc7ae0bbbf94f9005d8e6d11322c69468b4d3f381b05a535c827d875dc95690f333141141eb07d3d914dc71aa436a3d08a9df838f82b1772a5ec5963d6ae5f31cf5e93e110c3a5923e39e2c8cc1e3ce49caac5ce9043a4ed7f83a7dbdb60dc8502b4b56d64c84b64d3cb83625c4bcf731229c622da6c9b38752808e71d956381c1b08c8b97bbeb1a6c826c1bed6f7265cdc24b563852b731bc50bd1dbb621b69a2a9934b569af8600010d09c33e29116fb0104df0a06cd9f3ef01212b36cb665efddb5d5a37af6af72ba920dbe053a180218ac4eb16d1d5d3042489671f05c9829296c6362769693e427d45efbd090e0179663f6085687f019986144e5439c338e773e4984ff4fd57a44115763b2be234c36fd2b9030fdc443e6b30c11310521997db0df4424a4ba5c51a248e76253d51e6eb9ca691a06e0d0c65b4cb5799caf5a3601f6a0d86600c8ed0dfb4923cd6cfe33c1033a226f1f5a940557d9ac69ade5c6d3192f9f83070a46edc7edcf0cc8a665072f9c163931b858e12d5bd3c920750ac6aa17e9866c79c8d98e07ba5b3582fe3d177959ab9d2bd16fbc55a48f78c9c57c59bb0d3e8ee1bb2037ea7b0d60a63c377c1048e626815c6214c49f7a39caa19cdf94b9fec279268d472113ff2db2a1b06835590908d41fca327ae95561e5a9b6e9e06c92218dee793a5090fe23f885ea2d296b64c213d810c1e666b2f0a1aa3ec343fa376fa0faae10a5f5b4d96e663a6e766cfe12113d8229fac969254e6f0a064bcc0c3511af5a3bc75068569a1b5c20eb3642818523dfc8762de6316a1af6d7aa77be44ffff29565418bba81ca258e1c067547acf49bdc252451c5b642d818acfef71d180cc622eb26431848e11c69757059508495953001be7e328f7beb6eb797b4e7dca919b8a5e9d01396d9b863c2004c9751ac2901ea992dc2c0a830d0122ce721ccb41d1e126a7ed82828c8c2cca71b7a933820782ef06761e9eec375829ba88fb923cb2b598bce3e788554d02143122ea6dff82dc13b16dcacba5aa255668a5f5bac7a61aa752d1fa7fe72c1dc34d414b06503507d27cd720b4111fb1dd1b8128d314f7e76a222073f9bbb7cf1518f9bc15530684038f5f117d55fe4d82c29a35831a4d8f40ba8a3165efeef41938b8e21a7e72fd485f2110d1582b7a4f2d25fbd21c14641613dde2725a43b94e01ba06c4efa696005bc135459141d0730baaa2a87a54b85c7fcb75d234e377a45b96dea0b37e1f437481cb3ff3007e98666ae508663edd9b09f0f39b827f69fa3954e1e47ef983434f14bf8aeeab27323d22710d2ea586647336fd43edd34ea2393cd07787e512cee5b7acd297bdd64f6d6f4002a7e465e307cf780bcfa1a0b771b47cf79b701790345e5f2fde6db19ee8e0f206048f7fec1d09268463e452d51ce475c6995b6ac9020556442cc48cc801e36c9d84532337ec60a543eae89eaf112cc5632af64a477c7f870a1c9f1d002a51f979debdd9a7b8630bb3d8f7b5034f5f61c5495af4cd1d087048f636fb30c6270b7dfbac98febf54f056f2e4c11852c761dd6695d60aa01f2be128fd6575f9240d9260a282acfee688fece556a5415c0de6721c17a9748b601a438aa04d036399de8d72c6e51c592c65e0059c7dc51771f921bc9bacbdeebaf4071370e75736023c404d91a01691a22e3de39aacf3f8ab68f48ce0838714a30661bf8e979086b4c1889ac896ba76462e4436af4a2343bd761fe790abe29ee760fa0f485e02f98a2d6c89e6e4a6d0e06da6da7b8416266054bbeb211d1870732f6959b5af3864f68f54cb5681103f356f4dcdd3f4a26b0cfc3bf4684494a6ce09a3b0833e977a32dc1dcfb49014546e163f4642e190ee9f64cb3f001420b3566e6ecd72329220414ad8dd913afb076fbe2daa68f9c58044fd648bacf7c2024e00a42b2add2148489034c747eca6749ae7b71848b1d465ecbf37849574fd4ead488e0593da743251301bf68b1c5169628435b3370780efdaca79f26b9f4f948db818ea32fe5aaf0da87bf66c56d0fdc64cbe608142b0340108f4ba68f1ac0be60370b13001a8219003061559c206403ec3d6d21c1ef45a3ae68b02ae7183efa37b4a1c8a6e199d14b152c9565a5e694e1c88090d77566ed4b043d1208ebef05216b6047a1f04c6d98be086dc61fe0d6a693a971abea25e0601c40675c1e0f16b16015a24ba7284260df36963a1126e5b0bb741039f31f75e56765b16ae7e8a40403e21bc0bf703bdef2a7a97823ade5d282431efaa14295a6c76015138cbf86c151ab1b4233261b3b2a3c94c5dfa44dfd74661e23572a467d3db5b78b22dd3b49ceff2c8ead771bad0b186e59c043380b5b08684a6601bc1fc821d4e277acfe5c860ac951fd0105384382121319d7745dda553456b6559bac7b85ebe0cb4e6b674479930954687184d6c59507fc41f4833d18398d9c542c7804c338bc308f5d8facb1f4987caa3ede0111ae93fe4d8e0265b7f035df0878b69f902af9c57b7779f6e1caf9efa8826506425e3e6fd3181c8a1e1333df0a0e8073538c38db04d9f8e19bfb2910716a1c38a577b5cf077a90983bb1bacc024a086eb4f18e5b3154540a65ecfc5bbad18cf44b02ec2a0ab9998cd7ad41de05f26367069297cf271ca7e7cf896a56666b97d8869f78037a020f30d72d54e3345aa5ad38572a7166d1923803d7b7613b02614ac89210b8719963e21a6e7dc309390c57a750b6047440bbad0093e6365f2eefbc77e975853abbbc6ef394b5320e52391d8f6eddbc8c682cd3ac4a1a0ef939e9dbdf6ed011356b60764a7180fb272f83ac30b2009603ca5c1f0419e3b74fe6f032a3502de7f11bbf560f8ea0a95a1bb4739e72ed69b637c7a60474daaea00a606d046d30695045721573d002420ac7b65056c1cafc80a92449cf2174d31cd3102f774bd77bc94328bb3b4624810495397e17f7d04e2621837f6fe9db1346af0f0027de8769f382d9e1ad31243260563f4bf9c801475c22afea458ced4f5cc16416f911e01c1c6a77e7b54a57854bb1350cce0e96580c1f35160fe756a33b01097044512474830f1abeea98f1ca6757d1b5176c5aa1b1ca20e8ffcd9ee37fd182512aa05b6de110b34a8ceaf10663a635f6a4d7c1254fd8e68f54f3bb6070a232dabe59d5de3dc1e9dd6bcf2efd1b894449a1da61393ef5b376e237cb49e581557bfca540df434d923ee17d8818335fc330b13748a2473d85e4c47393042f14fb517ff6ff832f3f0f4362ff5d052b691d95fe2c2837bc5c489418ed0ce9f0062286201f07e5ebba3f74e241a01a0279d9f87c5e2f2651442b97400ac1388e5227a76d56f237d880406a9bf2efaa0c83430f8d53b605aebf24e75c339d4787838b85926972fd18ddddeeaee9a80159a52928a3ea89b1a986b0e01ef32341187107ad9bc0957dd060ba06b5be8b2e2fc14a22a2a0c3fd68e8bf14362fb9ddb37fbfb9c1dd8bf914e79fe5c56318973fb0b3991eeaacd52be02ccc3cd84660925f80d3746753366a367acc64d6226011224628ec26a726373f5e202a0943900ccddc68e08beb38a1d3c9cad527941b029f7be0a6c6bd1aee7f248a735de58a8672ca7a623919c0bb2274722134778967eec3472b46309caa4837ffd32692aef9b99fd8fef992928f29618df4a5f2cc3e94ec3042d709817609ac487bc71f5b5e750bc6cb282e9678993842a9d761ae0308db85c160259e93e38d166c3263a8fc9764ac9fa44779834b1433274b34382b9f8322074311d6375c9add7c234f792f79754beda2238d991677750c38f1b50ea5401db6fc2a43ed15f95cfb6aad7317d1750b9f99c759ab961d0b45bdd19366f1c36fc2b0cb07b9f88b2b4cf072822e9e2f5dad64dc52d1aefa05783b51e4486fa7b0a5f42327611a390bd761c7a0f3beb5fe3eb0361a867e03dbfa48dedc53b47016657cd5cdf01abd7f2b43e4bff6b8efd9304894368b93c34f53f8383d1b8942cc939433911827c83e73fbe79d1c9262e2a431265099081d8ba8272e633911fddc7a2d70f0930c6c5e3c878ccab348ce91e0bbe273e8eba14b54ea701e69037d56da49352cf0e57c575f570b23a4b482d4688706602df347f744ead3a51a0cd14abe2bea3e535dd982388e725b7dd8b31c9cf5798a6aa381309a324600416ddc8f94959e55f0fd5492827bf6b5925a68d3b4ddb96e783ebc47fb9ce5db0876ecf10ac3795b060625b7795908da0daaf4587d8837c8fce63a433eb4d73d5ff1f463c98182cc97379bd685d5ae9cd1cb2f62507c5e5d5fa82c560f3aa6f46329140736b261352584017f55d9d65e7fc246d6adc7c45bd587b7cabcf4115871e76eae1c25a0e9e6ff4899614d5413ec61c3880de45a31953b4dc620aff44e40bab8448ceab20c999c3138434955e05eac6339a71236ff411f63ff24a73ed83e1831937fe5b7d5af933b056af41afd706f4d531f006680570fa93d23462f0763f59d864ab781ea8bd722b769612d739df26ffa79973f6c9fcb309ba0ebb3a322cd24008a02988fecfdd4bb9171f966d8abf049ee934336008613e9fd19a63f5c1d03d37cb7dc8bb38a334fbb190d12d05a70a45720f25e09c1c0a7519c86465f5a3506b53c1fa8893a43cede01bf62b138f4d22fc3f65a3d54fe04b8c80a5a0a4a0fa960d52edf80c17b236816bc92ea6c2fd856b83db5d2e1cda42fedc8e915ecf173a3f952580f0d98c09c4120bffeb33ca0b31f6f970547843b3ebf8e7c6a4e0527c4a732731985f4b7018fc34a9aa8c387aec839f1e280406006043b385696b5d21f5f64c952526642416de52f422f1079ae1a5faafd2059886d91d61c2efd3e783ebcede214950cdfa502015f378b284bf23c1e083e14a0ef5db7330639fa2793f031d8cafeb3f532b74ac924b34bf589162d0b09ccbfc8a6e281ed69337e5e457b3c02c794bf5e5af05cbcc3b263044ae66bdcc46a1dfe850ca8b1f02492266a20f74e06c6432a3f27cb4b553642b881c22cdfbe6c7c047e04896bd66da70034aa0745b2cc1dd0265a68d0848133e5281045fb09356270f8a997c71566a7ad827337147d293010c1d1e2ab0cfcc70e178ffa753c31afd6b085373b625814698e3aa36d2077074bae981dd90c4cee5567c80b2acea0153236e9960d0020f0406049ad79872fdfc36809fbee9a630808a3a1860808cb2ec4d3125df235221d2bbda84bad2b5273dbc92c8207174991479a9795601c44214fcbe26ed1732d4f7fb1df1fb6968b1a10c8b2a1284360f38dc85aa4a951f3186aceeb8440175b684cd18450719b80d90302a281f169c2dcfa5c431c708aa7ed62508fd42b3f05b050d4ed6122795338f3fbfa058fe793505736d654336fa102ba9c36be139d1ba47638021e18a4d57c943b3fdcabad10d39f6cfee3e542daab3cea33069a864052a36f891071d24baf3b0afb03266267f031296c880bb97d510d18d968ad01618663b35aa6afc4efc5c89f2d49c405098c3352eea455c5737775aa77d7749f3f90bcf0b8d9514e4dd7c01c60c45e306dce382f90e7e3ef1ba9e19a8127a5674fce67fc304a1d1e331453777b7961e8c9aee6d7a9286385b9e109bc7b3e0618318e33cced5728b969fe725068cc11f33b8d9bd0cd9c6887e8ee0244c5de7ef42f686a2334ecd122ef454006502833f7a13c5f7617a5a2903f28210dd1dd28ec2cae6dc64c49a6969176d2504112ab202ce626b87eaba17bc6e7123d4295eaf86aa8d8533ad2188e634d304a6dc54f6d225ccb1bfdb2d436a55a651a932901343415d5934b6f94ad4a0475e1b7744355da965f9ca4632dbc6e808555ae539176350108b700dd88b8bf5729fcea73292dc21b37246ff86fb9919495c9dc008fb51266942889227f1268f3ee80cb1e52c23d3a3698ba504911e62ffafa4e74fbda115c9e182c39cd77fa05f0624389429eec5afddefb0c3b32c5e9fd3d030cc96272cd21cc8fef724a92963ac923994517d0cd7053f437f0459a72aa904122cf403fd4e0f60bb4aa2867e8458b64a7aba0119a602e64415440ec1a93c5feac251b2c5011c23edf4f651a1ee999163054de1bd2a00f9d0d313249657be2e2445684141bb87e36d97426a468034f40fdc16b677bbc4ac8495e86db5b4674f1bad8a81003e777a0ea53375ccb9eb95aa7138cd3611e47c7e060b6dd8690527f11dd8526b5c33c3a6d760e5e7455ea3b5639b337cb1e4a32262fed29197baeb0bf5efa1f7b7a06a0347bc0c0ba367b21b85187a9f1de597539b36a42b0c69dfe93b84c1c568aa79a7d936fde10cf7ede33ba310e1eeeed32e341e46ba662d8fef48ace32c68d4bcb1abcaf411cc74a32ffe3ae13d16b5c74376e072ac27e0264b9ca08150ee3b51aa5db15a7b27c09d3b709f71096a86b17c81d0d250ba6ec9cda4fd22d952edff6c48c2b487c33277e20c11dffb0681a449d99c92a04d6a6d175e14518adf12c35f693ff1cc85701b74bd93dba7df564057bf6e9371ac632ead39046406edd3aa581edbfd505a2b97b5b85c97ad10c9b7257003a6039685c4e67710d22576951a980482fca628744803cdb29418e679d9868116cc762af9c3d5368b407b4af1ee5321e616235ccc86859598a1864b4673f6f9170004ca65e0959cb466b280390f50b366d1bcce55541cc28cf106ee080a03ee1380b715862e23cf2aa3ae40c62594cc3e01a7bc63a1a87698b4294c77c8371ded8d41e3b6c39809aed23e7ee33943d6f18d5ebaa60bc4c4f0fd70965c1f78323a383c2765feef5db153b4620b7fa1a501e7e4495783098b009f43eb88097ace54c39e1d4063ef930d869faf163ba0050f4af9ffa950a5c7331f37ad6bd3066c442c7795cfc3357a0ba0af30aaef2d42ebbe3709c3b2bf97ef87d269fcac0e4be7797d44d4706bbc34e03fd4890919c4541fc01051f9dca84182a08bcbf09610c96c0671ed723bd3bb620c8ee8686c0af9d5121ec148b2c060bbdfc3fdd40ae36b1aee50528cbe7f2e03b29dff2f658123556cb733e121f0f4ddf973840eeb5a48a15e057f4118e08ca62f33256798babf088fb0ba88d65abd1870e5c2172d93a68bf8a0e84a3cf864743fef837164f69a4d0cbf5ab691bfb8baac3f1f8a6c1c9dfb694e826e447c972004ea4f70f7e9901cbc5e5c776a9e7763a914a53b37f06bd0483b42b8844c3f32a37d590ed6a411c8b38270b00c6affdcf49269a0913e684683425dc7612502f252bb59a7e06da2c0400d6ad983186cb989f33c921596da82c83cf14c5a6cc333b00288f9e319f1835c3e1ff2f81faf54bb4588ad7e93f945269c8c13ee1bf265a192868a8a5898389e2cceee0ce1775206e53b40c8bafdffa67f4104c3148de1e55bd27a0c1ce0be10d36a8d2f2da3420367e48f2c3624b3a61828bad2440e5253122ef7d724bcd0685294f69a971d0c5293269bb63ac3a6f44f5201c310603245a048c6fdd8409760e43620e25ddc773f5672e84d29b55a6953fdf3d4c992c2c9073ca28fce96270475a2ea625ed430586b10fdfde7df3e2846580ee602d5a0ce80fb4a6007426a7c41e3821970f46d5d136fe8732524b47ac43176aa6b9b2660a5f3cf1c035e25f3335d4893cfaf4c2833a63d772852511a643fa692110d109e341ea6ac905880c8f24b26be3916545bf703f128ba8df31625356b5243cf4358caa42356845447f3553ef7cd90288b06271025c9069cb460f4a2d153042e46ad86944f40310b3327ada8e1e59025b5b277765f5de50e03a430f04a3ff1e2b03a903ef941aa7443420913b82e3cc823860dc8333e24d2d246d8a5dfd86142a21bbd03941ae2a5972a2a3433abb5a4d76feef3a555ee35797a27d1e59b1a88e6bdf6219695f0c8e3cde289c38da5feeb18d5f09482f9adef929164ea421f55f0d72db90924af2f39e7f65f2383d1c9ece26054b259554757720b7e78fa4b7590062abb178d4531cc1e418d6b09e0237907d39306ebfac0ce80c68c8fb570feb0f89638ae6d5c1933f8f5e34e96c5180d6d31d9ff37d7c5ee4a623372ffecf3f6a0a201c386b50a3e8ebe7f5c4bcefcfe56d640303769e725816013603f68f114fa4e2516a2b15b977afb053b440afdb272193584c1e27f1083ff4683bca4ad502935f1aeaff0b28e4b3fcb795a4b042f588c63c06d51c152921324a3af2228a17483c28e94cd831c5474926459d55093d491b482a44101aead55f8d33b7940f63cdc88bfb80104640debfef2ade4d1c7af31aabca285757dd2c285dcfa1582c0eab17c44f88960059460a4546c31f778dc7ad33feb7e4a157f52bc2a6f81006610b20f5d1d3a0df3fa484f4e0e052815d7903c4002c8171a6679e637ecdd04203932640853f386e353240792143cfe1750578f570eaf751bec2e4d7513841040c31d2030149ee76e3e5e877fb20eadb9ece9ff54404a47ff524832a7d883a882682193fdab247c8b1bf5cc854c0789e6df2b88c512dffa256a9c76d660425ac4a62a903ebe14c3787236fa1137189bde13829089e1e3b5643bf91c36b70869725406c6135b078ab06609280ea133e184f59fd765d9e7762708d2caf7d2fde561641f0257bd041e2af58bd73c010a666a87ba60075ca335cada22b7fccb3806211c0b13e4d8f57428972db4396a90e9dcaafe44b0c3aeeee97e82d3fcd12f1280aedbb82c5bee64bd49d93b44d21ea7a6019276889392d77a85b8f67a8f424d84a25dd6a8335e9abd9a82f3ceed6db450fca6914b2f87e96b69fd0bd73ac0a76446256ab18e1f959935a388d4a2a48463fc1bb9cb0e2ac089de40daf8d66f16730fdafc09d707841fb0d51f69aa1a38e0116a3695c2678427c4877836dafe5c323493324b8186081c3b95142fd36fd1caecc8468253e39b3dac0247317663b15630005a4b89f8878cd5fd86d222bfe46e7bd223bb0a305cb5b20db4c3dba90132a8136282b6e980244ebc1e7e6961f361df6aa3d3f2540292144b26e2e83f9b5617bd010c46dffa279fc245f103301479030d4f65659e716baf444ff0bfc2b835477a0a75c7ec581e357d19bd899a35239a7e17a6d847d4561f24307346bbd3929dcdfd41b427feeb9628aa46e1894fe51b8e546f9517d380b6e5ce565b4dd021ec6c2244a07c00d2254959cbfa7b10c931768d876017e9014ce447af3d899bf5fa582932225c0d2caacf57b1013cc50142f246f9be1482b0ff7636e788108da1c6d44d8d0efbd7326e065146675ddca92b8c5d7145d7e4335b30ef803a6500c01a70cebaaf74514c2b7ef46e4feb0bcc2ca61e866a83d3177a844aefc233f77476ee0ed54af9c8333234dde25b4a10c0b089a8fe372adfae938c638fa15cc6b7048c42cc12098173e236020e0378faa429fcef7e32546a64e3521fe1fa5e9403b9bc1d794c10afce2149b9765d30e7ab373ba64b8232d8a6e3b788f031dc67418e8fb251922cf6c185ebcc2c1d6238857a336b74273476beac83d6798e670884e769941409507037c38c761fd5b9d1b27c18426899f52b7eafc8f287e9ccb915d444bca85dd009d45a98cd554e9486e2bae4db131c4c4ad2f17a3d5dbdac9b14fb2c32e0d90334b7e3349d8431648a431c5a0dbcc120f629dc44c04c8f64a4b4254e6cf73c6d78a23c8a9252c5521df1b815622af4fa6f88b90ca198fe61eb973c76dd1f913bb20cbf185f1394d5baabe1da731d46d081ceab22f65ddf3b9094d744fe17e5bd972db70fcc951bd2f9b3eb03175cdf01cb923be634d73f77581232562fb009a416ab82c9aa337269051aa8e3fe9c9aadabb76fb451e4028b0424ec2f296972c3e7be527919477d39245849720d4fdc365f935a1eed9f04bda2054e4dc27e5defb022c4ba312ce74f8cc2e911c8fc2e6bdf1dcb96a6a609b19df494f7a5f05d7e83242492eaeb2f88186489855c1dd107ca5284bf464681791d61a1410858838e4901b95effd66fb2de3aa8508a23cf5d3c7b0760ae455ee845c17265fc0a1ca1891d91359d066fb7bbfa8b711eb54303f2460db57848d00da1739a6b6d97915913dd6fd2b29635d916c6d6d2fc9cbee2357c73795cf9245a4506dd8211624c252091301835fb673c785836c3f4b37cf653eb40825f4653a17eb0f41f52c1da4e718f75f0405ddeaca05d3cbf3c55e806710c06f933f941bf98e34572410c87f4a001cdf9cf35195e937e7ca1279dae5abfd91c55b461a22c34c2a3babf8c628def11296adf17433700790b08ec670f39ddab468b975879618eb1f450fcc5be481304a624aa577d017c4e148616f41af206f5e24e80e3738f5b5677690f39820cb4c86b440dbe3ef7e896950ee5b7300c634b7326a3394f1710c40837636c0376fcfc2bc0dc906f156fd985a8e06e095a41d1b079e005d8b7aff0c5098e08d926d50a68478d675d0f5ce475a1ab4691b4c23235f60d8807e706405d23a98c5c2090a8599a356dadfeb72a467f4aec8cabdd03e0be341f897955f3fdd477e9d9ca5a24f9a65a08e396b59af2a3d944ef534289190b0727e7e508e5b743556493e6f30bcc3ecd7a172c9f1611b2194658930755fc40a844d831aec5578bdd631229cdcffba1be173356989a4906319547ba828c6276936e637417e0d1b50d15e91ac3980915a2b9da52481ccb1ff30959af698e3178fc1e099928e0d6297228546be38ba4f7ad9de4e9db74ce01d11b2ed119277bdf67a59a482a02782ddd29f90fee0a33461056097df6d0266bcfdceeb4ae6ca64f8e362b7a7450b097fbd3a6778bcef0883833bc19208ba82f93e5c097f07276d6133756b499958456e657d17a3b2a0ed8203d6898c98cefdb727c0e5c544f7399a0f71ade0340477b277d69317cc32b86fcdea647de1b41179ad7b73850c03756c044b11c5082d3330079854feb80de586b55c33c6154e674e42462066e282d9f698309b16903e883fff6305eeafda7493768c94ed7cabb8b00950e961388e02ed3bf70c837f337f27e08ad53bbbc453694687a3f3c6c0894f1f79094b4d3535d228c66242866329d77cb65f37d0434cd3f5fd4efe8fea0c9632a96346d01c8ddf54c1a3f08e4d6e8cf9a554cae4b48a62b2406eaf421650334586f6fd30ca7328cbfca26b9f097d482982a9cbe33ed6d68723aa0fbafbe3276171fc9bd0c7ac5975219aa227522bdec3a2955ca1118ecbd655aec15a41926dff529c24d74d507e04f31ec816049412a85c43ceb2e839c9546a4edfa9b29c9284b85265a63b08038d2d7072d8141d7db1360fba55f1efc9544d9e7e9d19039e3b38b822daf71af5540b2dce2700523ee8526fefcb07962bf1405575b96e271cbb33a80061c5a2a176d26fd87e2f00c7ca58374306435e6aa2837e308a0b03833edf779e0c40fc3127bf78bfbe54a217be91f04ac0fb4720565ecee2e7f5187a7581b9a77caa66f40c53941f755dbdca9d20d4bbfc9eedc80ac4f9d79a651da8655155f4494a6015f9ef3c19bf688e0ab1283d64a14990e72bf1cab9a428f4a897f8ea1cb5ea4346a9467c4ea235c96c2252e03b2beb6a71769ead85b89debd2da0fecf80534f45acbd4e541d93229ed0024528defb1e6a44e3decdd32c7b58ec27bd249353af3c77bb9490ca0095c8ce4e3b4fa3ac6757189e62aec9a4818716188e936631732f71de3d6b23d05b036fc23b3c690914374a72edb7fd93a53585fc5c31c1d511903a82d03c9f01d4764c0fa5abe62432aac83c6ae199f50b1ab41fb20a73da90b4b3b118b124e0dc67f7fe4a67e9b5b41e4e98508be69195c0535b70b6d27766ce2000cb9d6c821b7719761da9d06353e5524157bda0ff027e2646c7bdc8a0664c82a10b367345ed2e49281dd1e3530c28e6635a695c856f4c5d426b141cbd7e547a51a1ad8903a42aa7a12fe6ef44a3754c09b5445af2d961dc5d3a6528c9e5293e37c413cf42d11acf69eb9b6b3afb41044fb3edff4635b86130697d1d4ccdac34415aa237658f29ae077f4091df8de88ab68b1f032c9244c730e47fada65955ae76ed710e54aebd288914d88268f080ae7549136fb4e0296b7a7705046b9c1b973b3df60563d1b829aaf4d8fbdcf93f364edad7d910a1e1d278d86bc8ea1d0ed7ad834389c06a48026e102dca764cb35119882584c5a4746d646fa8ff25f804774ee8ede4c19a63e0631833505770ba4be570ee01d954ac8e6aac90ae93b12d3bbb99fcba731ba60c30eb90fc0355993de0c52fcc0855261c53c68ac464cd7b5a4f05e76e738e42450b89371e84be71524592d6a27bf1c199561c117c8dbbf1aa32871fb5d6ca724beb1e1f072c6915dfa3915e7796b79b8f7c868886bd066870797092d1d726c803cdcda1258c09d737ae0ddaf0075779ec080506d90158b1fc769e776402510dbab77dcd532bd782900e63226b08025e7fb85ef87716bef3153ab87161d5f8545a0dd93ddf540bc28b54c609f05d8e1ace86833769a85589476cf5245484f793dc3c18519ccddcad26ba21cc1fc3e86f584d2fd8ebbdb09be7081cb06d45df18bfd3efbbfff4c2464e5658cc4fba51e8522e8eb21dd4d169e09205308e441ffef7e4b00db4c3aaa5383274af98da2cc43a97da35d6b1ddccb417fc25d5c6542e297a002f61f4b43611007d00b8460f46a9a713490c4f641fab911ab77c5462ba2d4553cb1aac66e1b3ef3c3447542a71a67eb99c7ffde9bf00652fdbc500935687d42dc20c4298180d23467ebe59502b9a1847e516f6d8eae50f5f4b22c198f912e6e17968e790f2c00be75d47df3022b5c19fcadff3340af55454cfdae68d9f43466227931aaae40ac069aa9edb2ff7c4b3d172b9a1c48a7b3282677bb5d7693cf93965963330f0a34dc463666063e9d4597e01a378e6d98bbefd6b7581753c307c6eda4a826a7fc56ff13637807d93051a1f2cf4901b85d84036fcae7479f4b4845b9cb22f6376d1579036accf2514d94a46d3dd254c3cdbb9065fb6d38e00569861f6c24379d1bf295f374aad51094d0534face0ab20b641ec01065c5c2056cc712f21537db52bd7b4d1f15756898af2d28062d116a561f7f008f7b71e1bf9f9823fdfcd6f68d0a3e0a1111f343f765bd368512d647cb34c5c5ff7ea6202485d7442eff06ba800a481d8d796571c17c32fad1a5c73520b6c3276ce8f433674640a7d7edb05a8885a1ac8f8c15f843a9bbda97e24765d09ab862d68401f70a6a0ba03ec48d5edda90e4da248923887d13a71c832caab8861d66c4832662d803f351f816c6ba0614fff08ec9e3cff72ba557c39fd3b91657e02e149ee50c653b21c8782410306ffea9ea19c5f08077d0ba5dbc0c98cdd5a1c5c142d90c5ea91a2917178aebf13c0300d537986946b5d2fe720a378c90a06aa8b7fd195038a54dacff55f21f2a2f772d38a1643a941c5b7462ffcf0833dc5a7b63cbd0bc121a69c6746483e445cfc1aa776dd6f7ed1621b2f2759bce02f64a422010713313e42b43a904eb0a858d4b72aa46f8eb648d1d4b91b57eefbc49e9dab98754d15789a4d722dbeb096f96f64367282e63f3df3aed9d067777323f15fc033d7e2873c1e66273a7a57c48f339bbdc2df54cb4e7676c6c08dab624dfd5bdf679ce713e4c3e588ef712dfe5c5d71f57cc510689506df94d2395d8252252583ae6ea6e0a99cc07526ddc0d9366629b88ea80cdffaf1bed40797186a54ea5502e6e3d0c72828eea6d8b5cfe0a57f4b100f707c72071afb4ca40420c6071e21a0347c52c16b7e54d0eeb69a28e37dce912f4bf680730861c30ae4ecae2cd548bfa1e589063a9743ad7b83da768add27ac6ae6752a9b8b1430debd06547ad4876fb727dc8472a86697771dc8fcec6aa215675b4c08a1c13bcfa6613e7a62cdf7187466c716f844c7d2e77df3076eede220f847b1cabc06b0484865034936ba50d73c2e178295fea13b48a86c312fb9722de9050c306b376abf9f2305d99c47b11a72daa7a579c888c5ebef39e9d1da108444b3e3e3f4cf6a8f65e338eee54c0e7c800a4bb2f3a20403c0cb0c1cf7cfb0df22b5802a08f26cdfb9488021bffd692230d38950ec493a9aa1d54727bdc37f1624b008ece9c105f2256c7c029e12e6897df9bf7237fb3f13c00e25d37c2e91bdc5b3a909643e3fb824979f4829051f2e562980500b6b0446ecae8a12bf64ea02e7e0831e8340951b444079ee7d4c67cce9c3c0431125ed21c8d1940590d780c17c445742d7805c4464c2166d97e630e5078e1b4597fdcca82538253dca8d4af7e4d7d76b81abbbeb7d67511a5d844e99af2c39333bec0e4727353e6ad88876c44710f0a3ca17cfab770c6c38a53bbd4216e8c8c4d3e6e3427a9899485a89aaff82b1ec7ff4f4cd0cc1893aba1bb100dcfb2755152ed610e4c92f3f1fb0951f90735207805f31bebbf34e97e147c4a46da2f0a495989fa98fb1c0586749dfcf56f5aaf964fa289734818c8f9311d635801076f5669f41d0b5c5fa189ca7791c2dd895b77f3b584cddf58d4f21834785f7a27009566f9236cf59ba802c6e0f6885ff3a48e5582168d60da03b0a61bacde10cb5313470125d009c8a4074357941cf07a1126ee74eebfba49e3f93f7fa4177420399f3d4e4184c2455c216b33d88c4bcb4be963d22d5ecd59630003d98ed5c19b5c746a93a3bcec23c306c004aa001ac5d4448e06bded9f88e70f1cd21876e7d15e2a26eadc9c19e8f5daf7eb80f9b934336e2308f36dfbd7e372b8f1e2c595a9fd5a5bf202faddc2efa44c7c8045e784015f1fe282a6869a538c84b494096a71749b0a21e4d2a6f3ac34b38ee7db6467a6e8023e5e65679afb0ab2e146d7f2ac9dee139073e682c6a52514c7dfe27793a8bc6a6caa982df9687d9ea363184f9096d80ebb5b3872bec5d80081ab60e9c9dc8a2a53c3f5407b789b5716e80dcff10dc50d003e3f4d80aaf97601814b029dde21a0380c5d65a08117fe5051fad5dc368525bfb3f22ce5a8d0544e2057335dba3c4ef01d5221f9c6740f1d061e5c72fc19458684f77b9375cced1c1618ea0d323c5aad89b4b91177191cf8dd7c5e654756365464d24dd7116cf9ef4c929c9a96675271d968cc39ab1242c944acc99c6b4c2de33d5192a2559fa8965665a1f0f3274bae5457b887924577631cffbf00228f09fa4db83d9c3d64a690c9a2697450e8dd33f07356e6364fb7aa627914d0ba3924c58d13ff834fc89941de4426f339d32694038da524fd494c6aa7553506972e6fb905d8ee02d89a24119f93f8094c7df36483c80f9704f60edc8ee02a9768260d364fec1c25d1f48d28aafb80fc0f8b3ecfd89858bb81805760e6373f57e3a95ade4b41ed5b7bf614cd5dcb3a6acc289ad2882af54a4c0aebc2c65267610e3e5e8fa2e18e6d21f82feffbe6565f06e705b34c3fc289067ea2a343953921d13d6ff6837fabfbe5aa11dc1a0e6ae6b4c908549c20f6e54cafee940ec62f63163662d93ea6712d5b657fbd5eafca0426740cec7f59aaa3ac0a4fca7e4434306f7f5a174695beb0bf58c7da2e8c8080f68a22bc9137b3be54174aee4174d2a9b894274bb35e4e2b7cb22e97e80ddf730cc67e4e338c29e0c110454d58542e9b9bca1077862f325e18918a3e2e3240298d2e4b6ce305becd9f063913cff6dbdd536f0a93a6bee54d0493de3b8458d3ca0ac44aa7a61e7ee4d6ba2c1896820258594b0f50ae2678714d86c63347a8d181c0f18d3858bfe5ac03e87bbf74ccc84d081942d61455cdbe26c8564b45c91f9636ef9af43bc11d4706886cd0ef8a6099386b331cc97bd9ddc27878c0ee6d4fcc6687368c896d936b1179e6a197275d30a13a4bbcc5dc597a1ce7e89c3fb228b892c7e3ff1bd899a162cd3890469b2f126be41642160bc49703ab1f7d640e9188fae12c3243724ac0e6db61ca13472c346de537a994847f2a92ad4124d86b947d6ab8de29581d84b5652a8a676f08b36c54a76c47a750ed8c782eab8c6ec1194d7e96978b39c6af33154e9bd0f73623baa582153cea0645f113adc2d92cd129cd1cb48be18ed4b575a411117439d16a5757036838b1167c6d07e4214b9d2895d964455d751cd492e221075b758bb6ec2c38af0d1562618bf526425e52f9559a85481278bd452be0018a56cfc2aa2fcdadce3856dec184c092c53c4db3b0470ef05bf4ff5f994362f85691a989a622ba10dc2c0b8ac6baa71554a23c70b4f401a7a52f8b55b228a01abbae5dc94956c39f3dc62d72d928053e98c67c0cdc4c9b1baf62c255be7c2e26942cae28d5dde63761e339cc7dac69809211130e8957f6a04e8330fab834c95687575c742fd235c4cb9f5a3322714c5e31787731cc61a81855f560f198ca5bd19e76c4893d2c7d58f28f66eaf9f26ffa9524321d7dbd4cf2b9a001b40eeb664b82473e0d6f62cb9b01f22af20ef76ba4014b79767d7d026d512bacd7369c20275af2723824b842fa83ec1902eccd84574109acbac54f7b87e625fdc8c7e5470898cf283e0699ccdba969432297fa85c118a09aa0ef6615d0ead8e6a543d636ac0ab30f6e8fad3d349963060933be44e8344626661f09285f574924c8c1ecf47e8735fdf70bc880161d0c3ea35b68ffa95a178313af9a290cba94161fc991f63a910b5bfc2f09935e17e453f4a077cc26861f15257bfdd7e0bc700ae484fc8683974e191426d25511606b585f51505eaf4aa214e45aa9f23644c53836a807aff681e3b89a9e081e48c99873cdb50cf3223eb43fbae8ed212ca8ea7d238b3c82cdd1c16f67f1e18fb96fae51321494f0cb7ee9d8b3f8ad5777e5eb1bc512ac9174e3d6d2fbe2fd74f43e9766299a650c9af484fef5144d94320f7970a8edf63bb3e24360f1027165bbb89f071614bacf4a36384c7408dc821657904e158355303007ff30587babc78e2dc232795fb357fe37aef6310e8e735fed0eb708f084f122dac0b05f77574d561da0426612cf26a0a41b1ba1a66d26c47d129559158ac833911be8db471593f15d17660f287ffd7940443eb946f319023d2680fd4f3ef1558b8240d5b1f865d695ae17fb84cc292cb17309d57eac2adfab0521cdcd108005adf397bed55ba18129c10d7ce2b3078e261afd611902dc0dc4c2105c5242e4b37d8d6de5dc30bba6b996568479a7a88947a568297f510769e23389817be467abd928679ec4713b2810c268d8b5a95c103cc099ac2907d580c6a68b4a9bffc4bbd10f0de57079443faa8d5aaf114c7e67a4cf9782147feeb332ff7cbc2ea27d45517593d66fe05bf93c59164c10997041978a7492deca509c969186fcb023ec3bc3f626f7df2ec3851f5a5e566fcc547c0ac8f990567ed192b75b5a0f999d4f1f328838d85d41b572486f6ea6e30e3243c059efe1411b5d8cbeaa96697315785cab51e549323943d1bc81427082cdf0e80c5a3edeeeafe4b2a2b778782ddd53d9f358640cd18b904bf62f42da9cf53ae3637948fa2d9404909fe7127ee7209d6d8940f108c62c32a306923a99eb5dc8c65ad0792678e75f38517109fac98bab4e1e18e46a7a128bdb1250734caeb583abace502202a64179a6763d8201556d13264b02539304025231dcc627adf386595d45dbb38b84fc7bca48a7deb4e80f2d28bb875c43d5cbdbe0548500e641e76e08e0b4952815bce84da55f5e59c3c7eeeebc44c8e54f45c20852a087b7a4d4b9437b0b24b3c63cc501bf1279ace72a59f4df3b05d243645de9a454f6c9a9161b3bcd55a4a3b00458321c6224143bb36f0ed8ac5d488482223d0d26b11cf09afc12d17505c78a215f1515b9ad3e9612deec4e984bd8a96476e5169339d3992839cf2b1a625295840b911b3f279c7bb75473ede1228098445a511cd2d176c2fd73fafad29e966a270154162562568b137d7ba3363352ebad4fa221a308e65f792fafc5cd793d41066ab53d38cc07fed0ec5d4cbdc7ad10fb5d9f12f6a91ac54b9496cc067631a8dc4ce1e743674654f7d3182f63251b8090649a59b5d963ec42384d7d9a90c2ca869bd2bf92f8f24b324a9e438323abf4d385bb5340f16fb78c9fa102d3c9a55056294051efa4c40cca76bc3aec7195f0ffd9b8ba66aa02dfcb6be167304b7fe4d46ceda495f9667a8424254ca26ac4a9c60ebd41c30443524a7310de2d07448a88a840476507760d52cb8ce891f3be3846873fe2f48c4f1036f407d06c4421d869f753763253a93bffab13e5f302cb3a889b53fc01c3f96c06702bf6637bf610b81a5c8e3ec2eed2424e210fd6698dc14a09b047a152443bddcf4b2c11b2aef5236974a6b91f379052b0fe7857cafd649e67fa372051592303c154470a67c165992d3937c380681296fc082266623621bbffc11f44359dfddec394bde965deab434eae74219e6bd618ff1eac0fea44048cdb8387b23d0df3a8f069e1cd6938e207fd0011ae5de26cc12def607dbb7a1a7c170630f5654b49d1c9730d89f148f765664553b6259e1bf61dd229fd23b58a9339aa1ef733bba60b43bf97c1800d83de175bdae1445ec27edc10bf3e52047ee5ba886936d3ff1856794009af7b22d3d44288ce37e42504d9e4c2cf73826b4d0780b42f41bd28be5036e578c3423174c21a7dbcabd363797ea7c774358c45e00ed6e47dc42922b4b0369b5c311b5f9d5a333b3995ae30b20c352d087a1635c07663fd43e2f1901dccd245be698d1a71991c27056f242b1dbd3e65ce888503fece25f71f571b33c4092cb4321f39a224586faa3e3a41c81febc0f6e267f9186c7611c8dab58eeced132eb68407a2e49745e3704b16a0e97be421c5d6c1b739b20245c1cfd3fd84d9c6f41b4c97a27815afd19406f4e418afe4b10b7d447d59df6b4c0f1480b2becd9f990554dfe2c907e31f72ac8bfd456b3bd7eeb7220512be95688d42631615f26e8fb63f397a2d5ebfc357a9048a3ed3990252928e178eb8c86570719190df1d45cf06ffd050777a06c6eb183d0dddd2279666de26555862da3a50efe2aac934d27cb25b10018e6b63505f6ea0a38e076fe5fa0e47770c94f39a3e34351eaa699ca67147ede40ab120720708f661011d3adfc227890865c02e48ee89d306cb6a1e036d495d5b9755019317dd8f117af10f57233ef0144577450bd78ad2bb0b5e6e97c2bf4ecd18fa2187bcd1266641dfa307e8102a47626d1e1d24ae3197484c2578be451a750ac222363b4d943806d06b74adce5591ad3b029b804b60714cc92b5d0457a9a6d33c8458a3ac15d126bb082c0586d732dcad3b5a996ce15d776b1574ff95601999d9aa01430574c365da2920aa9bb81cf7c086832dd3a709dc67ad7a44744b868b20a6f38dd60d281208eadbdc525f0826a56ec972d9622cfac38bacc1bb2a30fd34afe2f241064297ee42f9cfad9ee6bb76aa273bb87302e9e1e92a8e562ad7d830b892f6022973b952a5a40ba601a1e9b4b2a9b1b652e65ff4167a3ef924c82c89b1c35753c2aea9fa89d256c0bb5197fe1e94746df516ffffe398a40cea576ef8ee65f4a841b7c843244adbdbf9e97140f7652a9374384b367d269970120b82b3b00c6cccfe8fd4d059c0631987c67a96c38b35ed2174fbf7092f7d7f507585ba15f92ca69a008e263e995e860c6163ee6f916f7bf70dd434f1e9e1013c61566d3ded13de69bbf7eee208e04938a704c9072b6902946fa600cc47901fb0d58e97298df7d549d53de786c7d102a1b9417a00d6a671d0c2a3801043c4d722acc061e5b49ab945c25ba6895432de04b18502a4b5cc2f81447a679075ef0ad4cabd93e32db55323462633d761d2f2fb7669e110a80d31a492fdd65fabc2542c6e3655f7891bd4808840f80f662f5e93065b2188ec506603f407b0118085ca953da5bb07b529ede22b2037d242409f3a670cfe46740f8846293ea0264ec299d8fa2996d6d5802940ebadfbd7d640c2ca4bb973027a16b9d7346d8d883737702e58017cf1083cb056cff6d26e86e823749924c50fe0d3c260760ef1c3340ccaba82bb3543025dad04f85486312628128da7103aa1f76dd0b58644bcff4b76f99eef8f992a4ca838ba8a6b6ae9712198c28839e08b066cf595860cd11c1ab245f617a415cdfa648968facdd64b10da34c4ae82dbf68e0753e473517cc91523d0e06544484afe607262cbfed93c4cd81bb7496c9c8fea54e6152fa4cdd079382a06f05461aea5546ee613df7c3fe782e54c23ca99786ea32df722a40e1bcdabf1af7033772e5ee9ff528d29f4ecf896ed321719ca419f70fa163d660b39d4566249ec164e46d5e07b014fa63a8650aa86b6d69df07405a36b10c36cbd37674152e446725c37c5b34a8550ba6e844af46c486fd1b37ab00291e2effba3ad4aba1360a9cd9d685780150f1282d5598184ce4c3bc79f260f0cd370ea327bf24e570c1abba431a9b575d8dff9bfc1f7a2225873f6654514ad470082d3c10d92dbf8b6474b67e0035f43d318867fc575686525389d10ccd3e9430e608d5fe25fdef03cd1a9a8a3945e6ad2183204435a933bebbf699246ffc23d27a3638f087aad83b5d2172495306178c9b3d59e450f827aec036a19f59ff74e39c324ef2663292b8e392bde723cd194490795040b5be2ff072f44645bf70c48479344a046f999a57f12771adf2d1a63c1f36048b480edb2411fabaef46117134333b8910af5072bbf0e4d96280aa5ee49b97d55f22be5884fb42fcf22210c3f5121761ffe404c09c9fcdb60ae872166caa45a4a32658c0c2017618673f016ba0c62848b4e2f808091ca55f45e3cc691f104ed76e0f9406f7104cc00f5bd92e160310b2c87bc936c5a3c62bdfd14e2ae915f41d3332c0c8701fe0449695d50e6d5306649fd7c29bb19cedd54dd3dcacdd90f73eac45562a782c7ee683fadd7eaa4f7313a5730a6ed78cfa181c2f6aaf943df216ecb4fff2716b06f171d720f6c18f0098efa67090ccbe827cdd87a86117af93239e8900a65e5e20e0a61286700d2b3d4071a90a8cf208dade265a74672cce1a9813906e36b28cd22205b9d035740e4618558b3d0391b86c7cb34df855f1934dc326531b2ce81752f55ef9bb8f50ee5d3969023b78b146c229725e41fbcf504a8885d4bb790c5c99fc9ca37bb219d11b1dd90b299283bd9dcc3bdcd20dcd97d0c043dd5d7a4c4a639874e98e1577d9622205905c5bc58ee255b95ccb32097e16bc1f48766e9896a61c61559b0da1e81da611f4d99c98501fd0af000230342dec36fde3e5e08e02c0ef79cdf50827735fbdefe38bd4326cdb27bd23200e3b6cdb07787a89d2abbd79a1f45a8c0fb5d71901ef3845254a7eeb6c3a86cc53315531165eece45fedfd818cd6556ca0529e72b846da0c62ac81077f6b27b4badd103064795f3a479241db036dbbe58625dd49fdfae2f4d5a9e9fd2621c90d96abd089725b4cd6da301e6d8249b7b02bfd9c0d22a839cca4f5ad5372a648d55e64b7e92c83d43eb55a23f984fe3dd8b7c7963bee0344df8c0f4e2be19fabc38994faf830fbf81384236604ae46417d33685105057487b42e649a503dc52fd97dbe20b1e37af18ee1fb4e7ce2f342bb7678d929c45fdd91cb026e3e4bc44feb30389558bde23aeb6827d38f13fbb23bb6d35f26cf1b7d0a96d92de9c2b90867075fa18eaf519c585ac79d66bdfebafc9238b24b8ce41d9cbb260dab23d97f627cdac908476dbb08f91aae431efda971de974f3fc61ff36f050f2890bd466de6da6b4a21ce0055ce0a8d9ac2b8bc87ff08587f6db95f493d83858104c0fc50478944a0bcb719aec3cfe7b5f772847212fa19bf19dde9eb9b4c87987dbb14d7d0599e9e90d0e54d33541028d72f718c7c62ba694bc79a94b93ce8a3b058967305cea6a6530c6412afb3ab292b1ef8cc8d3b2d9e29b3122369d9beabe4fdcbb7b6fb818713bf5883b5bd5f6a59584f5a21c099951d1a963d359127443dda17770473bda92e2ca5064ebe11693441e6c45092d42d30085c0d0b275e72d289b2d8512b69285fb548be79b9d65be5c789315a38829f184c48a6f247822b9229abb170f58d05d68552fc443dd81408b58d9bc683c4311a625833adf1cb8c59e4ed313b3bd2915f9f776ffb4d06f8ddc1e79e7f08a1be1cde34206595dbd8ad611778068419cfd6ffbe300a72e8a97e096a342e58a82e108998ad88b288321fe18716b9d54f31cc5da78736b411dd1273f6139aff3f4f2408275f730f2bd3b1e5c21b109e3b31ff2d13d401c5e838fba380898de1b8f251bb6fdc39c6469d4a1d1d0b1bf74816df388b301d39bf065b558eda88cf33711aea4063d3d26f530288080e3ba0f604d25027112a72d6cc2d1466c978c5a11c14e22ea3de8a0c6fe937ce41ef8117bd4510ff8975e88e2c66ad822dddd115833148afa83c8f03a75d3268c47d5604ff3ee85ee09dcd54d2cf1ba6cea3b8b2b9142807279663fb7160030f60312a10c853315a72de6d00bb8bcb58c74c6b795416b2899260339b77bd0aac7e33b4b6563adb1fd7ee829e1edcde4e198c1a236f20d45ceb49fcf04298b6e005d6317a8c4d09c7d8233970006042d487d422e26e240bdc0a24dc8b4cd521a194874e35d199cc94a05a4e09e59bdf5fe12fcce33b35afa5090959d974aab85b010c5d7b0fb5e87b7d3d0506c25036434772f3fe3e4ad270be64799c6c8e64cbbab9eb2f707acf20b66353719c8415af8634b3ba0616eacff3147ba01128906ff423b30dccbbf17f68bc5ac74f266defe42257a1a30b810fa229fbedb2f0badacb9bfaa1bc78f00aaa1debcb89bd7adf2cd2c89ecb65b8e38fe3c20a8d0b0d4cfc7a7b6c26f2a58bbd070768a32c3efeac2df457e96cae89cbea7b15933f3c7823eea4a770144ee290c7db5123c2373bc25fd3d43c3e1ed13322f5588715f7c6f3637ad346fbfa16ee913802d5166ee34456ece002072e732e660c2a576f4122ab86b69e633a55e54f7f42c7b7c68a981723ba0dae1a7296b77117b238f3ac808d37eb412383950ed32e97f48af3185a8da2e3a2ec39e173027c2e3450ae57d2a4664f83d6c353e78c8b4da65f7274ce5278be27ea01c774da779d94533bf2ec258ac367aa48dcd690dfa5997a90de1ae0a36b8e803ec608dbb933ae5e2246c59261ca71d7819b5e54a91e05bf1b6cd56f419127a55b94c53044cd1061fff59efa2ceebc2a8bf0a28df7bc7b82e1cf8d984a9493585f5f9a1109e64d77fac05828c1aaa23bc3f53e182de603a0c333233020303f3b46993d10122b6e7e1da9480a0e732fd8b78c1d0b3c863711745963051242bff259dd0accb74b714957e7c1de8383512262d1ecf0db3eed47fa4a6e204fa9806c6a34e058c4266195301092cdc01c781c533923a6cd8e9eb47cbba1d7fc3d37506b20105742ad52f2b13f2df612cea806ec2743262b340ba5054fdede8a90362846e68058673b4081b1e4d09e9c87eb61390e2192eae28762b4f9b53345d629e52525909d355e3d8c564c482321b908e2d30b8d1e8e924279616bb467a343f58122af8002f1d4303053a1285d870f4f317138850e0d1d7829feb038bfe477893205cc432555517ca957c6cf87e5882fa77764bd204cec85b9dec0455b6d90c6620b9a85e4f64a22d1d37cb8ab14f0fc11c8481fbe828fce171a0cffed26118ddc104309984b682712cd176c0d5d21209bac207d53407818b5e2b62ddaf4c72a036a49bc1546fef51575e772b9ce37c3f955767118bcf7ff2a2dc5c040be7b2368414b3f77a4db8f72e401eba230aa684fefb928e4d0576dd7dc2f3e67a1a836926c3b46f294bee48807e8ce65d102d8c62663e955b3a9511e482b5522ee085fc03dd6258d33862cf28cc67f2f5eb43a87062019a4bebe635e6cb1ee79271359741f992c2525d8dfd5cc94100912532170ef68a7ab2f24ced637b8f9a153449264029cf20a534cd78f0272ff637958e889a59ca0368865fa6e70d39b0f83ce7ebd23cd93eec7a135775900bcbe104f91c96c15b51be3109008ab03ab0e9036860f39eb99151b43f36365939420b4c9469a985d4248835028a48baf7da1e1ba4a3b58130507180fb2db4c43a0437bb3dec04d2e8fcf521639225107fdb7fc1a081e2a1701b668f71ff7c764aefe505a0da5142089efac1f122bf0dfee02e77329ad603775a87dc789e6058ca69fcc687f225697038ab0ff5dc90d3eb279d5c5228a58ed78873536746556e3b64c88bd4228bba9c7bd39c202673f612af38013c76337c7610ff3b165409e5c98d8cdff3e3dab7677cb829d2eff3e74e280236bad473b1ee70eb6aa710ec75260ab3bb11030a98b5e114bcde55bf701f95f0172582b2baa897ac6f1e47c159b6be51141df8b0020950cbd9763fe28093b89469297fd06b94008fe6c95e0ee1ec6d6e8b25b7deb16c5815579d7029e257833c50d5a7a0308c9343d30800883bdef5142c5e1ad5eeb1d5d705fd5593f88ab6632e239311195d6013f8f8c074d04d3050eb95faa330a90f10d6a8144c63c9ac248a83e5f398e2bd1c121f5900a78387b2113db9a7ec4ae6a4f87f0d2019d0e9bed3113e40d7e80bf7f0dfceeaf658f45da38e5e637831261aa230b29072e89ee23db7c06207144ba32845f64bae426b8e6daae960072c736cb3fb77ec45b0b4c6606321c3895ec21241e8fb441c51f423c7b7fff923748a1338d2c1e7c56211b8655f06c20bbbf872a79e8804c38202a2e4ff2ec74f430a4ee011891790a446444292734436dca6b7d5daa6322c478a6624e29413783cb808246db8f29450cd1fb00132106b6b285679fb4307c90b045a69a454245953d5d596a0c04e7362c13a8ac58eb0072868b2259c61b856179cdb55dc13ac1f40408cfc5b01a23c1ee2b2fcf018a4d6aa1722c2921959d26ed9190ce2860adc903524da3bfa063026f736cd3a8a7232fd98d8d4b735e42343a3a911a864ca0da1a45b2de82369ce95e27d842bcd761b154e555f4c41648ae7771c13e63b29a0b2fce490434357e4028f1dd89d5bf14be8ec58a0093486b14dd82ef3469893e38fdc834fb17f4276c7e14de31dfcf36da2c42b9354df509a60df999edfbd7c2f71d05cab52e34779ec778f24e00e828d85215cd5948affcc7d5247ec4979e19366233c7e2aa247a0c4da6dbdfa59d3163cf856c01d8e8f51886704be03ff0fca8de6019ffcd1dbbfe7b3477d6675c3ee124777cf33ce22ea1d49b9e4806de3d2b728609becd058eb28fb0a936000d36ad8e573899b68d753cbc08ccb9dde03ced5ab306192c72053fb7dd7dc324b028727055ae053a8d9030626e297cc195b3ae76442ad48d1132e37d9585084e7866f6ae0e355372b8b75e94994c9d6f88742b36ebc31442594d2f2befb742044b38e6d0ffed35e9794684c525bee192dde1d22efd0398d4a1b9347f70848bfa1329e3397571b7628629868b2d603c7bdec82015670bead5217963e02ed28e5ddc1834ea9ef4fb40bef9b927661d7f1a0432998f4dbaf2e6d8e75b64adba94d44a4dfddf38fc676cd53ce055879e1c6288708d140060e5a900c1d2b21f65dc150b3880b5ca337fb5dd4dd7bc619cce02bcc46fb0308dd3bb746bbdb810c3d9db6c0d90fa200760b7b73b1acc65717a8e76de86bfb2ee870e3c5ca15db28e826a063e26c44b65b681a2a42f1565b29a354b8d34877f893c006fe643f1371291d3ef569365f4fd45f7545161d38a9c3a137c1f5e0826e469cc79a3d71129d025caaa4e4dc52a59fbc22d3fdace09da2b6a699df9741753ad3670612e22610aa6c140442f223fd5b7cfbf4dde89701335be690d45bdfcc3249446044e36bfd4ee3b381555f21b11b5516cc784d6c45d0815eddad4eb26629bb961a0a007c8d61ba58119f2c1dd6713f95a54e51327a551bcd698ca510d22de0775745a4cd93144ecc64a030091c615850568d8ef6f6374ff6c84e77dff868c0ed5f3568d68b144ee030c16f959284d4f66aea8718fff4bcdc532e6cc00b705ac534569b36e912ef1e62cdeb25c1b386f69653faf3ae80368aafd2a79f648a617485b84780bcaff9e733a5a64bf8df05623a519f53587d0c132dfb2b14e80d6bd6bf89b8934e139746330e0543f899624d417c61675c3131cbc3c4523e9a45f4cf96a409c67b8f1c7e91b11e987c3a1c5243bf41ca19223f28be3aabb6db3c0aab7200a2e286cca115955682b91de12fd2791f0fdea4c7b11fb022e5995666fe908b6a3167e1b6409af5b065171afb2ae17343cef1e957797285c4eff1490933ba67b01905b112cbffc68acb9b9d8936ac01ab4411d0160739b0c6a79bd3080941b5d6db1a93ca099d44384036b0b0b3672466c8fd31781d6429fa865f0f46ce05e10346400ef74fa5c372bba58feea31607c8664daa91c48511a7b250eb6e5fc4b51d7f70b414d6ad412e333f2cdc9d578e9d040e94f6d5e677ac380f19555c07e5f3cf5676015c1ae2e786f30c19e3c7c6cb55e5b61b5cc3586288f955d0e31cbfa8a78cb7ba3b1661d36ae1adcf45cebff8ab05bf998ccded66c881a55330ee8eb55e1805016acfe614516b509bae1ff7ed4e704d2cca4d8b4ecfa083bdfb60e67a5668e4acc986b17fe75d6f93799d3be45c586661bc49d1c9ea998a319cd1c467c306c696fcb6b3dfafabb8f797f907550ebc7d2b86410fcd4240cad22606aab9dd729f0d035992e643b4eac7749c82f66ca56a1a95049180211617b79783cc20b4dff75ab91f46f6f5e47bf486c6a624e2013059c3966a2227f449732dc1b780820391a13d9ec42563a012a3129a6a7a5fbeaa3e6352b49aa4b1a5a13e24ce5c67c16ddf61fac281e1125bd24f7db4ccb654767d51eea4e4b7f882778e2eed8d20072b059110c4849b671e3d27c405bd201597f563287a7f6c3437887b1a5b798abbbdaece30c701de48f84bb9be22199a9df69186c4e33ebea6be02c2717a095f0e6c362dc61063063d24939cd13409768cc3f05141d7367b684162c3d359bb415ff298b1fed6b860534f8ac2d89c164990d00814cfef46f47ff040cd68f1ee6131b6a9a65eb45f215e26a8493fde5eb20f4d66547df4da38b24317fd8816c2924e3eee1a06d6b09e33f5221e489b55a4f4497c0754d6606f9000428bb51d6da4ae15e50a18a632bcdc789603455e1fcab1bd6a665126521260d40700b954316cdd9cb064e366e761de76087dd7e4ccdd14d476fc0cea4642df096de6299cf20f378118f30321d19ad99e4ec6709c9544c772c67d7bb13bf1bc1c93e3ec453e85f2a0967a4b407e7147dfcc80753de9aa7b0634df3b6777fc65cdb37c0829c71da773172da9a216b2597821ff244c59a5ca9800e0d23352e999821ab21a22816928d12a3d1c534bc58b78edc50e3efdde9979039826c5b630fe38d349577982f76a8080743392469d4996e6296f5a41a97d57d105adfaede54ebfa9bd73e0e35eef67524a9a942ce91f569c2e506a5bbd442788d8ad71461c8f4282fa79c139085838292602842a0e71cd71543f1a5469415a0766f3e6aaecd89869ecee52002fb215095ac994219717bf0e4d9b991b0a0c76e3be5e07d5493884433979ec43a43c99820229758ecf765cc304f0fb10992064bac27727dbe7b5df91597d8938f7f814f4c923d50d2494d211a827120b48cb20b4ef7dd00535ac5c580d0149c8086319384f0e431b29c530a6798517c86068a3338f2a80d82736a3adbf6c51ea7819baa6ad341201728e2342cb785bbd832bffd01e536be88c4006f60b68754f473eb43f8dae724b3ee451d7c847e1c7409dca0fb749743f05812b99d6688c364d4f0e8f24a4e152407d3dff79fc841a606b8b526d81ad624cca382e4a22f6be9584851867b60642e54c3730bc62a16e7fe017b9710537a812029d3bd82e7ef8e470305e3250e020bdec01e16c64a64d1b68d224eb202abb03329566533f1628406b4146534175f6b7d919113c12524cde22d97e8c6098299e3a8ae20f667b194d727c3ad36f11cbe0a43889caac84d7dd9d4286923be8a12080e7f9f375334f18f3e2a8dc2d536d0ed98b16467957a6e6fd2b9f64ea44a8c16f367f1add77c9c9b004512b32800f28cfeff8bd201a6ba03af5a5e23f69c6a5b6aa6120fc8ffaba2c417da12cb4ae703566a29fdc08067dbba0977a7c85a5a46a6d213e757d1783911dabe04216394ada54e1fb59d247c67d682c6d5296541fcb3956c4466ec84971cf44daa695d03bbcbe357302449c829abed250c8081a9551798ec733bdef09b69b0b7a1fa1d6ad61f6a03844d4c13be676e93edb8b9e91be904ca7032fdae9570718dcb3624e56c190f5a61be93d2fd3ae1eb11d32774921fcaa7ed9e9701478a4abace9b213c7e2f5b5ba22957a110355b3a11b220648caaa05525c80e112338b06a80a2708a0571dcc1d1bb77b2bb42bedb99f5ec77bd2dfe3641c1c0d35bb4a60ba40673c4271f04f3acb8416a4ed51f6a0b5d76a2a774bfa7148f03b901bbdaf572cb4460f1c715f22ea8bf6bb11e2008ab8ccf74f7bca75296cdf2bfab4c5ae81818714ba834a7c2fc3ab786886549b5d7c00d61c76fa38e4db8fb4ad59f6140fea03f44bb84c69e5fea33cba2b9d5aec97faceeab97f7e504ad507cd6b55a975111606dbe9aa6aa921a61ed977717d720ddf79ccde283b00b4411dad5576731953a0c7465cc1aff2245cd2d39ef0be218882e51b71acc68f04a5275dec03f9600c7663dff32081cb39255ac5b5bd6b663932fdc6341ff10e27ac16164e0d4ec76091de24e3a037a6ee032d25953d6f20a18a9c5a54f0b501d7b92e507041e24755e381c0f347ab833f42f5cb4a04a5eb1ea23fe129540fa4d13735508c25b787d6a36dd5481478f6633265e4a0f9b31b6b06378176bca39b902791c99f581a72a493b745d712d1be2e99c88b83182bd99904081edc3167c4d63b70919eda6d38dee558de7b4d803f6dcc9a10020a2a4b0afb39801cee468e981e7ddef4716c9e30a245316360e54f7b6e2d3cca85f3df71ae94e0d26253a903fcaf338e88a1433b060ed3b505c16529397853084a3740c2a59d0ce7b425c99539f2fc9f9b1f08f532d8ef0581875b8e76006d6031b93b37bd7c3aa9c593f0392f709410107910da70c22bf1052fd4cad337093da194d8008d6ada49005cd7204a7eb781a916aacc041dcab6f4303fc6b9c5f3970ca442a4a78153760577321d759eda69ab5f74a952bbbcf6c8838bd3591e42e8fc743b7b5054473d7012c0acb9cca29319fffd5270d193912fe16da684517775c32affb0a92d2764b18e9f9bb970d61edfa69cb1345bfc75b690db655dddf45a8603042fa10e48ad1a39f65e14099ad1051972eb61856ab6a202f8c457a947e8e0118bb00acd7d03906a3f1a854caa63a7973dda704f856ea0e1054caf12acb1b85cccae34fddf13d07d0816644f11bde5871844f5dc22b540f9bdc00570c7438f09203e3acf06e8613e108a7d68a458db65d35894d15956ed33e32e17b6d49c3cbe42078280ad6a8bcb0e6d4ee4bcce034cc718c2241268d084bdcbbabc4df9cdb279cf043125d3ced98f63dd7f60c1a2c8005d21c304ac53fd6027ad8f6b5a09438338ca0dc199b06bc557f0df95a95d2ac55933dbe27fbd1192f64826f5e245208b4826b279314271ca5e5c4a1ba86c4ac68d256e5ec9d2ba6e40a4351268537b892d68383fa94635402115d4e51cc9acb12a21b670c52dcd22d0406f53f09161f99ca1d416d19718ac562d17abf6262929b29154495a623993b715f96a61fd9a36bbd2151965483c2847d1d8077d662c165ed0dd326983ca1bf5ecd905a8d63db4fafdb04a90e10a0773a4b794ae72fb579d4481ad569847f691959d6af43f9e770e2fa157189ceae9f05f26d043e0b8955530d658970f1d746f03850dc21db2f991dd7a22c7a0f416700ef481958ae118f17450079cfe9e06d3ce721fd1867d6bc8496969dcc7f831744b03b57697487f86e71c3579ca64495cab6d866114a95a1ca59215b4234e506ce03398b76671ce43e5f086c5ff690847b7da94198a30b9c2a6f10a83541633535d2e885e6aa697a0373476503b5e413cd33dee78fa8be395a9cb3e98cd59d622034f2b2ddd4412bd600dfedd107bd061a9e3bd01b970f36085623da2b04959a9458ca61e43a8f2a652f4ed7d3d820b45f99ae6444db4205e26e073e69821f939519b3780623b2242e43659c443789bf527a15ccbd4c17df8f94760451c7e5d07ceb060a5a0f066a98682c196ca7e5bf48f7a078d9ea27706ad07ca32ccde07ef6a303bc34225cf53a19c32a1c666d8eb7834d8249544f19934555c7713eaeec38b0bca32a681a77951ae7266d048245fbd08e5e9a8b8501575257aabcf416bba691a23e596325d23f10b76350ed32c21c9e3a001d9af6442454f7b9af2988a03fcad395776f232eb6316f0f87a469d47e98f4f48f0d9d4511cd6259c74991099d9ea151860e661da25061024c9341c13ba5900df0dc2aa36d413bfbc09e3fcb7bdd268b0f2128bd4a10d2ade327ca36091b786e032067f7a1101fed0da37ea79ba7b8f7f0eea76b170bf1d4c3431f2d8d54131af6c0a0e92723ba74e836c3127b041681b5305ff1183b6363895dd7127f0e928b1fe5d195eb1d33c49d175a171ec011cd523617e71d18ff177824ec72c0cf456da0ddf25121ddf16be89fc07cd0827426b79baa1f4661faa40f84d2243e13753a238681692922b53c5c05d6a18d2532925b7ee41c8954572d407a2eabbf10619a6b5ceff360d2f7dd8d847be5d22920127897a9bf84681619f820c1535e9fdd8f1090a6eabd6e2ed4d5c3cbc669f1b17ad8426c337130f7a2f87e4d8e803eeff7030f1175160c00f780a227162fbc93cef39db54793397df2504a511be24daa1c147a4cc0ba5b46c9caa4bc164821f200e2a06ee4a33101f6b73b43d301aa1849f055e64756a108363a81f79d1f985b05dacdac3a225295298cbef3cd44155ba5cc45f1da93775b185cf545d0c04c769c5e480c073b1fe76ebc8d48a8d27879994493be787a4c300d23986d8cf81e217b84a691420e3a6df804161ecb14e9bbbdcc165ee073a1515f3f5a5bbe2ee7d6c9a14a62453d4b0d5132a40625716669089c5c7fafc4c5e6d8d23e4366a1a5e18bf51acfe3e6311020513c488964fe87eeb5ad782906eb0bd7e34f259414663c225217f75a23626a67c7270ee03129d09d27a1b75539fb10f139a380056a5b75e3bd673822241326bd78f663f635b1694911760246776233c0a038e79703531e7777d4007bcf15a04f70f9b34db31e059637511d7bb10ac500840bc41a7eb86c637312d8d1615dc2f08840a3dbdd1cd70d65e72df6efa149ad33c66b7a9a30be29fbdb035f8b31a9e338ab8bcb22208976f4d27e8c1e7e566e095ccc6d18ed0b6ada1331974700308896093a02d9bcb8c67d0e13049a81eb30b548eba0a1b01a2ba409e52e3370bf98ce424e785f16c4fb8d8b707b11410cb297d50eba711e279c3691c03762611e971a2382aa3aa2e20639b1ace4b93aa872c12a876f9ade7f0ab8f8840c83 diff --git a/crates/settlement-clients/ethereum/src/test_data/blob_data/20462818.txt b/crates/settlement-clients/ethereum/src/test_data/blob_data/20462818.txt new file mode 100644 index 00000000..5f4511c5 --- /dev/null +++ b/crates/settlement-clients/ethereum/src/test_data/blob_data/20462818.txt @@ -0,0 +1 @@ +0337b13665a49b2049b69156446a70b6d2d403fb091b1f766ba6d81f9f406cb328ed257948100cef9e5cdfae2ff4df64be24d25338aa8f3160844debf6b07e9212150bfb9048c7746bca3ad6e6017b556e3665ed1dfe0353de2cbd99c584b7c12035ebd81aff9eb8d6131d7bd6b08f11bf47a5a15a976db91e9644bd9d4cd7874b3c928c555f6801a97a2e4efba5036407165b52bd4e14d661ba91de3dee1cfc43ab0ac9da00c82ccdbe0a359263ab8447a2e625f02533c0955b38d3598cc2933883b2b47af1065aeaf78944301c0a1b2107be448f3145f59cabca678d032ac4738bf96db6261d58cbce0a14d645135df2ae465358e13431e2bf959fc359d0136cbac1578a33af966ea968b46f1a9b1dfd9cbdd14707b22f9fac1d5f95469000500b8c6278d7991c852ce487c41fae22f8f8f915a15caa0894ac4ed00d0756df23d7ba77f6b93271cb65d28ddd6a6805453575d47991f4ee17cda32ee7f62f0c36c0424b761f43054bc1da8ca3129f39caccca5481e6f05cfe7e91c0180bc90101f2c4b830baa968f3ad68654fa6da05444c3a293905a85f00f966644efc009167ffcd1d0cb595343782f645fd163a9f2cb3129c2f3c355d7116063fe38d04ea23aef7e046f492a33484f44be85f7917607ac0e07640ed50ee51df340b404909620e6fa8610216a03bc507974035298e892b2d7cfc343a614c0a85b35045f1ba08ff019c1987f4b5870cd379904f27a455fd30ee942b336262b4e661131466bc481a060b41357a1355ed844cf85aa46d776d7c26cd63eb2a0efe5b4f05418d7b3f36ec025896de6c08e6144b53f2ba771eea1bcf9c88f49f766fdf3143de0fb76a07372a7839445845319831fa684ecead57279443d92c2a241e7e80b0e59e8a0b4dfd18a77b2cba89610149d00f3ff228cdb71e89f17e040278db06b9743f3d56d1aaeb8a4f526c9dfa23cdadbc625340478800717e0274608a60e38875caa50388fb67d4340433bfafe2d9723a2884ab1dc22deb2a793b91a80ed01a06ae62206f4baaaf19314ef110102a0d802da8cc40e44ecc5b130d89893c24137856015988946d5b8d1c7deff9789169a13a5b8b37089a2e12dae89df76c37e94138056ccae4c65818e0116186d423aee7d8b899d2059cfa1a0fcf941539aa72d72f3310960ca969062b07dbb21406771b97c56ca47e24af707ed07426efb306cd8a582b9acbeacb8f4f615f364b9fb450d0fad26a7bc535dd9899c27bb000941579be193735aacff4e287f4f8bd3dfcb2a620d833da97a7e69ae2c609f6750ce9dc96056d50f70074bd060c2315cf0a102d52f93afb689704fb2955360d084981fa7531b92e3ccbede8e737ea2c9467bfc288cef48077ce1e84e258d9c254ac28eaad551beaa9744e0a8e1339616b02ba7e716a39d46afc386f9cf34472ae146a4e95402b8631fa0f46b2f161b2c92010b47958850dad73092a5533391afd3353a024577255ea8e544d1407900cc3ec61cd6e633f58c556a4a7b643318011b75bb15e45ac40b0c80f608cb827e962a52fbfd308bee983eedc641b0303bb14ba840f7b42821b968546a42b6c34654a30645264c9a41d940851848f7e797dd43575f90d1cec80b0e6e81a9e0442b332ea81e7088e31a6c33b2197edf7955985f2075f9b4a1614ef02c7d4312b57325bd51e467ff29a1606af127cbc1939ef622d57769b698a2de63498b2a44ec654105faf6c28a0ad3ffa525567e92a4854cf57e31a881a73ee1aa1256db46dee7b05b834566748433454a45c37b4f2985621e6756980391eb0acffa50dc404adeabfb2fb352e87ac0b39cc8baa82236202ffe1c8e3b42d5dfc423db919c89817c69d5635845f41270806c56aad10bbcc4a6ea49017b909525ca6741c19e1b3c7ed32a60e7e5602398a9648ac2702a24ec4d7fe22af8f6c50c20c33bce85bc9e01f035e7663b964a08a5597f4b80251cbad5182cda2e36e0350f71e50c29d5562392f94034e8541bef62ae274c1fd696e3d943cc8655f3767c1da999db8b79e87b8bc7b6894ce03a951a87fbb268451962d5cd7b446a41d481658e1248568371c88653ec362943db51e94bd99c2a95556824887f8b1f8258a0365238b52ecfd9499435bb21fa0981f19e30ad5612a50d68e1e54c12e106d926f70a678432165e57997b64868a2e231765b145d47206b36a2dc57c2df0d36e5e51af9917be33adea4b66cfeb3309ef4a60ffccd8cdaee01b0d21b05278611ce451d0da413bb1b7c8e5690724d28a0be5f2c9bf1c47dd2d1b30939837e6c37a2bda09aac7737e39ee8b0a7cfa4b229a74ff3637c5302f08fdbfbf28ca9812a7487d7403799240db2d95704a033456c3786c593f51fa50d3bb0515779d54311753c444621fcd1d7eaad2a0ec52151e99d7b78bdd35ef4ce283a08442ae1ce10ada1e9a6656807ff2e4d77e624bbb3f42003400fefdbbafa04206fb5808af33ea694dcafee61d00fe0b7418eb08089dc9088810a3adacf437528c2f5969fd32b465836541d0dae83e0caade11f38dc0dd2f54e83ec38740efc4b147de5256300aa1b538ff4b08f880a1bcfb6308e4b1622d04e40b39d46b388936e1e3974513a0aaeb6d7c353b3c716339883c9766ec717cd3d3de16c31f6fac95599f3b57d0deb6865ae85a9886a9df1c60e38c521ffc63cd99c4ce6be045c4df596c1c754046e2deb1145457759a4886184620b9bad2ff28b85aa13b3d58862a19907a18b445305091529dfae5bc0f23682bdcf1d85332177fa055055b81e513db23675804d05d7b0c3af475ea84d8878f48075775a964446ec79ddd03bb9a2ccfbc2fd2d3f38adef4850ffe9b617dfd6150ed181391dc7ba9cfb6652efadc9ad56f5c2c55b3bd30ca772b2cfe0deb954b60f994a50d837041795f2462b98d5310857c43a4adc32e5729d9356ef6164d16f7553e9f93fa6781c1a3caeb189148ee351824d06d6228c7d4aa0ba5bafc13002ee973a5dd3fcf4e6ef0139e2bb8f95d490a29f1caa1d1cb445da0f739df71a71012749e811911ba2bb7ac6672613899fe6c8a9363fc74ca40526e2abfb9d67bc41da903ed1dafdf5c4f183f77b76073fd6e1be0d64bf6d0d5e31c701f7b4a8e2f073f0e67edb0e142592330574b212955a0d4a40d5a4ae1d76b40aea26c900ee4f377067f243b670bb372f5f2aecf53de27f351d0bc7a5ce898a23afcd785723a15c2da2e82f87f1976833151d38a6baa0b7be5b956803c9ccf5bdc954c5315188d29b56e8b4b577c57befefe45c487edc24062e049b73accc07c2c842e3d37431d77735bd20323e56eb88348f00ebd2a9710f3fcd731d0d4e3415750dd4d3ff4d3b9d1cfb53e7575bf875eb662bd9b72eb61a2c8d2eba664c5addd6f90ff3ceccdf6c93e498afd77f485fe5f52de73c4b8d6024104a95f3ab1fdd20a1b2b889aaa7ce04d93e954ecbadbc7ce751839c57bfab459ac737fd448bf6a194bd185fee5cb3480e8b40e3a224a24e5b241fe8a1f86d3dcb9edd85cfcae624ca81cc77bf75e0a2b2a9eee6a8043c3db7d365171d43112fe1d080dd46373bbf6fd239f2dbf55fb8ee1b12fe2701169ec2ea008bd8ad01088b3c0ea4d1d1eca15f478fc4895f40e3c67b0f5bf5e0c8bcf27ad1c426ff236b486581076221967f7d985674b61e7bb283e1b21dd95bf879d465ed10d6406667430a409cd48b64d35b8c6a92af58273ea2926aff3ff9624bc8eb47c5166d013de36fa082f08f7ee9bf55f5bebf77268cfd92e147dd6c0178798cf49665bd9f4a76eb8411843750d74646c075581d880342968b9e2b61952212d70d6132179b0449434790f80b64ca125d9da546ed17f7451b725599039ed9e5867f04c044e75cffc6325daf6e808dc6714c5ddfa0899a935f4451e1d304cf417667b18fede5219e8ff4c3ae2f5e8ba0476ebf6844688f072e2d948afd0cb44e91f7515847e8010c48be4a032d8cfcb51bbc511d35e177fe21df900425ac04f3f41d42b3cedf045beffd2f4c7fa96d37367119d471cb369f2c3f92b92a661fb7d0167ee6e484028680250ece569f58ee35ea2a3b56af82d596f79fc02df51f861decbda195326c5f03a4d1cbd498fa3ee76226910be264e6e98e9d082cf4a17ef6c3f92393fa3d9ffc49e132214ca177d0a96ee52e3f34ec78e2dc1e7dbd262be2de0742c4530bd7ee77af7b50425f39c72dee021042eae111f69121a6a4bbd5c8607eb6477f078a9543c9536ee4f35cb59b3ec087d9b236c01df545acc98b34b674eb7d9e8b49b22f6a239934627ca8524c5819c7090ed031153612ff55d857ab6a58b890b406b370453164baeb9faea1bd718035b775ecf50366c9ca93d272e1f0c1be5e184cf26cdb1fd762ce0ef92e4c662dc0efd3042cbd8eab8e47766ea9737c90766802b8888d1219f29b625ad010d0ce6511978fcf17cb2a616c6bbe79d350597e20543df729bae1d32c9d124a73be1286ac740e9218631ab5a65ae4dbd02343512d1fccfcd898c674d4018608c128a47be2e0bc49da7d5ca73b5b4a6008fae1400f5d6ff3e96304cdf920e5be0a32b00457abf6e7d8e4964d94cfbf6eb2484b6028440116a504eff20470245df8e77792b1927ef4e90895902a9d85bb56b07d9bf13adcf0e46ce2e2c584b6fc0d21737a353835d975ee2eec8c67b5de1152a3de07099b36b398d1ffb796de98c0a366427f7c0a2a1aa3325b8f9f74c1aedb446b2d3e47020d93e38b6134afe8755f63b8f694a475a8a308d56a79868c8c130df73f27627a62a5d2666a29352fefaeef77f209b7831e8a16de3b8d3e9aa588be12a718e2dea15eeb6bbaa24acd37d9de70235efbbcf66cf715e8513ed536c4c7c78d161c8aba8e7193626b64c3afd09c5a06e1c75cf150ee6f96bed5e11430ba4885281b4d551853b2fd47a4345c6bc67a2559048a45c58707f89ef0198d80e434712052bce1f7418a2595839afb95429e481df701c20777ad5aa067ee8f8bab99511b9a4407cd897b9005fd8b8a55f8ea202db3db3cbd6ea6c3bddab6cd415a2b4b2c42c07c2e1227c7a537f10260c95497a647361f367ed7fa2ba59eab665031733849562a0fc2891c1abe31fa2cc8f1cd936d3d0407ed2dcc4406e92cc75cd73764cb1fe1223923f534d28475bb4b173424ebf54c1c0a85178c32e2fd169294b06306fede6ddad346cb9d24428fe7126c8dc3e2fae19a2378e72485b496c37d3226dee4affeb8b2a97dac41b9750677b0b278976919ed482191461edf1b02c1d65c5f3584de802f8b264332fef63ebe5be4503ddfb0ef00724772b3a2beea5d893f2b04ea1dc98cfb13617a7521e36c5c7a183b3f0776c25bbd0922c73a552a00346542d9441e130e4ef0af035786ac693dd910698ce4751c207b3e9778a762ea608d7cfbfaab207daecee760339a81037e9225da40ca46ab06bf6f28038f08082c12172d7ff380db9998ba5b655f92d43c796b92499514dfa32f625378ee83d168532446b7f410353e022a5acba419386b8c660862ce051e4e9d32b578099f4f2b67b1ac4a5517dc19e28450ce516932896057310f642c66c06d54f868d826ff02061268420993b07d37291430aa64c941768cf69a30557dda860ad3757679994d68d5136ddfd1d8f2f0efedb2921e25f739bfb316425a3c0260a78e379dd77e17c2c651fe8b094d0f091121ff0c571b108607935418b89fe343e404176102556b3bd281efab3f8e9870f80c89ebe2887ed3c7837b648f32120a7d267c4bcb6927ae7feb2ee6126b19327078e7921b2229dcdc9942ffc88fd631b6e738991eb20d8c5a8d688b639f805bd102b875b413a1625a1ff58a8ab26dd121707da77e3c4e5ec39b48e501a7fd0715c14fac36bb5e1e3b7d445a8fad1a8657f6e15b17fa5f1cbdf113e3a3bc600d65aecddeb9585ec1a2ad24497179fd350f68c48fed721c01407702b9a4496f8ac6c5d00e95c6f8482263b0caf7558877e6270c05895d515ab806446176f638f3d12b8db4c4da3fa3f509674a2ffb99aed1dcb25e1ea165e95374eb25bfca12a6207427e883aaa7a14c33a6aa180e5a8cb0a7acaf4ab749bf18ddd5127c4ab804e19b89b9e170393650d1dafaee6d5761a76e0dacb0263cdf8e4c85bdee824df2d2d1edacf85d8a4dcefc630a6bfc25b0c3a8cbe1186139415a1939e1f5b3cf06c391dd15b58a3c46b7d785907f11d4cd5373514eb71363125c77aeae89dd1e4aba2c0834c29f9d803815051bceaeb162a1a2651998421cbf8ad6c4ecb89ec15f23f221e507911277ad1db4b8489bd4a60b89028791d23d6a1979f21aadcc13a134c90bd74ce5e216703cbf0d87664150287582edd86f4c99182ec34a142e44df9694ded051f8a4c16bcb03758d145210316d87b491966de162a648e5759c8f596669a32ebb06251b3c14b592568ea89f66332645e2dc50556b45f276617d5388a34f4d3e0233e1c8c11bccb37f48521cffa58682a6f863c017d42a144e55d409b1db6ac413bb1191bf270a57cef9a4e76660b47ef8332125da5f2ec3f97305e66c97ba6ed9ab9e8f55e9de5ed735687b8224163685046688818a990c8067f5428650a16b02f24e2c306ff74a6ae4057a00c8396720c54e192f16d140eeee7c3b59711029e9a6d406fcab1ec93174989879512b8bb0443031c4d1cd25bca878267c2791c823423851d8b67c9ac7a298f75d70337730a71ed21c7abea170ce2533da6bcedebc0b5f9da8002d5aa1cc5698b61366db48c36825904b71974adba6cddcdfcdd779e044fe2763876b052aaa3a349976c4ec141f4f47a11d003fd4835286cc280379628cf36cb30450894d1487e13c0ecc75b632fcb337a26487459f7e8af42a42f401badd9f82571188983fa5b60d027dc67a258b39ca10c0ee356b9b0a7c4a7a2a1c27924938f96b852cd64cfd2c2f92d66e2372c41129b97809e5e2ef43e5fe2b195e652e44f2365c050f27c1b58908fb025d5aff7f617ab4d76dda7cf8c2a08ec2a6c5f204450019a82ae0ff6f34b0a381010f77ca8617da0b196c56dd4ef6012881b52c731f321dbf97ad586a6f7809f949c3fd893e962cd0a3ea86cc45ae56169a0f2f7e3bac20f5957c0f3f5546236637519b2919127afe740ea5b098fa1b92e04a1dd71b3ecaa3b9a421da4e3c0dd02fe783ee2d48aef45a52204fe5c5c4be2b12195a3b35dc017d2960aa1dc7097d3fa522539a2eccb0b3a3ce721bc06c583e03a5a71d6d71b1930b8a1e0aacbd44584a19ebb09c8b59adbc47ed6452a22328b44682f6f169128d97ba6727e8f8c732e53600b4aaf4b708617bd0b5cdbf93083ae8f3d47543c405a198e10dc71fd16daa86a25e503566bc13d76bfa92cd3bb63dfb55683e399f9d686fae47bd00a9172fb0878105171a53a42f3c62452493472107b1deced4dd205ae15a6955e85e0bd7bfd7abcef6bd9e11269b19853754cebfd89b221521e93d49f6a1e31c642973d54332cb1d7a43c5f1dc3973b3d821817aeac028d4bbf87159c28d89a450f664427b4f7ca2016ec0ee4d0c4d275cf96bff8d3faad25e1524a3607a4a52ad114e33b79f072b0c9d2c519468fb7d3d45395fa8344c41c777948efcf08b04e2021b479e72e481a9acba7dbca498dc52015115cc253b67e43daefd1bab83f527f430dd05f1c321a06d5af586f8f49f92edf51ad4cbd2c35d6774442d5e695f6479407b5ae1e67169ec61d7862dbfb7b4741957a4e11895dca473e33c330cf3a37d1764ffeede8eb11170402d7b1a20519a74ed279b627d100248f769b2815d888400fbac11bfb47ac5d63fc58a877c6c21895f4dd1e47deefb540e091c5c6ead084dbeaf6fcd7424cda998cb0a5bea9be26d220acef8921c70f81450e80f13569d37773ad484d1f2d004cf51d34e4260e478ca5667acf21f1f416c2c479625911e05b85b5dddb54996e5deac29fa98e14f3348897639557f8c549ad36272168a4e5fd1fb5637123ee9e3ab9b468f6ed0d59a89244aa8e7e843c39bc7f42ebd028d04581f6c572e62aa26bee3e17c5a2e39fb7fb13b2e1db642398018bd428640b168f2b0800d4fe21cf288bcccacec0c3db02cb143413e27cf2c36abbf715b59584924a4847458c4f70da8f0559f6dfbfd3769c94a4ef092845ff772e5188bd6eb02f7f72f5c76d64f88dd2ca8ae56003c4bc6051a8fdd1da1d4c5e3a3566881930a3399475716cf44e139302e5ec442651154122e1e8771de58ed16bc2272578824810036497f18045706d0cac8254acef417cd3f80a5f3c28a043edf35b63a4542b719bc8b64c434e19e3e989ab2e5693245fcee83eeb0d8db6775cc836f4b9d2e27b8a5d171adf5500658778ff71053192badd9396d160046026cde8c0de77e3649ad5bbd6919485cd47dd8d6457624d5bcd0d36892d83e363a91ab9f1785fd14107caf3416d4961735c8fef364c52ff7e34ed313f65a21b583d53bdd83671852348d2cbfe80004cf22ba538c75057dc29dc1e01898384a15c36859cb7406cf44c4b2989497579e5927495d8473b1f43c3d5a25ba64714e87055f7b48d993a64fb2210d03cfb167373dafcdb79a3911231eeadfcb9847d70a2ba695c5783bf4397337bbccc03705884cb71245683e2faa1e2e7439dcbc424083ff9ddb28a099034e3bacbe3d4915748b58dc6295e2133a527b6b11457822d66479e7419a570511108a253aaaf3ce2670699c05bfbbe724879b04af5c3ca1587b94881be9f8c2313fdb24c89b5daeda73182e8d0a80301b1ae40da39adacad27dc0c9c7ba98191d019009b3b99b4e2fc3ce94e78ff617b99d385637796ace436f6f193a938e32492a07a28b45358b67c6d2773b81b33c1de85690d87500afea0954f2be13bad872fc94afecfbba689839f2517d2d8019c9b3382a0afbf20d043d8d20ab032abb07639dfb8ae8264cab2df801919063245e13f2386a5e5c550db5fee792641c0e6e73b4a171586a98c97b2186ea456ab81a755ea3d27ab3f81ee3914570909b7a374ebad8e3ff3e5506f2ca6e689cb270d7a4cb6564c3bde4dd8ae5b29c8c736b6f42f52849ff41c4ad346716d5ba7154a13d527b94cb955276b230e3f2bd666945f37c1e4a5999ac5a80fc1c98ef0a20f8113b3b5a180c8653c404c5244d02fa4b5d53b84d18d21627af49777bf6ae379f1bb450eaed353f2f0e46f58c9c31411c13c83dddf2ae29c75c2f2d6b7ed77db6e8552642eb6d9224234e35cd0941d52264e9f004d57f5db8156c041fe4e505a00cc3c40d24ce2e8afe66a66189976a07a37f90658d3213920f17ea6f7dfe394339a523ee6530740833aaab485e8d240dfa85570e4fb990515c77abc96412202a266d20e7616e6b5d5ffd57d7eccd6d0299c824e9c95fbcc4b9d1c4fb4c69f6239b9011081adc348e2dfb96f4750f3b0499e0167a4e33ba03b7ffec5c037202d2c7c6d274b980cbd6e18ea28f66b10664b48a113c374be0ccf6e1dbc0d591d7ac34482a407889c3b3c8cb279638fd8a07052691681b7ba4113f9ddf4a74953caac12548a2a7206d74f43ce9e241d82f1dcfa87bbb286a4aaa92587f5bbf2aae8f1500ea0cce28688daf0bfd424d272e177cf7befed678293e14f7b1a0b6e471a7dc2ed5eeb76c1b7d1c6a6f2af17e8d3e104e5195162728d4226a5232ecd117b26e1fb144f60c1122fcf6e517f085aa647c8da9646a4f240e774437593fb6b24f0c8391a22b1ac4d2f752abf3f4377f346f52297f4daf35a3c2055ff9c1cf62f8aaf9f0b45bb9cff7b473398a9e98121d0af517c27df148aeb54f51bd1ae30e060730d471cbe707ab0765839b9da3440aee9ec92adbd66e8e53ea2e08999efa08cdacdc11d1ee7952719c8daab4227935c0edea47c3aa6e984ab294ffc298eb8ed5610872a43a542b6c044745a00f1364128d7bb6d1c5e8e87561d848a14d4b769a661cd054b36b3ab9f0966d01511903ebae801acaadd41b14c1044484408f7a294f5b4da684cbf8bd96c8de95e8d55435b6d9110e976ff3cb42f9a7e0a05a86c3db7ca7cec5678d9c3cc1d6a90b19132665d16ad5d4abeb09a680dc5b52820f1998d9c33cacc415f19079d6f7a7547135db981047289b31a56c7b82243cc172afd2a22b5ad9ce45d4e3dba0277a594a89f42414163491562b652e41b54dc4ecee356f68412c83e6919850dca073240bbd2ac82f9bef2c4668f58ffadd9c2e4b2918e3132994dc587b4aaba8ad85492991ae9dd92c6c11b7978c0aa4b0bb146842edf5305d61059737ef181e2168db42eaa39e9f9c3710c6058f6864a114f3d2cd68ef6cb6c3720b83dde703cabde463312493d10adb696f8b36541a52ccb9093907bf129925e9eaa05ca219a4f9585968b0f1cfbb453ad59c37eeb98f7923701c63f7455f33804c43da249407d7960bdfe35c9d901211a5516235518c29389c82183fbc99c11ed0caaebbee797c5440c1c7eae9b0a9ccad5698c6e183ab533bb31b3c22e2cf8644151a3cbeeaa9514fe50f1dfca3c8256d8f32cfdd4e3acbbebff07c20c0806a62bf694cb9710311382b004db03f462a22f8d38ded2c85f1768f116d5c7aeb4a566ad20db517589c1cfa4f63b093a26abb1f35b4e26fe06bc88031373c956d39d1aca7a6ed8609db0fe432a1f19126041c49e2baa6b6da48f2a40373452edbe34954d7af893335301c335943e1e03ae1381604268c40b0b887648aa46ffa17edce224a36b69718880178373571ab3d3089df5cb9792cf4c489f2baee04da53cf2de743abc289138d2abf95a4091ec983c8afb9e3cf197f72914b46441a155ad72e694e70615a1fd41af7d6d23b01a1043817e3a79526a3048b9aadf817be8b2f538ac50a514ef6ed496dd67351a57c9a8afe967b44265943bb4537616b0ab4722e19294e6e5f23f85efb8098b1d7840ff5accecb4c2f0929421c7354a5a9510c8ce0a4ab50a7f7c14ea1265128f03bba5e5dbf0721eeef1266168fdda39d6bec5010f22339111b56261e7092dcc797b6ecbc69621653a4ad2d8ff0214b9d653709e39112a9565d376892375a4cf5f41241b084dcba17dac24f38bd830cc8e66ba084c1a73782ca6720b69684a1be209144d55451172cbf1d4b45d7c36d2f9a85e3d140342005a6064873eb86de4564d68676f5da7df4f82f8afc9e8d06d45010f09f322759761ebe72421a8a3802409677b72aca58da6fffa5134a64757d030176eed1fb1950956a6b899fab4d0dfe0b28f27cb6be0188da9c75ee8514b00d1942a60a0d7b85a72e640f5d22493abd15cc1d4e1df536e06b613ce87ec997d3ab1b022e0e43feca6059a43d79cb687e8771418bb6638bdf7dd044421e75b08b443d7930951e932c7e495311b4c2c9ee80010a7b60d9b6be2fa36fba3ffdef8b3e2a7f84c4f5e5e56c0cbeba0ee39eef128763aa13d9d3a18e6e2ab80fa4c0853b4068bfa0a30ff9f36e16e391797bed6bf4696ce05724f5a4eddc95a4dacc25d54e603414d20e6eb14782965cb8041725106d1a28308b69bac102a9633791ce898b1b3995f83a7eb663834be952d15c8a25d9566dac7604c5d2f8ab35bd6b2c84e51fa26e2e97908726c276c9b357117aeacc4ed25c52af4eed1fee1ae7b12ba033365145205a357431f1496b453574fda79eff714687862f3d9cbb01570b85ba260c620d1d0db9d21851c1d019d7b24dadfa0cbaeadbf999ee60a36ed7c934dd68fcfdca4dccf5245430be7c0cfbb6e5768e9c0184ee16dfbfd85e7d700f40fd7bc8d56460939150151cab1887de435d953ccd72145f9de04ad4f4c64fc78546286a86562dbff3bf4622d028c11be68c39b30d135c995732f9f17f9f1ac33d63248a5a992d21b65f5a11e622c9403f88ac047965abfa90f6942f469057322e96f4040e81975bac9c6bff65927dfc9590319b714038cd7aa19d3d8893d172900aaba9dc00577cacfa26444603bbb4d66b805ca9130d1d0f9590e2673b9730e02b0f84f1e4c5c9c5c152733d71c5e037ba9b41a7d1ef40ce70c735906e62ff2c9c0199c4846c38d3950c508d909a2e5ecd26d0137e7db0157531a72a9654836d4e5c797f220446f9051dd9b431f991b7db3485d7342cbfbce691fdbaa9a7db51ec0b4a63704a5851b862b872766f8b18038c6572d9bce0d1db684cffc14fbf298abaa791e15aa2be753b96a68a268ea4ebeb7856c1095fc43cf24bb0fccd2395b6ce7961a01aa2e0c31cb330a9dd8b8851b61d33b8870d8d1d5abe52a81b558a379ce9085b96c13442283e958d8c2bd9da59ca24a9d699217a177b1395df86cc593dd35ad4eaf002f63309eb813dd5f6b41d2777b60accba4c153ead25d217455d2cb30a600662bfd46659cffe74d4fc5c9bde014c1ac973f6bc593ac8c718287c515574e0150f3b7b31e89cdd971e30bc2e76178681c0bfba443ceb9ea17af9ae0822e3a64a881e84661009182a4a17c02d693799051c72615602c42f8bb9f59c5d830e09e197b50f49404b45dd4beaaa7651d21c27172969b5a7e0324d5fb25bcf21095623abbd7c00191e67e41b76fd63a69fc1e948342904427178e8524f5cf2dbaba445bfd32d4d58530611e2c231f88c22721e923b8c6caf937be7ebea885c8b37d518d51adf0cc46d05d8e90a7b773e6075be24d2c8317dceab075b1be447819c615928f7304c5fad5f805f1719cd29bc9db69fd1ed56667e3c68ff856c2814598e43875d1255bf08e04172877a0d7e5858826fa392ae46b43f55380d05ff715018fdd0254455654a2dbd2e887ea04711fa172d8621072db3412cf32a44576aba8262b5c9110a939972cfa2466620711ba2f94ce21bdd9e456d132f76fd174e1152833fdbcc1201b21fdeba369d17b91927912ceacacea3752a15fe266238930cf8ec88e8202bd61d0d5f7569c029e1ed3732b3d219017c3dcb9f96ffdbbf76bfb2d03ceea6190589c925076d3a100ea4834d3a2c93a69d234e5df339abc9192b8a3e4049ba3ffdd196257d4fbf312200f5531e57f279ed75ea8ebcb78c3e6d21fbc48802491ce01dc6f44db55d1fd065b80f8ab4b54868e542d8f1176beabac315c7b1bcf768a7d800a31573ab7d981663efa90d3bc770cb7d2c1fdc4cf293808cc2709ddf7215644bb630a5cc1c3d5bd17c7d2139c49d6c8c2ab87ec1b087e57243949c9e683a9fed7222f314cf10b070b8f48cf0fe3d89432ee35c5a532425a7330beaa7082c577883b49798854c2e64e80f5eab178ec5b52d7930427731c8588703f5db12e656e2a090a442e580ac74c8b026d5cea6b8bf502d751a6968d7c32add8ebd737e3fa151e543e495fca810c6ca32f0acb6ec2e0ab7cdbcc44738f2adf9f3982934de737b32b9011673488443c047c257b27af7cf7cf4db8226c29a05179ac23869215bc64c1ffc3e9dede86a9e556eae5737464a6833d58b9af898da883d016e80e461231fa6faff1bd38c1dc021caf2d0ceda54dced8a0729d8727ec79c3b14ee6f930c0dc6dba0bc920585efc7826dcd2d60f4d9f01b9c05c0ef565401ac3f7f20aa40807a7c2d9cf2deb3ce6525bf5046781e2dfc41ba87d5a74d9233ad1ff7379b797e21359ba8bb96a4e73ff4ea7b35caa4581ad000290b14224944992595a5a5cc64324aba029df2cdb9a640f460f82adcf5af982e03a8e7f65be93846432b690ae6607f40f0784eae5ef1f4650b6a7367e527dca7169085d0ad53cb5c43bd7d202deeb2f033cb6def30ddc163263c2471c3bf8cbef8d6804e275e4550df54e7618b5435bfef700c6a827bebe7c39d7b4461698768ce60209a99ba8d6f5ce5e1581829f28c9ff038d47663bbb349312b866ec4d627bea83786fea34f40d9f9af3b75047cf5c675ade3a662706d5eaec016752f89f39aa39aab731aba5440526b1e1576b63f0f3cf8eabec984ced7c61542c445988e4d5629c9eb7a653da1c6932ce606ae8ed4726f67f85e82d119186522b6384b3369d9f6bec379563509a46f19bfc4cf0a21714f02ed584a6629057c1d5bf96254e53d84abe0687928d56498ff16b73fb7c14c528c6ef8160348e29bc810ed8b9b22cbd1450f7cfa0885db8c486e8666e2408c540bcef2efa47485ef2192af8ada1666b79f0341ed5bf888e59e3b01d7ebbaf887207141a64d04fa0ab66f8a41f307804ca175df155d0e99d7d0aedfb1ecfb327426b8af14ef7a2ddcd76a177116bd3dfff26b71314789d47add752c68d4eff85760e16f81d00a79ce38da3ca66d9515b47ca2c04c36bca6e7053669a411443194a6c722c83b8849288781f5defaf650392aa453b605ffb53204dbe8a5e393d254e8685eb7ee495421fb1628aeaf2f6d5f8f6c24cd60d2a6e14f732bc954f6c0296b50ee21a3daafda5dab515f2bfc1e96c603d9f82613b8ff373e969d9116b965fa892f5e6dfd7beefd1d40513efde5ee42de3f473c5e64cb9a8716b736bb9d40bf54e1ecbb14201bcdec781abdfd0abd1bf6bdd010e58624f4e2586ffe199f4827ec187f7d3a0208d299924d8241e4115f69051325fd08a14aaa5adf129e69fdde14c738cd3144c25af20342979b60d8f26baa55294786fb29585867e27c4cd7202f1d81bc297a27fb6f848634e3cf3a6d784789032c386555453f72935f6f15145bc45a8a6868471f3868f7c1e31fa3a09c8acb52ba5e14001f6a8e45addf9194c562c2b8753b65ce5e0e30dd7e6f38655ff21c26c06154f4e61f8eacb36f30b4f62ec8b353e32a0f38861dd8ca111c661f2ed8493dc985976674ce2e22edf55b7395df986346df620d3ced3e8161b0ac4da037247a1a35ba29fbb7b5fe5806c52a1efd8f55178a77a69e8c8d6daf43f3ec8b07273494cc0201c2d11bc4b260166acea12fc96b8c8f095b50be4011cc634127706b28522a7b0148e05fce481e0f1109c58674894cf437fdcd6d504bacff97927d08b2e8ba5bb155258fd22540d40e98ab6660d834482b7049265d27fcd4c3c65760daf6ab0e189883cc5bfe92a002464ce9c5625d7038984654c0d7c7040970993b2e431beb033f7e54eb17f3d8a0e5a6745b66bdb1da56021fb9bf655caa57ee5ed418d914d520778b7c88b88669d55adfe871cec8e2e4b50390c16b9933f1d223bcbc994f37f287ba8d7e3741d4ee354e4505e9f5bef98d328a8b533741f35f0ccbf1cbae19a8344ba630cd9f4dcb77d1ad2b42434e02f59439df82451bf9853f2c8b004955cd4f5d4743efe0dd22389569c6a70ddcdb62373bebc76532f84e4a9d1f28ed0e334b6f643f974f8a17d3e4a7dceeac260afe6eeb85d814086b4f54fbb3ff58e5ed88802b978c075908cdd25036a6e2d3bb6a6ff23658489d72cd27f722072ecd5e44e42765805c1f54773bf883bbdac104c0b3dd042a9449e5e845dd597abca3d2dd9f19a5d98c261fa22ef7d41c30a76b3f1a8ac06cdf47bdfa4d697626d26eb9571773a42034fa661a8c6ff3991d861fafc280f9c1e2b354b968f4a2803d9e605776888f2f30c1bbfc542cc1151c428b9010713314ca2b1a5b52c26fe3becac1c1311521c4eea170d70157b61dd67ccb67a5acbd183f8978d262a9c5704ded75e234955f0d9494c895ed05ad722d41aab64e7f6ade40dca8b918129da0941a919b38d8ce99144165793d0657952a6091bbb83fbafc64e189f54e93baab4233c1a176b59579fc6539de4613914a8934f1bf1beab9d29e46117e575d42822debe360e26b919d8ade89834f05690e4b9db51f2c256fe89e5c7c3052dbc5a8d60c464db1b26de0960a5f9ee9669d64fa6a577da283cb9a9d2c042c27e9e0be8f1ba5a78961cafd59df9a30d5817a1421110fadeb343325bab7687637cc055c6f03576a6b79645db08e4fe547386d21ff78967e95db67e7906fcc6c56f90ce30c50bdc9318ef39f6cc876b546f1968264d7dbcaea3dde290a3eb2f16c431bd96203f2aa8d42d48f68c8fa0f6d8462a7c7841baf3a2992476627a99508c12295c634b8d2890dafa9e605e2027b762f82d042c2a38cd6db30edfe92cf653a75170dfaa7154652c9ac18dd60f81f0d0f450b9c333b10f06469cc22527c05d302d674ba6f15c66c3e032904fed1de87a8d1a17c3fee6aecc7c93508903a27b329b90e942ac5d9c44853fe8f570ab588cba572798306444b862ea186b9f12b12a481d6c62fd9d590fb4e418df580d77848112a7f52d039cc4504b42d7e2d5e10f5060d5e8fdffa99bfc92008d44c32bbb7e33d05f3018388ecc93c920b7724169076df4ddfd461d3b5a54037fcadf1a34acc6a3270963d8869fdb080b8143500bd3c270a9b8ee2bdce296f6ec0a6c4d2db76b7c2df063861633c79dda4b866e3c59551cad9dee819111c296747f272b9e83aaf2701a5f5a047955a117df4418a59fba2499b18c9f0df1a4e3629acd07ff2a27bd99cc24ad0888f1278949e01e6186f9964ad6cb50b9310fccdae622f569e7b75c0b7e093db8f05f4ac146525ba0eee48a181e5fcbcb5873afbbef73a2da08f8d5c1ea87b93bb2ed9be831555cd0f5834d238a838be9636d99edf4a0da5739f208e9e260bd9f486056a28902e1d59ac6ec2035584f98c9de2d54c3000276e13640ea006205b8c0d4226c4f1033cc81dc73eb1e7d9598fc8044344ae697abf70edf3e71138ed9214fdbd9cac1c58ec0b0d4eab37db80d5d051061043d65d38de0c28ebf87829e8a6da90daf769865e5ab1b6f2e03e2da276625f144171c95bf033ef4b2f217f8f6ad837dda32eeb1e21c20015c82e21d30c79c61a87d8e6cd9893aac2bcfc8d5a913d0dc83d0dee2f68c6b89977b9d5d8e2bfa2e8ea72830c019f92361c8746bff761983f1c21c3d94a05f2ef84e3720613a1f87f470ea3e15544cfee576120275e15f82abc436c9f4b76b707fc6ffc79f8df3ab1768d087e08af8de15c59b89e761315443e33e7e1989db1ea8d283e010595451da73cb123838b8db45c8b9e05ce3b1469a003b109cb8220f0da5a88ad5d2ec5c98096572e11dc82351cbca8b3c54ed16e7d41be1fb31765ca98e56507ca5d20f2642a196f936ca12ea483c77cec058145b949dae9649069b40d74fd2952281d41c5a699a5c6ae4710abc58d70695b2c914368094395ad34f87c307343a41029a7341ce376a58b93fc9c535c83cc0b3ab65c2e1530911839d8415d27167d26208839a4e58b7130c25b274c2ecd3f1a0172fe6182a3985d2ed25b5c365fe32446d3b11b8c2f6c5dc547dc794b5a518ba8cb3469bb0510963952ecca86850feed85e9c467180776e3a7a818fd47c230ee9d6313a40674ae2c90914ddd844dcdbfbc7616ca908dca0981a6766e110d207485b320d0d8d8c85043f4cabf3c38d6b51324ae21eb1cb61266cb8dbf4767e7f7eb0ad029eac56ee7a4d778eba6dff2fcc20dec30d12577e1c029d3c76bc5f85fda9de279c27f72f1bb0a9257e9dd190a92e7a079435e943199e81eed86e60edcf5a854343b4ef67d63ff02fb44fc18a73edd80d2b640357b4ba9db305144d97794a9c69561a4d0fb8bb9911ebfd865d1dc34d5e66ffaba3adc0b68909353816eede403ab25931b1a4e2cf8857e030b5beac5547ab09e9d7bc4157fdf6868b449129f35177ecf4b09f5dc1a58ec60fda609bc83a47f532b8269ba3c1bf2534a125ccdd6f14876e5de52084585b17621cc191d96be8d8ed07437882ca6cce90e9ee4c2656dfc177e38624764fc3b0cb73dd01461431075023dda9f77be60a1dfda9da0e5c7b9b809058f9f7f46847b0300141d3255520ca436cdff2745b46a3cb3f3b216de24859140b002643f34ddfc9705bd13b8d8466d190514b6f5c9b5571ecb5f74314b2acfc70c1916ed11a161e0ca980e0f1a7f50e25e8c252b461d7a2c92262599b921ea81718818635126637040619ed1c020298b62feca3c880c339eae1df52b8c19011e9860f9acce008abeec38e4acfc573d7da80110d5c9e7f25ec64b518b936243d02710983f1a6a1fb73c40ddffb8517a32e9e8f23f2e83270be24c05be07ba6cb7f43193ea050dc61f89c0ec5dccf121fd1b7b08ee0f84d231747af173186fd03b83b98458b791aabe08e3c0375f23404fb6c144ad83e057bafd7a4587a957d1eecee10092f248ce25db106dd9c8e5922d493beda16455371ce98640dcab853a9b1968b9733ce1334e78a6c1316dfc73c7106af22bf90ee272dcb265858c3d4eaa4bf4d70e5fb1a205922963a100d873a084eeb58b1bc722fb9d19d4c8def1ce037ce841af3eb30f3b56222ae8627c13b1c2a8fb6126bac613c57e4585aa57522717d1919ee2b2893a95acd2eb0fbad59a47a4441cfda9fff62bd32724d78cb58b9dca9452cc5a21a0afefc0e8bae80e953f092bac1e70f6985d45823bcb2ba5212c3230748eecb03fa4a9e399b653c5e73e59bfefce1d96394d7122a5950f4f766902bf50dbfe67bf77b123d38316cdec6bce492073f566ddf3dc844e8e6a4f87f082f746d6fc7aee683f819c9c16dfec60b44aed1947510e014ba34dbac80b91046109fb87b78ec5f52e9e964a71efeb4014feded9541510205c7145c29153c3c160b94f00bdd201a9366fae60ab7da2e7a7761f69fbf222bae84060b21ea091815cd58af8b046f84962cd0d63493f55d03fda397cc7caf0fec6452d0388ef468eb4c61782f705eb5425be722e7b7e760345345342b3b66bed26d405d51c0852077faeb8cd107315f26a438fbc8f6aa15173a673ade63e32957ab637d2146edfbedd49f30d0a7e4f8a8ee261e8546a7e292251d46fb9470b2d6cb2dd96d8e3b2f73c29909970ffc74e750c68f663c5964a093848b92f764534c813cc6425788d64c5520f443cd4fd5b13ce29724d53eb055c258332dba84d4d4d31c656a647f2d3d629b2672977a8ceae5338096ecb418c092d851c398029663da3a36285c538d211d418e570ac8be2566cbedaa5859765f79497817507fbde5fb1c9174da730d4c4df26489e4b26b9c6c80d928f6c29ffbf3f14ec35dfa45e441341e8ecee2531a60a4657e95a947da4c8c43c8d1fe33c552aa5a2acb453240c5168da213c66bec5ce6c130180f5f3b6516fb4dd69aa23d534073d530a94744ad20b1b91f8c28a8c7193e68cb447e2b7fe3a4a43d1eec4e929bb450e2a003f94a39de5f971ae3095c9d51e3efcabd2c8f3ec2c231a3b3120640ace76cd8639dca0ff911864ec641eccaf8c4fefadc407ce5ed50513f5082d41ef53c83d038a7ce558c1d54346c989be89220d26782ac468f869d25b9f793acda5ed2c98aa9ae6f2853f80e67c887e263ff29c489b495f935fc09adcc07e7560b544840933700576d629586456bb2d3249b66c6b8cfa6dfbd2658589db4dacdc568a16028df5114081c591e8ffa6f8ffcd1a990cbb7eae3eb7e5ca0b94326a6aede51356489f5756a4c0b678280c1173fd40edf87fca7c1f0105d9f6112e5de511669b5d8b5002b0c973b0fb863711c83650d60c892900f3c2fcd4e72785fcb0627a65b7b162e5057121e76ca3feea107b9619da06a38c53ab64bbd0a13ac872bf696293364711c228e6465b6af86c993d4cd3409139af6df8789eeed723fdd0e92e689cd9f307a558b597e74cf6d509c301029eb3071b90b95a358b35bfb6f6a615555e7cbcb4407ffe6f986c5d22c5fadda9a5f426304610e1a200786bee0f71ce59bc70ea95f39078f8a62ec43a4deed7dbae274b764389fe416515dda30a5a54dc69830d3d52fe31c83a5edab994fe393a2ea803554a177566800d2ea678c1f7652d9cc4dae45c70865e87eae446b52f4c56bf26625894b4121d100f3a8849229ad44658fef0d532231c16891e4d4f55494de7d78a72d20d6797357aaea4e633de869ca6b0d45ffb96c21dbda3df0bb8b4ce9cfa0267158a8e4a253db5520be43dfe5870d8e6d758587b1cb8da93babbc21c3ba881780f40fec6453098a7adb77cbacfd835d23e6f66236e69485c4cd22cbfacb439b46caf118ac46768e72cf8a285223c6a645ecad35762020a11404195a07d113bbf3d805fc557c58b75310241dd60396a215e6515304e84bf4ac80ce64e28986dd85d1a896c234be6f94b1f39dbbc5b557623c72b00057cc1181060a4a3616cb40f8ada83ae6bd831d66d125bdffdc37022c0faf6c903fbfa1d5a2716c2191b2d31b1fe32a7ba48e0472481d7af69fd52f357e758e31190f9c04f00ba450aedfcbe47397286c89c9d4834010969e65dfad5b67437d68f106bccdff8c54d861bd90e80e88b49728e36d3f789a92f89552dd369bfed4c6c8bef4854190c10f54c61be58c82da5666a19a085c5446ba3a91992bec69b85572091d7c65aea2dea2c0aaf9459fb8c6b4f35b176554550e27d74f21337ab6d5288cd1bbfef644f6d62e52977fc90bc7854156f9e2109d9443cb60171b5d5bdfe57aa2bdd40e5f4d03e5e64e12c02d80cbe8377ecb86e31d20c7b46b7ec63000543ca60f14ce779650f7b8e845ac1580eba54b324f44bc4077d24b4f00dc13f912ace4cd89fb49e0041e7011dc99a6e6a114e4f361e339af942b1a13b6ce2977f370f24e97eb46a183822505d3dc0b0658dde5c80c3e6ea452ee774a695dc9a2c26381ecd0c818771cf539bb32f4354283950cbf06a3d037d16b3f0c7db2d684d3b13ef6d84b973c32c0547e9ed149f1062107273f2db313224fe04f90fa522841081414a5cdbfa612098e8301345269bb288eafa73ebe75db59426f4d7857cd32c35ef11b5e0de0ec1c06de0896321108c2d423a2de187f2a877330bb4377d1b696b760db3430d4d57adce4d79c878e005c2340d06efc3bd9dd7c0a943baff5c8356639a12ba6b16e925e9946e6036f36d5fd2bb9684ed56140476ec20870d8a856da4554076d52af50c945122f2ceeec54834fd810ba383b5e6f4410f04136652687f8187369841adae0917c7a757235e3037178d146191fe8b837879319a06551cfed5ccf2833f11a6fe8de66854442644a4a6988184f379f4e1e0134f34196ffca451649898852c8e45fb9e548038e7f569872ff62d7318ea82c57570957033c50fb06edd4c22ac9e095ee4348b3ccc109f79e3d612055fb94349231b8979f49aa474f9f58e928a492771ca19a4051a2ca5657eef9a59818ef3c7dd3c4186eb4a04a4481cd81bb262744fe2994a6942e1175750dd0acaabdf90fbc450ea79754a5bc3959b91c322bbed5fe747979f74c83d6ad423ed60105394dd0be294281b9404102cac68928f60366ba1225a6f91c36f3914ae3fd96806d5e18b59348a7d6f1d0dfc7fd7afb59ca5fc924872171f15845a145e66da04c676ca88d8ac0744e62c55a4b7b7f6123326d5916cf8a34d756a6165e8d92a82734576cf50cfd0062e07fd90bc788054bd516bc0c6ff1f9bebc1606b2b01f92982d30b5b4d29b4d84fe5be52234a7841ca6336efd013cc92846fdff6f88f1486a88122122ebfaec113e324fb8b552677b0db22f27cbc1eb956de947d1b215c4aa2815fe798ca0ee873c6b85d23f2c6b4e4046abb755b093c9aac3b574bc55df271d63b72854bf984a678c702108141a94827effcf1f114d4a123ee05ec9fc0771f5566f750ce333e118adcc8a0c7f9de37d55b15c0b3a49daf63dbade47052ad8933596090657a3fb56b99d0cc13b92f0ea005f6393e84c5b7fd76d68942d29fa0c12ee28695a502ad0f9a9861a24289542d76739e54cdf32fd950109bd3810d0833d7135d5b5ab62ad2cd71763ade10dd655eef39a461c6036a89e92feec9293ac22c3736343d01ae9e8c548548dbe73a9ad5950c038a2d1cac87c886ad85573683a1a9fe406bf3b18d648493c60768c75021cc1a59c59ceea00c548b47e5323986c879d3a6f2d7a598ecc320fa94dce2f8205164fdde7b2b172c5d33c1b71aa78209d5d1104f13d712e936ce7d942d6d599d845f8cf01f18c1b4bcd5c38ef66116b12b760bac64befa849f48ce432a57a27f220792dd8abd11c8e7f5fb8982ca6713ea774aaacfc6a69a2b55e643f32c3de47fb836fdcced013360be62a4c237b4c178471fad23f854a176721fe58d1a25391df9a63bf3116428dbcef4b5e76744f6b6cedd07d3759e65fea4b342c64896cf59bd4e5c2897624207762ff4ef9465561ef2cbf216ef8879989d94875fa14f78ae4d7c5b2a84ad62bee94e17af729731cff7fd07e461cf1e65487102c558458d0d9ab91f5f8fa660b3d31c95edaf5656b90bde67062f2b11f1845d6649caf3b1b9d5127dcb1020ff8a12c869d323215264bd9f57debcc7153bd387f15e69a61cf50fdb7ab6e8b8e89477b65ecd3c0078c8be1760ed053a5daac9f3106cad4a7a8240430b4086cab463da9c822256d48549d10fd7e96a037202cf8117340cec31e357cf0d6c853b09442b88c4c33ba016a9cd5485a104ea33238948b3694bc0c535297936019ffc7ec29a40eae0f34389de6038cbfb629573d2a515d376aa48da182dadc8c0c0c799552cb6d4a26eb2bacd82b0ac6949d551592bda8f5093890150938c2099a47c4a645ff0ef808704c0f2fe58097bcfbe98a616a28398f2f7d6c40bf48ddc43f9bc5e69bc35daeb319f5866d00e51d606e46e470e4bd318b70ac27fe4a6de6fb444c22f8016a6d2b3b5ca55ef6376a0f3e5522de507325ece06821ce5fc99b8cb75a33ed76ae6c985bf4ab1e3d2a645c51a242ab6d9b62febf33356b5b6de80c95ba9133c6b3646e4e5dcf7bb0a5fdb176b631e7d3a961e17a02990751c982f1dcde5d05fcf363356ae4dbbd25eb58f1c732eedc4ca6f5d04a8daccaa8fe3726ef0b81dc360034d62cac842db2c77f588d9438ddd57918e7413cdfafb1125ed6b11f92dd97722a79276263e639c55f02b8cb3d0781e02ee07b07e5ce6bfbdcc9261244a925dbd3b34e475de1402637e465950755a296314e31b1779b11cd552ad2e6626c05d2c79525ce53f5e80025d4de6f6c6a2048cd4a15246ee16e9eed72a494c247e5c68e282db4f997cf8bb7b275e90d3aaf18c97fe2245992e535954cc74eac21281f129f5de6f70e946ed4622df3900b23d674db32c527a1d0ca6fa89667a198018661083cb05201ced4166efafa6673f4a6e81c6b11289a18cafbf135c909e54bce57802c7eabd075a7330a57d9203437802bec7700da8c067959b80c03ab457b7ad0db4aa69609df618d8bae7b48d43e705fb5dd827d2730b092561653fa6b520eaa7e22b888d9942417a8aa561836ab2162e49346d3417c056d2152b11929466790971a855ff6b22992232d132d5e05c5efed07ce35b3b4255c4fbb6973f6929551274512b5d8cf4f52f23c455dbfd837920d2f3f7a20edccbde56e0cff6e7124250807306e6b02839f583016b4411336f7a1663b4cdbd580a82688eacb1ae4d2d5c267ada62c9dc73077e29d4f2a4f7f6db6c454c6edfe02cc8742b70c1c13b5a51c3ff4171b542d97271cec8af4c88e7e020040beced10d61225935066d8d6d08832d21f8eacdc4893ef3b84bb334dd7ae90e4b23deae90753ce40f9de31ae855a65e2221ffa5700dd63e2d1a690ae061757cd172952e2b5493392968d9ae0c0e716dd2877bbb1c6d4b7b77bd595cc67c41dfc0af6003cf762c3e0be173fc9661dd275678478ab814b91b404ac0f0a5fa0fd1da20de4169e73b03d99f65e00dbbfa276c18ed951bef7f3b7dd0f62bc000a4e95b0da0f80018819eb07fca3276b4e942f4534d44bfd885c7936798131f631e211ef50fee66c51bb92375234019127104b721e0e5ed0ca13fdb57822fdb6634a4e070bc38d1583a9a1ac7628cf1844a33dc5da4196a594bed08254ac7336db6ea73a90a4ac474601ea878d36e0d4d6a6a1c137ec31a0a76ef96eef94f7c5a58846345e073027b6a95bca1a1dc8f095c369736636e21089d6e99005a680f082d4d17ecfdfe0840f099ea761ec9856c401fc8ba396e60e53e08a594f5ba948298922d36d246311cf9c06dac9c4a963f200e8d08835ad13f4d1a1f3c5b9b4110f2c8ca460d3c2a6cb39a90b363326abf815819b0aeb1a8730ba4a294c269a06f9e588d9c97b1fcffefa5958cce8d3841290a98dd3920ac51f161180240adaaa0fdca777bc2ca5772d90aa9609b84ea823b13b114a104ce0b20a4585f4531247e4d3b63143d779ce5a66c862ce953b0420f3d61214b1eb565ae0dd02104fca5432f40cb8517722f6b3ae77b76ba09b94053320a8317a0506085f52a29bbc374366fbc310b2f7956e4c36241460eb9b1fde71927b79c24301269882034b001ac6e776299837fd3f1ec3868c18926427350765b15530e64651b4a87d0111ca8d95e3cdaba9b0557e29e3678113e7438dbe3233d0de92651d9a57a333ab3061d22a3082f841430c8775be404a5fc7e2a84f6704f85991093eb874f40831160fcf826f8b857f879fe631163e4c69a412474c35b0973143aca4bc09e1a599a26e6776bbf370e4f7ec8f5ce8816b35db368fa25c04711750196ded1d7635a3362df63493ba1257ff9f5bd3cf5bb4a3f8c75fc4a016e881cb29c662ac351981850fb09e5cfc14ff99d926711be7aeefab119c51acf17ff4526d432cec98010f6b7167b530582e044888c0eb64b393f0e4ff7ad2b991ebc801786dc7d21cbb809acdb991727e063740631c08d248f9ef579157d022c4a35c0cd7f086e8089caffc8dd06d55add2b3eedd349acb7ff27940af343ef273bd00f4c5e7fe5cedd3db2fd5b9627831883187c440efb2fa299961cb9646ce801f8bd1547e955be187f7d5f1812ba0a33789af65d03fdaecc123ed4c79a64315808b8f4b92a4f82236b4e463bb57258f26037ffa1eb7cde181ec9504a846aa90eb0e5a383fb231a807060e8279e3fb3aded553ae3519a204b96698c684c972c0f2b45c6e36c983aa587dd927506c687b2158ac6ac23bd64b14e937c293941711052c62b5c21d04f8384bb8c38979e433680f4127e4cfe8d1a9f528f28f3bc106a4ba20071b5edaf560edf8fbdff9ec7ff6437a5ef2b02f46e63f774f2eb7c1b57690332440664beab91244b6ec05dbecad63986f71708dfa6f1ac6dbea407ca02be0dbc48faabf1205a47cf930fae310f8df86e9fb2e0637ac25378d33d3ccd0e123c7a78238f85cc20326d7194437a8a9465b14e4bb59ba211b671af49b0d166e82cfe9cc566bdc6ff4289cedf0b5c4c00cf0dcf9f6384740cc0ea511d4ddc3dc7729230eabc2a736a87551371f640a0157bd3120777ea7a98075990d43d2542374297a19fbb57ccd868c05cd2dfa8f62ebbec989e65b50606e1567edf57a85f3b31179cbf6f2c68b4008dc26a6bd4cc9bbd1a7ee09a2e44e160d4f25f7aa5093b0b19ca4e155f2f716078f8b57bdd6a5aa1857d6ef12bb915ae10912cf45d4af4e131ba0682491f7d233100047bce46d21ed591dae4ce7098e0d20b9bcf521b9c97c4b5b64cf50b018142b4a8e3eee7af4c362daca983acfd589e0df15a0b1d38262e1a4d8ada7951408cc073107be104a7f23c5d15b3f92245d2dd16820308fe8872ba269f22f19e581592bdbe304c58537f254e361e46e559f2ed07718e0e96fb94152b551e693a5bceffb28eaa70f7de759873b107d16717d2adeec67c6b6ff41a64d11ef49ab466e2dc8f60226f08109a3e4bb91e0df1b94917689a8f1e81e057f2064004beeb028c3ebd874d1dec1abe911562478ace86fe4facadf02601d2e170d12b75abb88a88d446e7f4a16d263d60a83be66711b6aeff6304b03f9250300a8f0e2e55abbea43b1efc5de7791a6469c0aab1e0356c6e7697a9424ea706305d48c6bb70e4721005591999a9971a7bf2127435bd8adf67f7f5e8e96d0076f37a671e7624e0c694e4ed5355e8c72447c6746f2b06c3eee4e7cd18010e41f0376fcf9daee1d4d0813a46ee83bc2c843650be1690fc0a0fb4614e939c6ecb55383bbb48d24bd367c3bc78bb78a628095c9a60c735e2f53534d9b09bc4087383aed79414a54c31639e679cffa74da15baca5ef078f57ef0f62a371d49a26f82d577623810672d86d590f4b8f74f072cab8fd591a1d49c97d4b7f5bdbdf48cb528e3ef2f229b6a101cb7d1f9c76cc2f95aef2dbeb9c6e73e4cfe064faff59650f583bdb57955060915a3e4cb6f2fe76b55df3ae77b69eb54192971a2aae5cc5c4d4162f3ca3577735d505850a3e8d72a99b168994d5b3241b10d4be2c3d6bd30827b434e426e4d025217d8448aed2e881a1af3bf2dfc213b83b41e8ba4b0b9922741dc0beaab7488fc3a15e24c45498b47060709f5228f17cfd2cedbcc81ec7a11af852ad670b6ef78232f0d4e6639ef05a3113e8f75dd8ec60baaaa33a3b3edf078c5f3b9b26d70dbf8e9b255842dd3dd98248bb25c59d9cbf58fc3f415598f6c33e08391f82270f2cc6a4f8603a97b9978728bb1c4cffccf41acead136dc1bc8e8ace8abed9496f49850c907ac808adce3cb21ae2a9b6a344c826238304c08cb72f0288dd8180d145ac47ff575c432d31631c9133a11e62d590fbb51552ecf57c0322b46e5e56093744b668f6a9ecc23a21664dd82d3fbc9532397e18618b5d14eb4d113c2f1e1446c7c984cfc67280e623dec1bd6523afe4e7b6074e142123a873004f37bb99b5fffaf925b173482be0ce31b84a3c2b4c99add3f4d06239644e710084bd0afea0b642f4149e230e7d391b66e51ed85ab3d59f64b4a924f5abccee0ef1090438c114864ee199e8fad26cad863468ce5a0143257ef6f03a5e26d47a67ea0d92c46fabd0dab671252c77690e23f944398aa327b5994e230708a4e83f5b97e79247912430867dcad860c1435951370967a23d9af406ed05282fec485c4cdf70d015b21b36001dcfe083bceed2e9bda3bfff3e34df1289450beab3fbc510d6a8a858eb74d4eca310ecf97cd90d28aba9d06c60d147f798ce5572a22af505ef21734739b9d8cd879874eeeaf698affe95ee0b81b6ca1d719b478f5fd997a9132790dfe0f6aef57c568bf52fb7033942fd0e5cf47d8370a39e66db6e0d804d54bba84f37adaf1d31c1d29ae4793e6a84d8e99c6a28a12876ee2672eb31922cef33db1fec9b943925a47d65ba4039a7272ad3491764eb1ee98456533b42ca549d14095680f7df0735aab842c6e4d818d0bc15eb7c12a16c166f36e01a7af095482102450ec9d9063308f1183197ab4ec4b37aaecba3a6e808fa2fd7daa88e2fe5d1e188337acd503b164efe5b94e5beb6461d68148520c9a4cc02f6b38c620f8a5072fad249efb4354630686baba43af820927ddcc7a322cb1a27300ea5229d8b900a44abde0ad562acef0aac12ffd48cec82626de7a31fa39b225ad3fc613d005731c4d3b04981dd1fa0fa21c7381dcdfc46f9a559b6ac7528123c61c664fd5a3aaa353d3c6cb4a9bfc7c0f2f8559bb6274e32b57a1382e1866f0e1c9b773d6f950d5398837d1d1845c56d3e46c4a48494f206ddb83442a9f3691b15f9f7bb3eb101e884c09ed83c8796b4f024ea4ac718a877135d199794761d77c56f70313f38174c0934c3299adb6627501446f4a20dddff3cf355b217025a7022c14f18833e320d74c869aaaa7d72642559e7776dac3e5ba26bbd628bab4423135e4ca35c5a78cd974246709c3e6e9f800402e516970cf8f5bab061a80230a4f2b14a824619b44197e4758af11cb0e40d266e527fa1c61d9b2d617f60633066518bd09948c5e4f5c1885a06aaaa3d59b2a93aae177fcb0b4917fd6f41e50d2399fe780c79f4483192dcf14cafe1fdf69ff6693b4537623652014f4d67363b8885f84d3e40e776a1ce3bfffcc00f30a5cb94bce85b348ddbdf58bd0a8f2a20be1d671a3099322a0fd962bc2c3eb0106518bb2eb730b4b66911a01f1db84065b79e55dab1332fdcc27e937c585e8ce75531d3951e81e9d5b336c467110175240a44e75640a7f3b2e5a40e9d526aceb92aedc6d0ab7208f688e0a4136de75e522529cebb42ed87a6170ec403bfdf77c2009821d6b5c8843f122c7d01358f714fdbe82564034b3319cb0a09177bba91f9caee46d897aab6e07d39086555a83917699b557e17a2f6b89a290c9c91edcb136efa6b1f8dd82d1cbba1b13db938ce59d09c89b0fcd21666a218b8885553e1bb67b01f9ce3f48571b0eaf5a1f8c98f261beb27b151e25d49787c325e553a8c2dcad4aebfb911da7e6909bf35a954bb59e8bca1c19f4e4a203ca49c1d51db890b5f0f64c4f5e2627a22a6b83cb92a663109decc5456e700558e1217e4283f22cfd0690fcc25d16d003cd069727fa86007e7b11f7bd20a2586865273619d1f5efaae622adc0a0b52be5bb466a0d619b767c9a6765ba1a5caf368c58f30a1ebd42de3625bd23f654cea1c27b00f457d77097f1907844e2ff44314bb742079243a4e264e923d0a5dfa20f0a0adea4654c06f76d706d35e1405305020784630c899c91d011d77229303db3824ee0af39f28211df307454e6427d8f24ba2987c917c721ee60d2063a8460846eeebbca4245d41f79164c4bc3664939ea1b8355daaef9b22216594be1b1e62689d7b91dd115471cdba01f5996a3f74ee1e47ff045483eeb05b48ba08d8d7dd6bf9f0455d12c40d9cc2025d89bea2fb457b1952d3538e091fa006b41f2ab74b2194f96a560ecb4c47962b6b16d4341c161e3afd0432058abdc01951aba637b5f77ce42023304b04c6b4afbf53016f3dc823c604e95e49563dfee71378a45683f5d6d22c0c6a3e67f95250466fa616e2fe9e6964a78929940fda932492c5cb4ab248b2a25bbcf51e4946d87de92eaf533b1512d66557cdd2b4dcbba7ec2f26aa8a92079f997a401a3248972557589faf617a3bc129f163083743a9ab0ed57243590cff87c264d25e984c1e58983230a22d6c964940e118429af4a7a38b5fa5e58df4468b94b58b6b74f515d46957b205fbabcbb3ac3f3c0340221460cdd6b7da775086f4dea8953e74dcc5c7c88ba16fe99bc71035718929d203c22b01f86761dedd354c6fccd05142a4f3958b1898c47ae9c6d8905b533643220c6090cac2bf7796e805a4925d4b630aac795b362f94ecd902b23bd084c2c2ef5e6ce73c696f5f17d62a567be31323d3107f5113a8da1e14f4f748e5330c008b0a92c0af2156ae816d8023e65315ffbc507d61f4db6dcfc44bca2e282aa38e01ae92110499ada27537f5ac8723385b2921627044ea0ff53480e43bf94e4ae7a3e263350a1c1fdf3764ae477785273dfdd4f88a7219777744b3ac9ffdd1180827ecb8dd5b531d04523dd16560751d48ae78cc9c3b968ceb75e8d77538c7e1d8420f7b9dcabdadf0922dd787eed4014bc5d42a981bd7480d7cc5bc336bc9b492863c3456546838056064bcedd2294b4285f37203f3d6dc2423d328b5102890a6079decaf035051b190e40eff8a36396aeb38d5c203d5717f620f1de290d286b63f9ba1c07821212338ce0929b6a26d65b4a76e6e47def55715c93c3dd3790c7afcf72d03b3b7678a584abd347de36094a64a3a83f577ca5d544257fa6602ef6fde6633a9a1e6741dc2bda400dcb25fd17febcedc9e8fc1bcc832e9835afad5374d5465bae0b40b60024dcf106aaa36d9cddbaa827346fcbdd3fbb2e5902161de172c69e2516f3e2eb2da21adc8fa07403d603823f6ce7760e236a837087810f5ee0ec7ec03374ce7dde6228d24f51ccdb50efd00e6528764ef6a919c3c113c8169f8a3fbaddae15c867061dd0f1b32766587bf465c285b19228cb840e9d2fc10a5bf80bee1d28be72f4eac05bb2c59450391653894a1c476d1b1c8445628f2840570ded916e8ccd7d23af35c0d590d525b516083addd247d0bd291bbd29d0f11e935de1895296b3810eda45c03a354335a2c4be64d45828a5a0cc075df85c4863926258bc3be5c855333fb314eff26d7e2c831e65f789dbe4207f789b41373f71fde7b2a442dd2bc52ec7c1dc82572f9d111497cb6c3d454ec8b0d529eb9022709807f2239097ae9c9a485612512428f505f42093af371ee9f02ef4e58f2826a9ecf14042b050245c8777a19385d0922fd14c846db5afdecb8ff2d9a40ce1482908817c1b562474bb6d7ce26502c521b78c21b1456fc84e5b38fb41977175305515be696cfa2e9c96eb3c63fd9e05a592b8f796e56c2c9177e836c9fdf4fb9accaeb0c90a35cc66c383ece202f3849222c19ae8b24c1c43eccda80a7835fd9611770b65bfff6f3755ffb28f8f7a33654791936cc59c9ff17e18c34b583628e56aa0e24e1d9261c2bfcec4343b21f22fa02ecaf79b503fed62ace78638c763028bf240e92550b8215fdd12db4d767606082c116056ad86f0ad13874b3d0967be0a558edae8a6223b45a9fd559376248fdb28e9456150555258b44d625a88e06f01dcbd7a220f513cdd74277da2c1d2c27218c6b27a22f6019f589b23bc65abbaac1a73d07a050c3fee0ba26ad8e52308a487f6fcfe9fe9485f87a4248942ce050a5108aec495fed3bdd63779dcd7e3baa1e3b457db81a8c62c24316d9e2c7b2a657826bcb1ad04fa3aba96ea5efe26654b9400fbd948ded19df8bb672ef3d44e5608eb75a28d40c7541a7cdb1d4705fc655440a65c2c2e3a7137c925033cb0d47b2dc11a6f7274e343a0fb851c930225e736161e7f2da23291c65d3561f61f94b5811691070e043ead234cd966ec057005ca396ebc0b1b00a839cdfccc6cbbe3ceb48abc3f433a765cb83219878d5402e1d397c1cf39e746671c6c133345e0bb638e30414b8a3868f72335b58ee6c6f89eeb52f2bc087ecdb1b2199ad32dc8dbda8e30a0eac257b4a6024908f701d71e877601b69344c4341e346c7b261916a84cf22a1ac8fca390040e76dd569f761befc7cd0783cbeaed74b28515904300a321b3ca08a8ea4c4a1fba3640e387b2304f752bccc6b99bc48590fb72358adc3ebfeb674cc91c82a93be3f230253fa220c5051a9f8ea87b566a02ab6519d3eaca2c163b8775ac0d540659204af45af5bfb73ee4965a8a317ed01227d558f63f118f3810902db0e14f2ac99ca24cd7166bef628b30a324ce4c93b78440fb093e32b93b4f03756adf3f994d50210fdcb1740354397ae966d5995a88de34a6344d380c49283a28e85f1c7eb1510e0d0310da03d90ee0c9195e8f377318ae719bf74401ed313b523de5df5fdb85ab0768569f5da01451b4d6b5f1cef3610169f9a6ad5929f74b2eaa4c23af1571f7c696a0af35e1f2f4e4b6695a59a943f8366083fba710d36f565eb806d3594c97656a318426f59fee830d4f90428c2fe25e56e625468cdb3ca115e86d894db83de7c801ebf7315cca3e137d76d48d42f5c86565513656709483c9315a578c55a8f23a6374ee5ee895793ac8d406616746628f663e60129e9679ee70df106d749dcd21d1ae9bb3798ee42dadbb350df3bbc914e3262a2fd7314e2f0e9ff45b289929dc45e8ef2b04f52af20ec27b480292e90631545ac37fca3806788ff8359bf59056951884147a82ca80d1ac0cf06f46d7c3a8201e263571ba63173fd0f8a52625a0d27a58fce358347a1d962d2d5a58aa69fff32b8362db29a7e762c0de193e733246283db2ca0afee37685059e773af61ffac7aaed2b0fd528153bd7574e22df8243a6bd795c3383a997e3ab1bf296115eb0d0d9e132a9b319f328e54fbe12faaea724a728c8742a54800c4ccbbc2b5d783098312cd27e0881a193e5c905379edbb64e4ef73b6ca6884ea95d774a1b1e9161aa3ddec33ec85a1ba2232be27560fea54e01ef67d607d729254e79312c6a416ab27710f9eabc0938e9d3333aa006fe462b7ca3ed8aec9297280992fadcab399e77079b43d1aadc3e76a7fd39a3548a348b8ada9dea3dc268271fdee9838beef7a2f9957a7243b0f2d70048aa475526c6d5bb08c06abdaa1525fe7386686b8944430e2433a7cc3b2966272f1f45a234f32227dff6636b7041b5a6cebd6cc779e5770a098922ef4118e04cbc9c2711b3918b66d3ce8239f59a18281e40008d076177377d5cf411ff319f3417fdc1a138846b6a234acecbd4f786d3af7e86a10bc02e56adbdac3fba6cd94fbd7df432e2853a587b2221aed7f27b6586e3b13ecf28143a92c26194a93d5b9af3b95af3fcd2ef3710d980109bf767a6622b7ae65814668c3fab63da37e9ef9e8beab119bde63380bfda658b6f2e2ceeadf29ae3c5109bab859f21a654d853c344c6b4e1b3c14775d28babdd7a961fa572f8568a08d63544bc2e5f01e86235ccf3cb9d9683c1dc7037347fe414f0c94453584679c20ac198da8420fb696bc4c6aa42fd5d4ec25569ce769de2083fd2146b2e0b2cc49625145fabeba7d7f6a2001889c6e66216b7f9edfc5503a05d619505d1e3eb0543366148e6aa137346eddadab325bbc9f0921dad61746f3980e26dea7bdd8ee243200a3e668e47baf8f0863aa531395e80c76eb889a07a74431720b0c723d8b90cc522216604a3f7a2a07c87a1da7a5cf68e3c198ce0146b778c1e8bbb9ecea8ed9092b2df7801aecb3716f3c7f383e33256aee3f3f42fb84eb77195a5229a8527b4ae70c530bef808a6e5df34b5863eb3788e75c7a8f9316ccac256e80da31183a2da873a445b0ea8e4597b69270c1f0640d2d60e7c9a99a8bd80e2e91d8f1f2657d5a621805c0b6abc67a5a69eca110042e254aa0b77209cb4e056b5770b412f11786730520f9f3418f8fcab635f8d35a0607928a6c82a3859ca75bc278df92b92de68716a617c3fc85f8264ab8606f6059ed542c5d0b877ea5e74571636a043edefdf871eec2c225e3465ed52d85f172b11ac60efec6fdbaffba9e4fc363523b02734f6b2b700a77eeb3ec8900112118a06d6f6ae3876990e5d77c90026c8b0f071025be055ddf6f620a2b551e6376622855e54e258d3d6022055ea6de66d9f6beed02ed2f1fb5e56afbed7c6abf695ce51407cef4d2384e66d0993bb8d10636892bf6de0aaaa0477217ac93f296e35501abfec88542c66b2ce4cf09e73039de9a325f54ac30183498d78b09ecef6f44a0bc1f52f737967bf44902b4508bd48005cc17e2f9ded91fda7795f617aabe25f8892294c0c8856d1d011e27e470a8c85f79e346c3983026064091aab9411a2ec3ac5b0b43eb202f8ab495de9249f4db09113f1d6b6a944bec54e8abdbdb1201411cd4513872d8d0416367aa423c989cfb7be1f2810bf1c2924d74b48c1d08040d54ad5a24dc4e304ee1a11a2fb6754d7a9928c34025620e865c17823d3ba105e1d59f592c631a75146a28b4190e899ae2ff03ccf620ec6f45c2476736d12020b207ee8d686fd5a381c2f1a8a14e8d1c8d7035593ca8b0178f97df1f9c7f4331bbe0435358c9bb2920e74c82a53355827c34df0651ed31b05bea9b99bb8f810d842aec201cc82dd666dee39c04f04dcd1789356248d337d9630e527883627d2570fd13f1905ca4b2049a6ffe6ea526241bbed45060a2ad203e912f49c9ba673578f0ceb4cff57771dd97e5eaf3577f1d43fc3ec29ff234e3204661329f3e3c4e21161747f7321d493177622cd03c2fbc943d48f5f081c3891aec2217ffd5a21cd78cee7a96fef752ab6686da5bf5f91fcd63c8da455b72f8657cdf296966e55abe3c614e67f3c86ebc293a225d03e766a70df564b553d6e468da753276eb6a16ffacb68072b135365eaaaa47999cc90d31a7570dfbc8de8337ba3137886e1f1433298e868c3eeff551698747a5fa9942a0a9cb5e4985128da34ca47ad1f93669e7f41256d5c1dcfb6fce158b06648db6482f8ffd5eb78e33cadfb0da56462e39c83a95ceeed48a75a39bc42676ef66bfc1b41e61f7524d61125a0ed58cdd3b1e7b7d044303768371287f6c3579ae4f22d0f832551f78d937c451d05df3e7806c88872bd48d21c51f847c5112408291d260d8222c9e71357f527d59f4e89bce20aefca696eda28ab9099a40bebabd7de0481a4801806805f1f35349616bd08b27acef38d4991ba4ed434d940c404b0b08245e4aafe8e5f7bbc81447f172780d17eca63e0d73c7987e729122aa2e771d8d000849044303680cab4a9396263b266607244cd960345785357e3eae64c2f9702c8262ce4f66a2ff863ca06986409d38bcaf0e7ada9657f286add75b7ddca392af1d6f29f02ed454d2ca1fad9e662915295622c56d720c8388bf5496a6244185b708e1a3501424762206e9c2db301d41940604a44a69983920b63287a14135b681d31c418fec46f18d2befd40607bf4e9a66e61177657ba583b2897e5a0bf0282387d7b4458117d42e33faab5766fa3f429e1a632c61c27c59a5fe917a65e16744e1d4dbe8a860cb3c2f90a6533bc91a6916f621026ad5cf6757b90c7c7118573d6e2ed20cf2db6a370e90f070de6a460039f5225ad7a4ecc84fb516f73afd73c932b8ca0e33698b83e7379c2deff7514009548c38d991cb9bf578f70c5a50d843af600821b91f267fec1821c3a78e37705ee9c75c8f8f9934e2d4d958359aed50856d2033a614781303fe5a518be1732344d47eb89fc25ff0bd918d1cac9b07f78971a0d7c8b5f52c9b04c9a2b6f30279b6a6342d870a54bb4299c9c3d4207a3ab02276d7450506e065e40ef645056bae6670c24a07c38042113f2158a33ff8b291a47e37cd9f9e5c52fbd58bf4084296f4183aef62fe5c95b685d4e9e1d8145089d3d0bca86c3efbc96c185890a16b95f9ddfbabe83d1ab04aa1ee0911cf243259ca6a2aeb79e469d303ba9de3e93da001a15a3d4e4858e1d02ac37e0ee24c9b0f3dd01bf646b5e17d11395a0bc82215c76f06feb68a7660d47cc351043f26fe4a9397a5a3f0636d41ed0c62e4a827693cefed8eba43e0b9659420d6ddf7a252300014f29fff6860dadf6a5ee0a444850c36923632b1e9aa7f48df342b5d180ec017d4ed2e0172b18a3a6b7d72c560a584127f6a77e941e533c2cc1fceb096dc0e103f136ecc05eb726e60e7771b337faa4daf3962d133a0aba6f0e330f1b30feddde549be00f48dfb2927b5d95e04bd11435e02beb66fcd5448d666ee7aa0382d28dd739cd2c339806f6013a05c055ffa04a3a6b054b7841d2ce98ad063ebfe6384c570e2f77be4a3d5a4c25ef226b0bf958908c667ec7a76ce13bbbf567e91d7961330177296a6312e4f2059973893b3f2a2d0a5713290c1c9f0068cd2fa17c522f4bc16386e72bfb913b3c8d94e1cea45451644e96f4130316b8e7b31fcc1594ec896b619eae8d1eedf84a34d3049348406fce9bf269b5f2ef39ef9ba9428f3389961e3ad3d1e6df7c5d52dcb0698910cd1a122285a0a7752c7968638d6dfde6586988b7409a2df36958ebce214a7fbbbf05d723ec08115ca2c1fe717610a0ad734a0c1eb7f2c62ee67dd1c8b3e5e760800fbb98d52b780d9c5b3f05100165e6ac730c68066921f8f9a83dda405d98cfec7ef1bc24ce73ce1c4fb14fb3fb07256c52dc77cdb5c161f9d405de95c992ed20cd6a53aecf7f7c181c993a1392c2a05f2bbfc90232143706da31e73010ccdc4e537475d0270184e151bd3a90dbbb94502a2f2e2583ff93418556ba654c6a297c06cf389c8b0b24704861b933cc0f8c61a30329a2637089407b6203107a42cdc110bc035a7f779f10058187d27de9b026531acb4cdcdd83153d150520902594be61890551389ad2a2d86ca5b08723f39e3d3f917bba28741a379a3873aba5d529074ca46e15c7892f35b320e87f2fcc03fd310630c642ba5c390ef87555a2f3bfcab493bfe906ee9a4c9a4ac264f08c639343cd6fcd32fe3b9d1b663422ae81249c4c88abc02e69842dae6afc3ae9af984ee7736a792537e932ad45a2831c1f092c8744e4bd75b3b4e8e42bd3773413cc4a10720a7915e1c05a5f453644e9927ba371b09a3427c10538e5b9cd29376c894abcf005a5cc1b0267a268b2d278ee38697dcd2ab19dd6eabc87ace77c51dc8b2f184b8f82ed1a98ec8ca7f3f3e45066dc18325e9a08c7271ef2aaaea1e7f7470becb04eadaae565de9c3ba49b41693eac2d13c4dc92d8c2c55b0fb9fde1d85b36e9a3b43860dafbc118f6e2ee70812cb7629a5e067135e42d08a531db57b46dbef0205fb60958b671206e83e734e252471421ed5f37f3cc0bd0d0444f9f4c31f0bf272a5afe9f943d0dbfb11e30b8efca3364a7e05320531605cea866c43242363ccc84f7584056ede89e42a8a160151c62eac4525986e7820e19646810234ee2386700b096f7ba08d6d55133085af6c4898e7d19857766cd34c08c1695bbc0425ac35fa9078c6d858a3f946dc29433c628dea6f9d251955603006b8844ba94a174db2577f68aa6d9113be3dc5ff444448e15dc4186773877fb9c31a086c6aaa97a4d77f1f8a54c79d800c26735f3b1f753ecec40d36cd98a66fa20f39535e7a7690cf5da99c5548191f96161ae1311c2d7d8b30123dbe60b67b3bf28af529b0d193b163131ee733a802251bc6d478044d61da18c17818ef0621116ea4677a9ca67ba2726f13ed2b3f60d3204b9da16d11abc760ededb0cef4849082b4f59923ab4856d5e068c44de2863c5107d7e3f360599c81d20dec23dd394140c6042d18f8fed6e75b620c1dc034ec0e2baaab2f3f1b2930924d80803fbbf17658ee2481e608db02934c9fcd86e45a0899932c0852ebd3ae82bb6eff24879fc262e1d215b942adf62f7301c1ef8e493d8a301a642c25fe5136a42968ab09207d1d87278892c5264e64fee2e355b0520605203d1f40a534bcbb80f4266f23c1a82a2dabfaed0979c8000bb8d8f185be58df53c9e0bd15b4de9dc158a35c67a842e885148b7d0d8b437cece3840f7f680899e0c2b87e4cdbc01593b80effa5c573a7d49d9c7a454bb0c4f822ac01e1de27d18b98ed6e3d84745f237bfc925cd2cc70f5fce6b2eb8d21a568ca53a7e8653142a009ddb9e3ba7e03aeffe6315dea2f12447de85cda3b65df88d3a3dcc36214af052e2c0296b894904505b27fcfa61c1196629ec1039808abffd408b7b3c1734362578504da2319688489e92768abee8c066fbc6739987650eeb045b7049a292b7047532ee370dd45621f3132e556668af6432abe58e7c394d541635b02be41712706c63247167838e59bf73cd92045f2f1043ec82e6702e3c21dc72d92875ae4297af908434c6338b3793acf329faa3e098fd9b6fdf320736e08bb9c724c27e1fe89fa91f942d16a231a507db9cd7cfb898f67b042e8d842830cc1cfee2b6af8f37d41a2d344797e91cd66ec747f4ee8c417da4322c1cf19efa212a630886edafec0f0d0d8e7c4fa64da1eb12df7c9d650cf6f1dc7f3e5c4e7a860080a8762e1de4afcf538829ab76e6e197d806be450f88a9db79972799056274f0399657066b4e7c6fb9cd387ea4c45ff68164354e803cf6ab3dde866855df562aa17f973b8b48c8e701ff96253a2e0a5d050c6b030c206382683b1d0706c4f5b3e02484d7c4d881e7fa294840734dfc65228d219a6f085c8e0ad88ca4f576de95eb4a8306b81b53049224eea8d7c43f89d3962bd8735ff4e390002fd6fe2a780e7b91813f4fc4dcc4e3d082753897d27b84790c38c3f6dd12d256e8c1aa7a6b4453da80b9d567d6609352db30807cb23d710c4cb1bac19f159ce7c45c413aee506c7a623ff70279e2dedd391657275f983e8e40330073f372e3d53639fadabff34eca7622bb316596c5c3fd547344c9c010d9106184c925dd7629a8ec4a276dbcfb8454a6262bff27a98d08f144c28022f72fb2cf71b9c381521d3366cd6d906f4cbe64f81fede13b478c330c97e659d165571549f5592bf56f6cbcdfb296fbbe22fc0384f0f243ddea38463cc70ae9fae6ccc57e938aa2217a829e2b86697ba4d3f29565b70195223de87760356dded321c60edece37b20a8adfd83339f5a0d07e35b17c8f7ed7b6ed7d6a827c0121992f698facc175287ee5d739e4b9b3e9fd43a4107c8de45e773a0f486ada241d93c5a33cab367ee6561dd8ce96c25299a28ba0b590c0cbaf8ea999652b85015bac6497b6b6f9ae2f6ae053216e2f366cf8f3e5d62c0dc5aece3313ebce971dca986e3d883825432a830cd01c3db5a2a338d8f07530dae81e698d4fc3cf2b64c3c5367268378d26eb5eb075ae9107f92e81f21080750ec9a2d079e2c495b7994d4153a4394dd24851541e33a13a7c852edf9088b4d25ce6c6b59836a9c32c71d99fbedb52f9183962ddbf118fa0ad56d5a3f10743bd9fcdf3715da904065df287f3a7d1774236c1d03dfc16224703e867d4c86a955bc07568965898d3d630228a8da4161f95125c29dcbed0e7733e57b3032d2f214208cc3204014a2112cf61af60bbab396907edce5f8015b3e4624a2536f1f5964e99abdec9f8326fe1d375592dd816f4836b71d37dff694da56a23ba72a08491506ea37205984c2e61797d41093c69b0e303d9759bbaad659ef2ee9045aa43a3c63e21172d961d9a7a4dd63136781f76c987f87936a0e1ce1309f2e678ffd45315d8e1e678134774ea68f06de64319df17ef2757cf57d9d46b69872c26d1fe559ca2557e59467700fa5f30f10f313bce7de0185f1e8896e9acc0e79efa2e9836983bf8223a05e5e5eb4cd05c7a9726651dafb9eba453fd75d6ba879ad40a17e1c27890202d76d6065875e137d3b4abfd2ebc05007fe054ee67f2ea01ac2939d14226d7affb86ef75c526be65fb0226f2bb896447ed820549baa6fe301b51e5564d856a24fd4e4f03e4772471eec80ac22e0796ab64d69e980f1b9bd74d7900e104bbbd8283daa3238f5cca617dcdbaa03fccd06ce2181a706b445246991702a3716da74142e336c4576dfc71861c3b4af93befbfc081a0f02a9b4c46c0dd038282318f1d7dbb32ef2548ae6354d97bba9acffd34a4850738ed54624200e8dc843a6a0d6c82e336261bf0b41a72d5069682ae9142f71feffe40a51e2cec4e3d438369960b55f146d87b49500cc95e8ef014e2f21c7c3418d36513301c871b2e7301e20da90234a3666e010e4dfe3f30fe79756024e10e14e035203381d272afb6c32d2711cb212848369baf4a79596d0f268137eee8a992aa7a80aa7cc78f75c35bae81a0316d64e99b2a1c5688554a8594fa2c81355e22ec764064e3e30554d3d7d09735f689b53618acf0d785aff35368278dc727d028d4ec93c3cf754e0e5580335653a7c31ac1f3246dc3e8d0a5cd134a849cad6ff3c0f0b8c6e084c3db5066a56142b5f8202ced13abf7887e017de26ac463992933c3a48f65c467101b2554edb106ba094d21ebefd8ed45cc64ce07730c0738b9770965a023128fa7cf157343ff55590a03c73a342f82ddd2a43944031e8c2212d0f9fb2a4c6dca9278f1837fdd7ce1b04a405d3027ea9b22eb138945e97af4ae8e47a47df5bc07fa76e12bab1e42680f28c12db16443baedb8ef663010697883219962a244ec0f916ed0b188329397f24317c539dfb43c5f8beb653e2d7bae4ffbd2e092632dbc98d0e1c94af7be4c3e07fc397513ab547f61909ed6e1dbabcca5962dd8c68dad074b81080b94278896e1628bd28234549100cd982867092be083305023f8f276eb445327c7abf139f4882dd7eac78b27081b2471fc8d54f6d6a07033fb5bdb5dc133e09426fa8468c2142fc46b66841f32bc80f164d7156851c5449a7b8a610b4650d5f3a3d5d6e5b7fe66e8bd50444fcf415ebf878cf5721c2ebddb63e7e15a1a89b5c982eb2fb719c2a895b61629eb05520f466899fddd462154eef70f7df671dff0ecb5825f7a0c9eddec58322a314df606eb9fcf5928b640b68b6b6910f80f3051e1d5aafc261ee3128e243ee6caa35ee4af6e8f01d19a7ae9a8002ceef07820043d23fe1585cb3e82b0c010cca3e0ff7d8a9b7474bde269f0ddb29828ade000263c0adb0608eccc9fa530afb76f8c46ff735704b4cfbebed0d55d61e057bb07d0eb56976da546e8fc9dec7e2424dd7872f844d79abff403a0eff1d4339e92e8462e5d9e5b79291e1ee2f2a394006e52653cbd87417733e46f8e3a0e4c9a388a10254cd9eb5b2b59d03ce349f45e8e109ddbfa076791f26403748deff76e4a7ed4c0836c463761beb24595d8253726052b7b1cfa73c372a7d75e5a80634c0ad3b3ddbc982ec05d549ff74be433ba583524dd4a712147a650d870d3ac63ff6d068301921e84ce0f3e351999468c7f2bd816c16d708c4ce9f42c7505b08f4fd7c28374fc5ba48360dc814c4de7e6c5828c4a6e471a2f8fb6aa5f90672992d19951b69a8f204a5be0ef5619fb9a00eab1a96f23179de424573dacc596fa505d916473ec85e96f2d366a21e7761407e69cb47273798e8fb71d3f42d873fa3ef96a3672bf0326abd1a9720798c3a6fbce14326b7176581c54c3f00b8908b0bb09efaa9606431b4db3918df328f9e6b42ddbdc8e45fe5c955d34d2b4f74ff9db9093e6267c17bf71219b6d24037794a9e2a36dcb18027f4b23bbe090302f08b9d4b2b920d33e86fb75b01d356bec71c722dacb4600cd8a769de962922a115421a9a0cb16979999b95f4e06cd187393720ec91a5c9cc4d32affde9cd33d12f0b5163360128be69915522f20cbab5eecfe2da2fd57930c9c68abdaabca3149d49feff28c02fe4ce88c732d512d0e84f5ec519290cf4380ffeda38c29b41de7c00e35eab4a0212f38d41a967a0968d841fa889348847c8b889e5eebed4a934917ffbeb39875139ecfd2d3e7e6f8fed9c337653ed391403ffd7edda9ed2268b5389b411708f3797f1909bd8abc3fb4cdc0b1590af91aed3087790c45a2758af5f92f1d35f3e3bf3290b8c83e440c1ce2d75d2764a4a11ddc005b3cbb35f344541b62656d2113bcd8b463ca1712b21a21c816208ddd6ec6bb6c2940428d6b91a7454857238012a4e485a6ef3e35f385387c7a88cf6d8f4f40a0f9c4fbbe0a2ec0e12b302f5c806ad44fb680ade2449883d7532f4c6037e420e22fdc1ff234a3eb8f9f4960b1f3ffa4bb69ca75a50391135eb48bd6d1b3ed6026bb3be12b6aaf1e1f712c7adbd02c219730ac05ca95ebaec0ffb053f3ee0a3f8054d829b2b2473718ff642342861227cb8c28c3b06899e297624cf2d4f36830ae28f563c0ac05e536670843cfb4badf9ada1f8e4e77ceab4c43c3030d1da5d7a3b171788de599eb886fdf2e80b2d737a56c6d7ba82e9c0f8faaabcf46b6ad815ab9227f83d0089bebcd2859abf06ed91bf34824d28afcc848692a7ede57873ca7507cae34f4cc0c51553c94b514dcf3e2fe2b1c9578dce6468397bd6ec3c5a73e57022c6e549f55b2edb7f5da9676ca759d38a3793ed16c793a993fad8b82f584b117950171ca67b74109408d52066460747c05c56b677ad1e09d1dee231775e30a462bd5ce4f3682ec6f9e554277183e71df7a7da374b6b8907f7e2235570573f3f711c2555b332a3ec3731c9073a6293b902721aec07a91b7553962726a18abe4d193d6d08dc2ed7cc451adb160ef5a676c46b7bde5a0ef6d5e0a22739fd0cd60fe1ec97074c3483f7daf6720dbc21dd3912095c4a8bae191481e30b212a2efc0180fd007b133010fb49aff54978c02c4223813ace0c5f5208cf708f7c462bb57b302519b926ac5dabfd87a1097ddaf9d2b2e7aa9b50d455af260e06731fdb33e49ae438418e797b169ad3c566a9d91497a580aec549e7e4830d4032a192398aa72b0c2f2df113990973be3058483f4126cc6e188291ce114ee55a56cc0218e9fae766891a29fc1068a96dbf14bf0e3a03ea90728f1d6a8e927a431f161739a422e5076225d824afe683fa14641c192681b5e3d7f4e3d6533706a1df0baf44d8a864fdab6d3215d194f854f356fd017d3702ec4851330b9aa1b98062828f3a5afa352c57cf61dd8b79a8bdc13563e4ede888af8d6fed4bc026cf5414552789e18c1cba2cb6f4588a2f03d85e48410452f130dee4559446a7942c7647bc007542efcf4a9d560b7ea8a0eaa83d574610d3547e161b275a8324df152afc6e7ffacb9d07895a15eb49ac62500a014a7cfb27ae12a1a7cb4753497f88adf9929faffb4c784317abebecb9725b71835daa9a220118b8dfd9b4bea6d53d29fbe26239f4d0b2879cde65f9178427d8b437a8ba9a07b088627f233ebc32bf0d4e6625b8a81b8bab850302e1e565ad09cb341b6288d164b4a007b788f066f8321a1b1b012db5a449c5a0e114283a7f781a50166191f6eab4d532f3aa85ccd8d560b807f1b469257d9523845c1c65cb140041ef78cb020a529641ab7cd00a80b14f5761faf4e870f91bbc57ae935b2b71d552137d51d5401a5f6cf8cb0afffc958570cd9e16270a05f1891b85c137ebf8170ca38a2cbd7e9eda3c753f02c28ed29f4a3b38839c55932171415fc79f565716726d230ee8d6ad6e337f5fdc256a5d797a6dbea7362477db45f7c79964c5397651048e4b59d8181c430ca14a4db6355cd611e5fdf6557b912df0444ed3aca4331fffc535b0f88b22321f52a4912cf6095fb3d72816568b1925c5a416f81d28fa45477bb626bf37a0d3fc661ef12347807dadfe7a486858f0304e8928f1bb91586e9391109822c06b75a1be2c02dad051e83be1fabc1a362e808122ac3552be192ffe688c9c0b2a55a5fa989f983689d9f641fb77440f76bc34b292f51694b8523398e31bfa965e4e4484340ed816f9b1818b6c5fd90b53cef24bcb8d53fc65406a54b6ffcfec538e4a2c414e3c8bed8c21fff4b20675f21c5092ad088afbffce0f86718e13f938de6d74bef8a9b5728dd2e6a6a846b409bef8fbe1d9965918f4512c0227bcfdcb6ce55cfc2a59b418cbfa8babf1d584392a294f1565076c6305618ed11d125a550a991522c703b7e1a556141977de5e2a3584c94d17619c21a4490563722a35aa735ef9e4d44f4684bdc33275cd6b2a4c3f7af26c9abf5d3c246b9de8262a0174fe18c601db7bbd4c709fa9b602598e533643b8614f5c500c0d09834317ab7286598326a1e9509f09428e42da9a7b3eb3edefa3550074f350d94498e0e1d28be0d66479be7b4b07188b3efb472bd079f33dadd72449221c7696112c1260c1d3a7d073f846529f0b9c925ef1744c7a261bb090919999d7e5d93814b689449636cbca0c7c3d5bf41762db7d9530bac698a080339f739242fc223017fadf243138e3551663eb5dc2dd26f38afb95d81667810aa086e3e26b4065110e8c56d0d5e931b7424bdc232424884123086724175d2a3cd39068aeb94c1ce934ab06282244de56b8c965be0699b0334b887360ce4c011c8df55ac3a64a2edd59daa4062df080ed9926dabd808ad6d6e35d52c37179a65041dde5f582e39ca12577fc437ac41b59b4bfd6419467f4646644faf7ce68b5b9badcab4582f0d033652f73144f27db2fae22fedff0e9f8001225cc7f96841c704d8e18f307fe0d3a272d88f3a83682526275665024c62dad30b91bcb42bd6027a754fc2e8d96819b0e9a7bea3faee360516afe93b6e98eafeb554a6c212b510e76388e10044181b95cc05e47e34da9d54772d4afd4c04ac2f085f12ae6539a3e33555b12357b62db1ea4bb35f0fa4a149d8eaef5e5c1e8ad81df5bb4aaf26bda08ee09eaa774ca2019a6e424d1a6bafd6e5c1c1ffc4387ae7b27f86af3563e22c7b32049e53610ab6965b60a26ad0c5ba100595c5d8f12b0b9f7c70d3bf8707b2b3bafcbb751eb0b2c3b9f852cb510428dec823397a388dc62e1c729716543156787afe6eb686fcc0e2b1b346163be3370a340124b7df836e4e984f3ffc5d74f1e41242f68c039f12b531dd3c3360f799098264d853ae07d37f24a607541639616d48a109515b5726c496f1ca0fd3eb304933d9d2623540548df5c7fcc7eaa2b5c3b36b2587cf76f4d342d7b6462f366f2f70d37106e15ce77d315fbebf9868ec1b937c6b15d09d949c093a2d2a4a22d8af6bb9f612a65e507bfcbce93d5f886e375fd1b17d37d5a404c0d4daf9a18fa8c1b9ae539ab8452aa9885e764cbfd028c6105c7614c0e214dc6406132538136b499304682b4d7bbb79332820a789d0a64d0fdf788e0b9751083b9472868c965a6a529c98514771d5ed0485fa4244a8ce93190e6b70d9dd61435f088b945502fe81ec0e92cf06d6670801dd87af6f0bce849402856280aab4990721aa086445220d48ea02312cf2ec4d8cfad3c3a8161a49f20d8d0eedada353887ea3ec51d1e5a1959cdcdb646c56d688be2896667976c0d872dc0c618051a537d4d4266fffe883f87d43f5f61e8a76acb8b6353883816871d854a27bc61705adece37b6a19c9b75f3ffbcbdba9bad9d1cf6d57d2f7fa008975ac4cdb0e2111ebf0c2dfde1bc2d26a055082d7b163ee406aef1c411a71d61e8246d45c4ba1ff04c9c61884dcfdd2a517656e7747db50120833dd362eeb14aa884a8df936a680219dc223aae613fa73ad21473c2618cfb336471277e09a95182b5805e2c5163e7e7429d192d7d655898e79baccbe4de028db47bca6e358186628adfe7a177140a34e6cf80fb0bb15670b8450d01c053d10ed62585bb4400e504746eafef4e1bd000a1ea76cd2b245ff992b5426e1e95fe9c682ed9dfab5749a3ddc4c2a18a0f6bf8780853783258fbb06bf22bb4a0234c373ed78afa0ca21e64187ff578737096cf67d143275e6fac03c3094c317000f32e3ace4199fc29da973dd27a5f5a31f4277c1ba700fedd131d78563248089e9c47cc128002eadb0e8440e50e8e692adceba898d12f6052ffb05fdc4cda76c38198cd0772c1080be83e22028709b0680859eb846fed39c039fe00e67f118caf4259471c5c61fd3bdfc6307881ab6d4a14346a3496f515a5d82da56bbafaa3ebf789929a76e63ab99821a939975b0a161c52eca8e61ec5c55fd9205c520486dfa1d50abb9949d63db5e6267084afda71d73898a568008910b81a0cc730f59e5516a9e6af18ac082a86b9a05bd79ee424516bdeb679501d3f3d0b0d0a668bb963e606de95b431931d79bfbd64aac77c58b776c168f4ea733c56344f5eb26dbbd86abc7a3b1c5adcff20e65f38e308c7322d25dd62b6af736c0e1f975b097f88fde859d1810016191ff51e86b1d7bbe63c95b4dec1cb61e8c1f3d3526c63b9dcd4c5375cff1d6d64a42ad76a72638146239e7919ca7c201c8305b7e471ee6a1253d3eff76a931707d6e4ea0648c74e75736407c4a25f94010f931e700815c4ab1a4a21581ea756f10ba53d8c4cda0c3613bf57f928f247fdb3799df68779b751a4811019e28c326d63cef5baa0ad20855655398db86b4b8da725be088a9a6ae97cc27b33b987d9062570ab1875fbba826d81b7d1bb4faca2f8d04edce881d24050ec6a71422fbfe2c272d977fb02a7155adb28ac74bdc8d717461298119765663622c7718ac8ef09b75f0f1c7fa233a1110d57aa0a0020ac170cea704d3e93b619115d47e1ab358d01dd5b0d4519553523c1aa9444b8896dc2cc68b337d781b41009ba400a4a23a7c2b0878c1afc4f9b4b0e9c75c1216eed3da47409ad93c4fec30ac0c6705249eaed03053812e650a80e28023b737f7c2dacc88bcd3dfba9c8fdd4cf1adb8499ddf0a18a20224ed0026cce55f9bf7ec3d8c521445fb14b00028225ff07d758d4d634c7592f877d15234dee7aad079bdd939f4feb875a33ff079cb90b6b51f9e1c1a69e6a02f3d643023ed823b2befd2ffeeb7975aca0b4458666e81787b278ba927522f777397818846d142719a33e039137c95b55f25e1c4ce3035c38195ae48d137779dc79e81e7a2cc60972329d72fcfe61a2515fde0e71f9956ce0c5dc72b3afadbe3e09f94f1d390b5bc613b9958f3f38ea98ecb0b5bc52101d99220d0fc78b3c27afc9cb2da241a7d4c287788140964c6b69cbfb7b4e510b3626eba49b5424c4f7fb801fb9f4167c96c9e5e36df63ee0e2e292e30118711b5a0500559b0d75d05c1be85286fe10d8e470f1944c181bf82f4d954e067cd6416f5319b1ac1556eb158d04b3d5025ba92696fe452e7fe9b204e609bd93c8068144549735d022f04aeeceb44db3935844af2a719d38e2510a71f2595981aa9c11e99e3230b099ea701c3f11f43412638c9a3b459611f6e205e5c6b2d0ac4ba2228e1371699bf3063dbbd0e9c83cea35186e8e08471116579bfe245e9e5fb8eb181e2684db83b8da8a929c40c5a15d362f45c6fd545da6a438806b8d5d9725b0b01ab50ef50b84b7f5379c4f40d3586bb5c99cbf5dea6b1b217381e10dcc36b7872962c54eb7233f60fa78d2d05b0345cccb4335b7bbe684f85722878d1206eb8f6bb9b282f202414193aa3487070d21aa9c4dba7e7903cec31709184fa0768c7c805ee3667cdd73c21ff4d937f39422805f481ad9c12dc41aa239073f5ce1f8116ebfd2b0da9817a03c36f12fb98d3907f3f1fafb6f7ecde65013c8ca7162bfda4d3358252ae56915427b4c4300cb46adc13d0c6891d4d5f6f8ceec323155625d9207200124d2b02c4d335b05c321093b3c601c163df876f236e4acad798d7740307d9db81ed1ca7f4774a458003b06ef6bf4430daf664a25f2bc3f0f2575e12f89ece68330c84a4d41782366ba11146238fb7a2aabb9e5433c149f2a02251dab665566f18afc8cdf5fac1a3f6e396d9b5b4ce118cbefd7df8bd031a4ace1bcae2d14c46adbef7f8d3faca15805264e45415646caca8b6a28a7bd50c4433bb16faa43c73b94cc7d0c9fe9c79734263c73b9d3f3c3eaa6a810dc9de1928ee0f89303fd171c7f016394a041a8a9ad683ddfb6325423a98661df042fff1fec358dbac33523cb5bac979ffed3d450a0243570e4f716248f369b94072374877b74955fabf3654eac989632f68c9870d10c02daafcd985bcba4f20aae63795e4c1ba5acd04d4ba9017c5a535662ce9e98671d17e99e441314b194a3e0375d820bee293bddd72cd4d4a2d69e5fda6ccbf63e56f82039ed0720b25a3ac98fc370c183f369d043fd67d32ba40a20c302bde8b71220d968c1be3729cecd0b2a5cbc422bc49038c60f50da2f9fb08825d40cecc311bd7af89dea5d917af005c33b2945c9d46c1c8ab483d07935c25cd0c692a4d14b0db67822bdf6685b548a670660a1a072393b7b345ee2f54f339186e86aabd819f096bd7ac1c68f8d427fe494e2736f9d0bdd9ec287d7c9173359884803cd0a536f832d7a64b17cf88ac9948f5560c33ce9f31d5bbe97d5c16b39b37179fe9e4453c57bfdc7795f9b352c3170f05884a94b9742dfa8aebe1a483025d631185c6603cc2ebefe426373c1bfd1291b5d6f95cfc7d64ebee5942648b974f05afb2c467d8fc59d51c4df74c2e032ef4db82bef6970bdc90528017198493e8324005008e13c4379a37e4a1a07ff7af70093900ca34876781a3ff69a97c808f6f86d390cabe69b2ffd78d3f126fd948a12f64989645857170d1a885f1a17fb5ff7f72f13538cac8b3fbd31d3d7e0e85426b1f1dbc290be56385da624d5d6bfb0737cf05485dfef3e94a5a851626383a12e6f9b51c470555e8caa122fbaad30941472c8470485ae74ca68d18d711efbccbbaff27aadc4e9a960a9158578d52cd9ac53746157e86fca522a3d53446b1843bb28d819496f9a4bbed4496b018c7ad7f6a518091eb9f16c08c874f60d6562726afdb2eed80219de298359cf8382fe31d0e31059cf0c319e7394649633b7dcf4f9547854dba1105354e40a4c50291b083268ed013cf58cb9a31c61559ba6d48c8374fc835115d4a9ffefa2c55b2b7355ed4eef64c7be8c429af25b4266b00b62696585123a705c5abb88cef5ace5fa0219f7c04817800e677876b5bcbb77c6207d3024e07a45db19ebf39eb8936f80bb3a69a83d53ef3e5971e72c3706479be48361dac52d333aec34c4759bd730d9018f34b7307fd4beeab89ff75ebeaff6e032ac5742ce46b87c1f4c023953c29a05a3b17b2d9f987be38a77e98e5e0d6adf5e525925208219bbe17accbbd81d2eff3df68a138a9fff80ea25c0e6f0b99b1cdb4d2c5bac1498d1fa166c1a391f265d80119552d0a621d7f6c82147ff1f718c1de21579de85cff527f8a59ecee78db449758e316c03bcd3f33ec80ab290e1684c8531d98a88bba80387dd6c6b18ed305fa9636ce7e32b53395c421688820a7f31b06c708d0e260c6e88e62c11804ddc0579f5540b7c50958b8bbdb9ac0e1b4487955afa6d2be596e41f58d5ea481b6404884b5f12496e7d777ef648cc73fd799ab218700429a228fcb4681b01af4889727672287cf09833ce8ee2885a5b071955b5e8194184a12b6111a4390dbbd6af8b3da726106bdb8c907693c774166a5e58935811dd7bbfe113f790c2877a57acd1242132ad36e22651265720d65e5c79be1a04cec2743de3efbb1245073ee78917f25f093f703096d5189fccb84ca0db0afa8ef3586213d6aab128131a1ac574d79a216472a461f0d98dce3a03e228d5f22f1f47ca47d476113eefdd5a02c10953632f1c287f74b1df03433b45dc637d8903a2f64a62867b9e753d6475dc39690f25345afdc8164b70f0b2aca06a5db2ad96aa86f9aaea4b88c65083637559fb39385f4d49b08bf3dc232d54305456c9410ca16e1428dedb0a8ca4e65a1e12ad0b24dc71232e6b949ff22f0933dea6f39612de2cdc4a0992edf0b07ffff90c65764ed32b407b9ce54e725a1cc54d8a598660b29db293736297160c6497ba1b8928384e0d4eda865d44daa2ab8e863b5e3e84e6f4fa1a41167a082834e11e95c8b728be03c03f09a8c275bf4d6f2b7ee60ff94cccf2e8a6ded9bf6f751f164726083e1d4a5f0174f30377779f69159ec9c5e883dc51166e597f8fe4d12d6aff8b2089f570517c5922c286846141a52ab87b439b9bdb75f394dc8364a0a989c91bdc50a32e5a150cbeab28ee06124c35e609cac55e0753eafa47e4e9e8642282c1400a474731944e7dc1c2a76cf3b98d42768eb014af4903e8e1e919801c4d121444da68195f9d8aaa17e78d9e881f8b4090bf1bdab005ea7f02bbeeda4a8f42d258dcea66741600d665408579838afe96ed81762f5f1af01ec2b567fab888f9af5cc73f5f61e2d4b8b23260d71ff8b86e31ffc3acd4de5ea74ea62ac4b91c57affebc616468dc9e4756e11e79a584bc7b95ffbfcca72d1fbc3e8b00707b86eb0de5c55c1be91f9747afb4ac19ee1b863a376555101ae8885f63555321655138665eec2b1168dfd2cfb99d6fab2eaf8ab5526d95c3ade0e3bae1b2a6d53c33e67efac49f57db814480abef5f7ef7357f7d2b3feae319404e9796e0da983ee83115b7d9dd5f280fd4ef21bc5fe56f85884c92427d00650862f9ca829c2e10c5b68d566ba6325d3e377f5f744f4ea54172f363c59368a51b1bfd4750bb8000e784d1ff3da245fd7fe98ec84a2e3152e1d33ccaed67c2c074ae0df2bf338e74438a32e9c7874be127e748134fa2b01c49dc2291ba19d07c3782513a6c18db6fb1c5c845043106c1d912e8db96b5456e9f76fda63dca8c4a270e22c2ef02b03f55e90a0fb1d55427a1ab579b561b980fbfa572ae033ef903c7ab6eb60915dc30644d0a691f2e4eab7f9cc3d46be83d8b5bad84e31f558e467c86e5632fef46738a892e0e72483efdd991b97b8b2ce123ad1a91180e1674b83693904cdadc3949af7a6cb481c45417779bf422a588fd62cdff5d501719664fc014487797f10eacbd7fd6f75c9a139087e0092c57f05ee35171e89e01778b327b3ced25d8444f204afabea4348f01b97c05e3dfac94f1fb96efd2465cb647df29ce290be85102b369231be301aa109343ac197596665136ea8f1abfd7f4802252eb2848484ca4179f85ce924367724a583a1bd5ef27c307f458b572e98bb11919fca189fd9e5aba095d29c6e8935b5858e1b73b5b7e1ffe83604a5ce669f3505f2b0dbff4dab3661997c1c2cfee6abb5f3537901e36829bec0298e8f398cd871b3fd6411188f93ec0461d19e1471825b0665c144b53f64a8e81ba86bf3a0f379a56bf51517dfa430870e69438eb64d9e3cdcb17fa6fad07cbdd52bd9280dd028dbb6f69cf9e073ff81ce0a55f8f5e1324b0557ed077e986a996bbcfa7e62f66916eb3f8684a7c7091eb311bd5cb447741fed4633c107d190aa9088891854e8240bdc2e0adf13973d9e0e7e696472fb4c6bf5e670cc8f2d67cbdb1c224b54dac48dba0c65948c263e9405eb563ee3f5c70b6caccaedfd639036f92f497e84f43d488ebfeb60d01f93225720290c706fe93487fde3f10820014a9ede14f7b2a2a4ca83b193e6b95a7c34869b7d62734793c123a4689b453c683f06447d6448676a93b57411f495ee948c48835c4150001e6e1064607c7451e536ead549db81744a11eaa416af5b23543b6591220e1364c3027c3b7508f10cddd23f2e8ae0ac0ca73a19baf751c499731fd0684e1991347e7c308b155550c64aabfe140e6f64d83997501fb8dc656282293588f9e805ce4ece02b2ce2b8052083504b5dc8a9928bb7d80d40d68b74bec6f930f72f97695832fac7c623331c2de2c449b22b53f80821d49bcc7f7653be2b4c8df1c20102eac6967139798a350fe7d826c56d0b23d0d4dde43b67af96a7510601bd112a36f834aeb9361c48a6fc36484bc805b25e217a69c5b239fc3b97d47ff09ef3030c5b3a2f80ab8c1257c1f9934ced21f890e24aeb47a1c309470d5f745d006ca1182ebf64f35f9ee2b8afb19f26eba7bcfcaaa8f7d94b476176f94ce983328b295165d830b48ccf00a1af7977cb47846acf7d9d1418df621af5a29f419b4e21c813a538ec9c50aae75f99bfdd6e560c0b6d8071038477e03ab8a5bbd7f62042772223e0a8124afd4cc2bda7a35d5dc73ca356ff2da07099298f48d75486a6700c237b1fdc7aea973ab72b2ec0e2934caf68fa0c50f58d1010fc53175e54750b602bfafa7eb668836735b911ef834faa4423b0dce742277038552034c2f740e29961491169b136cbfaee91469d724c89cc9d6d56fd7c057df7490d2837f2d0e61c5b474bf29ba9576303f5c1e016eb275cf525cb8c78307738e6311ccc1d10f45e5a0bd0132ebaa6b29089d98a0c8a42885a732a63e28deca60b05a3025ecad28c3561205e247065321a128a4e9d6ec7aeaaccafd2fd8b7cef76e572792bcfc3971b7503be9e70490ded8223630d81a8da63655797e17c2656760504509bafbbc200fb90df73f5e6ae6240cf58b9e418f832d27d7017ec204e39f22990caf3d9ee339ebcede18fa81b73bb56e6146139ec2759229ffe2853009de6067abace327417f60778fda67215f19f237d5ebfb7fca43e4d7b0f72d27c89e517c157a0d1c12cb74f0c2d45b145b1a29573987be8690d73efc7f2648417db201131f376727953a84641054f07c13161928f38a2617a969f9130ccd1eaf3a69148ff52df65cb6b286e9cc457e40aa58f775fa317dbedbda792e43937d7088924721141c2933f5017e641bec1164a2c63a3b2d200b3a67955c9c81bd44f30523340b56a6d960713b4dba9ac19af79ff86cfdcfbaea14eda490694ffc84e4740c1845e288ffb8428b3f6723e4ec9542e79684359e149b4e3540e08e0a978b49e81104faac2696b31cdec3616abcb103b5dbb2c756216b391305a6ae9019ae1a79dd72ff118f7d40a19fae84289a33df5fc4e3575f9c5d4034545943b7110c94f47f77b86fbbda63f026216f0010c14f336686c22e53f7fa761921a54b223d01c5d1c24cf2fa1dd3f2cc92438db0edbbb59eace34a21d09ee3c13b696f7272a40c37a2e5e83974a05cf828af4db7091c89a8ebfcc1d665676bc4e7ce988d4901531a13e6f3198964af01861877b50dd6c6a2f880cc9a9604149e90dc5e1acb069b7a9b162b4e60638e32d5f4a9e352d8245e63b6d9f7537aef817cfd883dbab832141e2366ffe6959489994a30873dc75a4f6f9a4002bfd76ec802b4e6495436d7a60dc2117f69d5537d92a6221977105b30962b73723855a7858b53707721d3a10fe1d944313831a44c822991bb434d6d8c91191437ed0571e34a2117533ddbf946466e566200722b0dc968c8df61d07e2fe0969ae3c6df8ff5a1a54f338b8f2d640f184df2d1769d0f075da88468187aed5ecfffad707666d1f81eb9fb7f5c01460b782a3f33707917fe0bc20f39f127fee0f66d3af74d867a9829289ae114282efee0c85278e5b765606605b29df0673a7315d2f27a0843f8fc6ddc69d52cdb27118a251f5f73c6bb837e3eb5e6bec58252b4c72df84f99abca05fdb0ad15d9e18ef677bde5302417b0f07a8adb810c7e7bcd6ee1c77c3aa27f4b2cd4cd0bd58724a318f12960e03a3ba3807a623ab655819afd3b49607526e80b7cb944d11862255981d6eed26c0d93255148cb5e5214e6c6b689b3ee487fa35de8f60ce336c99c375ddbb9b4e930a2f0642bc330da4aa5d6507a84d7905e00ae2f5d52fdf09bfea0cc121bb231b8f17bf8ae1d351a9975a3670ee9bff3afdad497bb826fd8d3514610301c66eb4867c7cb0dc72b3dc64cd9acf0817aad1a462a58cfd6fbad5fde5ef2061965e51780e5e886dea4b21e8b2415a0777ca86237aa31c66f399c74882293872d260e58b43998a0cc5e757167cfa6822a9d734d1a73d18b56fa50d78e51d2b63a45215b49c6295850adcda58299baca9f9a3cd9302e141b396b4b5498ffd54e55a4538741eaca4d9a8708cf24b4c75df0efa8fbb25fe8806debd34346184b322141fe1be65a034e678b6928f2d23eb0d82d8eb8ced9d336f5ca01eb61d933b8ae42dc3cdd2d8e408fec2b6930908387c09ffeea424b2971f914f6d6476c671c20c2aa8b771e625a9c15aa46575ccb49d3ca8cbc6a0d20e7201109bc2c29269dd9352ec4564ddb56f29fdb6d23d565c29a2c213107e0c709ecbc5db8debd914e7c914754b712a50ae7965c4ce078165f5753b9942026ec79f2e8986e6cbedb4788c3a683c8f7455687c103a4c3b6b45a5944ab0b4ef7f4b5173850e86d373b03285209b89644b49bcdfe80ebf66e9c31afa6843cda70888ea58e0d6c8fba6bc099f12c9fe7b39489bae3e6754ec096c82ba826bbc08e5376cb7815dddc1d465cbd16985cdc86b0bebe3dfb3769d01ce0371266c6ab86a42dc2f7698e6ca71690ff45fdde61fbcfa5d5e814839f92f2cedd221bbd8cf9da0c59b56230f122c77811b622e93ff9157026efa20d7b0eb3f6e5f80024026b0393d7abcbd2093e6dfd4d26df3c68d0623d12056772396059e78d90a05ca0f34d815c0f1ee53acb4155598022e32e369ad3cc4d32590dea5df77a03c0997b36a7bf1b1fe97f5f0cbf698d65591aaa97d5078c0b39ffcdb984e665ee7c68f10f397f8d32ec3353384e7196b1e08999a28b77d0758aff7dc4027e772919efcc16523902d7bcd377d1bb1840c48ab45fc9ea0213577e79c6ad026a5187358d5b24259552a0a028eebc649e7391eb04d2315bd95ebadc9110130f41c20a262d864422579c9091cdf1d23d21bbb3dfa11407cea7b44514474d04a0a6982b45e5b0a4dd2b2d3c9829df4e420df686927a563bc3d0ed3a9fd2a60a9533693095febd757fd45477bd6b1a5adfb0c1132ab9e3790b158f71045d986beb6cfcde8a4f81b025445b5d6ea1194a735981832ddb2d371c24eeb1c74d92314009f640eb037c92ad54a7c17cc2c49a8b857150cefcaf76258d4a7386cc3ec3a4213bc52b5dcb364064dea51dd9c07eefb4fa44bf1d011cff6ae3f08111e1c187f800d101ec5c03f2f6741b91ae4e4fb791c6656c2cc8bd1364676c5a570ad3a43daf7b231c71090557fc4f242242f62a16bcb51868b9df9d6b1faa9e006f32683637994c93c7240172a4cdaabab7f5f5d19e22b8cec1644ba8287ff8af8086d487f9b97c6b71099ab2cbfc4f3b72de34fd0c907d26341152435d4be15fd35fd30e370020795aba1905d7b2950f1d7b363dc145c9d4a75ee718bdc155f632ed0291d58c27c98d2f9a5b9d42bef68159c41d7ae180c1a8c0eed9c427de260e81d7898347bd3e5c79747bad5ffabfcb764e3b9a006e6af8e8ad8dd5662421085ed479a5cd9504b1156a85b0ebd358cf0933362bc2e2453ca87f74fb7b9572ede4256b60642ca426ae69439862d44a808085e1b1246f8cbc68810645776b61a988782a86b2076920e3a6ce6a16edc2e664a423930520081535c5c11e7f48b958aea189620d0bbad0757414329040b00817cc9a82a5f34d2146781f6369557971f4d86eba4f16cf52ec8727fe25d0fa875fc1246bc114c9b51bf168f5c8ff21c68721cad6ef8ad6c6f3fbacc956dfac94c635e81cc53b2d601d03eec90b2380f4ed5a04fc16ce105e4ab5d73d3a38698135623496306edd0fed7ea4276a51f7e1c91fea46bc583a75a579526e55c26accbb0a18f836786f6dcc000f7dac65b3095f3d583dfb581b43cf057fa606104261cfec2549e0e14f680185a0bf2d4e8cdd502f458bde0c3a835513b4c7c68bc91d6f85a9e2a5b359defdde326131c695ef7e59a754fa74fd983d40829b663956d0ef0798619637319fefc3394491973b28396adc186debda14678755aff1ca68781bafb0c8600289cb22ec47a48f468870e78115629b8e8015a75fa5704f12406a74e836dc5129477f57a74971f757bf5d982cfab6537217d3ad9e7c6029ef5c7989e887a1f33e0bfe4b7cc9096655347dbf38042ddfbee65ae6ca1bacba9584a47de7ff547325794060afcd78704b7961cdb7596911abcced7cdc79f5a8eb6618a1356deb40c6c8122df2299c636f207a912958f296201409ea4933c663dec254ad0fff48729f7af079fb6cfa5481cf50b42ab39e50b9da07517c0ebea86aac965cebdd75456153c88248e42612ce7e8a7be4e2694736b89939a290d766de6cb2adbaf36a650b2f9dd6b4161c019093c897dfa5108d82e367f7f997ead8417752b781265d259864150f2e680ace44b231272fdf790f07f31d3ce2bb05f537570d9943e76e636ccefd66e1dd2d1dc90871fb822aa8c38693c38a3e4c3648c2210acb91c3ba3519ded3bc35665f3303729ebc977d6591459d7b51ec060881f945844f03ecf18536160b3288b77d566f34d492364fcd3e19c91b6c28c1a85600e39355bc572d250ec2b1c35c3220a6e6bb4f81da08597e3fd6c80c98425ed61656caeabc148ef0fbb59ac72ec1a201719fb6466927e9e9df3223787040e857a19be8c1d94778f439cb9efe7d89b6b4b9d86fb7aec8d7ff4e81ef61d05bb771c0ba51e2b1b93315eb13b84ef5367149f0e30935ebdbe996ead7f318321bbe360d63e1029e9ded653f8118f851ac6991bccc30b4a0db2fa936689ba16f4b582bcb86fdd6aec54df27c33ea55004a2b9b7925e453e1ca81372fbd9c435fca243471cdb52f523dc4849791b2fad8634d7a43a7ac3f3c36e0d3149752eb2e87059da978c96719fac4e4a6f71566e2c916a3053e10e707b69869418662ad0db10d0012e1e5d2bc3e27d438d848b2e9e27435be10c2fed51f5b3bb38074063b978052fe8a0e91f79b2bc52b42b6fec75579073f7a94acca3d96b4d91d7d0ea2930c7278bc9ec156dbe8159f763cf9116333353e50e8ac05686339331fd4ca5decca250e76670ad5dded40526a14bf5fbabd29de4eab1321e83d89a53e1dec5fea3af76d6ce92d6454c785f7804109770bd1554fdbe7904669a2443f3bc5398e2c05ab0a32472822ad4a26a88da53951ec20f5ce61033d3a72f9eb1b6fe18a31b86e40482c76030ebf4eb6cc806e3580ebf3bcbb0ce8e3ef62ac4b8f690939aedfa453dd694154091d1c20dbe3895a82cffa6e79d20ebfe6493919119426bf4dc38acfb338c6acc19e9994a046b91e51078aab78e51b272a4ac1801215bac519e4f22dc02ce5ee4e694626ac5fe1c3478c409a200817fb3eec0701e2792b7cb302706474198864d00f2ed57c8875484394afbca646271a214b2a30ac778ca0559ef1bde75abaca0bcdd2b1bc1ef721b773077e3be577f11c6f8b8f6bd1e6f71af9014306de34c8b37a81a6f310dbba73c5e42570d0b49ab0baed77efb315a59cfa734350cb8fc246d823e56dd0c93cf0944845cbba823eeaccb5df3ab6f92583c0b0b73b1bcc76c955dd16d6e740c8200c66463bc6cea149f42f0cfb7636d839dfbca7b5965ef5c279ef45d159b364a5bab939d71bd0697ddc20d432b7b63060366f8a066f1893c947db5622f534e67d2e5ef86f5095377f1c000cec179af1ebcdb7e33e13877e5bd923544fdc31126eba0e9eedce969ea0844bac030448ab874cff16b1082b09bbf38121fa405ece531897ff320003b89aff561bccc123d5f17ec386c3a8a17d68f44d24a94ce0422b1a9280fc0f4783f38505e17bda53838bff35c735eaed48de2592702b54f0b8820391748aa72e0b3bfb9cf3c0c37a98e3bfa294220633263cc8d17321159b9fc6423b9a58f59088785313363afb9259066884b51fe605e8681bfae27794777aa2c9ddd0a7a7914a70c14d10b204ea9649940c310060d557fa58fba0d5181f361c1035ae0c6226cb1664578e583809d3a21329ef4ef5da21d2cdb9c58f815089ca08e3edb16d9a2eba9fd47c7bee55bb1b9288af11b3744f10e409865367d67c870f1bc24d90b35c8745692781bb2b08d3d66769b9a9d6380180e2c157e9dbe156c2548a3e75a87261f55dbcda8035b638a158e92119f7b4a0572d31047fb754950372b27520d03f506a29628e2b91f919c5b7d8cf21275bfa297d253fed6a17298fd2b6882cda539bec43ea51e0e75538b12229ddc37f0837f9a605626d0ed25917b0f17783d5d7b0a926ed6abb3887f25181a34fcfa713779c6fd23034be419e1f3a1fbb52b1c9ca56d1cc34596b926a6796195253e492e582d6120a1bbf3cc5fca2b757b917f0ef95021a97a42173d1efc2ee512e90ed27d865d42c76b8ddac5d8bb004b2af660d0043962b7cb275477a6dcc2181ebaf109174e0a4f173f8462723a21a7e9d487362530678798c4bc51f8650d3e97867e60c98557ed12810de71e081c5007a6b386923a8d0369e53d0ade8d0ac8b8e0e2c460dd282b6dbe2ed4a7d5a7601c7d99715993884da4025bdce7c882caa137806ea9015dc7c37cfdbb9265503b68ef2dab2cee03507ad9ba93cfde52aa275b407625ef54a82235dce96f7b7997fbb60f23265c4a64c04142eecb84de363e85802bd5e745ed2afba2d8e6c2a9ffa5c36a2b03c5761fb52822627c9bfb59b19094d758501e45ebc13eed4b69b7801cc46f58b447f3894fa96faaaa57c7c9d27c61de20000879e82d6dc34d73d976d27fb801b129b441f606ab0100ed5a1e0d6eb1cdee1b679c21d7fa2fbaea2dae036d64bdd54981a1f203e48bce89b54512a9f0dbf1896914f7019376b0cdfd1931bcf1078bf9e5a982c1b5367f80a03e590ad66a41df2427b9631eb3a7101f351226fa29d505b36c12817c5f5974f5a67935b744f10b0d5695c854dddee14b2780eef5b60f2d447cfda5e4eb9b5a480a1f6bb3116d160c9da3d4f7a18a134489ac60c74556ebb47873cb5a02a5293d8b6a9acc4af9c62722f79280b1b4cd6f107b99f0ca75461f5e1b534ab4d75518972684e86884894cd7b20b09104c63e14970311d3f37e5decdb740588b27f59813e198acfaed705198ce2d679fb02b3a535d1476e08d8335454b1974de641bcb474f42efb563303a246ce842ac4e218f1109d5f356dc1dacccb0c0fa19065a415c3be21bfa15ff6d9c51556efdd37ef775a9410bbc1917e5c6b25a587d8b6597b13f4133642214283ee1652986fa8bb02ea22403b82631f8cc89edffab362d53a1c9999e4295f509c3ded4dfd41df322b9d084b4aee0ee523b4a82838df28365cb390d74aaa6fc53a171affd1328fb5d911e2a899c4ae2170743295c8bfbcf8e77329aea500ac85717e4fa8f93c372c97bb76d05348e3947f7ae0f26b3589e05544bd8a7ce53f0184f8713521c401596f9df706e9752f300f123a8caecb38892278a1c746a081f4ab2b7d2605484f599b7d69c031e704e76306a2cfc0e13a4ecd41f4b49ebb7952dc813ad3af2d54e9c2a0ccb15281e0a3542c043f5a234981103078182f4d6513f0cbb92363ff013411cd32a6c9620694916a9ef73e78adba86fedcd2e41b47f647d18fde83c44bd9081491433282e6a019047bdc08c7285c83db93b1c2055353218593a2b4f5f8e7587a95639dd68ed011658bbbf759c92b719d64e5bd56c792e696c4725ec853ae087cd8bd9e2be957bcd2fe98c152e208c93cd35789f59975e8d5dabb42e7b0a96cdd862b583d09f53a4c6fb22af63a09e533b3afed83a905933b2de01199b085e6eb147dd8695fb448cb201ff7c759f3d25c2931f56344c5558f652d1d885d82efcf00d06a8021183e4451af17b76b71c8dbc04cc19c6773db1c3e6ff0d5827694d24eab51939f4b513c089ccd1dd787d9a56ee905d16ac3ab2f03ddfae9ca0ce8decf1aa62b0ccdbd3b351cd2b55045244e67f90ff95a2234e0564179aae19f1d5f439c4219331ab1a6890fd77a282cdf439143f5605d34102758dd9eb4c452355ed2923ed260c9131dc88f3608534dc5eb9e048dbc83d512e3c7638bfb9dae75344a2be34d82c53fa661e556f3a5bec6d45556044d6b41f0f8d44222ca109f44fc4a668563ebd3f73d9e6ec86e7d215b82b030bbc5b965277612fbd2450ab625fb34a0b9ab52dba7a32d8d29735e0c284cda42364c81f2e13e743a474113ca5508f3ce16d0444bc26cad0da743b84048003b4bdf2da98303d6fb04a944661e3cd6aa7fe95cd500de1ea512e6cb0a6a4c308404898e66427cd8153896864233db90fbe244b5b306a664995bedda7b0f0b3440523b0d82f6633ab8d8e654fe922da7d2dde99ce5907c0e0a7a9155d90de9775fbebb45e7940ffd31d00e369e31af26898d700496e48a0fc1e7e1373eafd485236ef48cf8015267b6ddcf76c4adaf0507aaf9d726ae0083ece6f74637fe2fc2d628b2e392145b933128ba4ff40f44181c9420aa6b3a179a13ca3d3952107e61780c26170e6565e40b705128df3d8918da167267dd71def7ebf0d8c44bf5e880aaeb03fe0a0543c8002a057ed488b6760e3e365893f43c83af99c1ec56ec44fc12b4600b94845a623e0024a67b9378c6fd5aed51259e579ffee22b4b5ca51308bed3a9484ba13a2d080a0f97c27398cd955be24097c741d6f1d7bdaf84fe359a358061fcae85a275fed4048f9561ad72cba488e7e44c9d725035b9b8a5a5aa5b2d6e945741815ee44b8172bc2bb799968b8431b8574eac898a432c649d76e7921adb10fa94a4a129d5471b096940e804327a27d9101345e5c8c8b5d339ad793290bcba4a14504323193122ca01f7485b3301277a9df44faa3f9c7daaf0e50876fdfbb05ba26309dd3a2a42987a901de793962e7c1f959e06e5bdb56e2e5ba19d31dff8e55be148e54f9f4a7fab48e27583010d6353a24fbd9728aec4dfffd24572a9638957e0683c71837fb2daa585f62d477a6e6311e2b5460402da19a28dce7ed6de041865b325493ded2358924e8b8a2268a23891256e90e496000885a71f0c9d35989e521c39753266d76c794d3aed0e41b657c592e087aef52166ae10bee4f044f3ed05c520fa17c017db543502a74bc25e8b882655a1effe46f9cd7d64156cea16b1265290ef965028b544c70451cdd879ab703f989ab3fc13fe872f3b7d62e8d7a7360d5a93b1aa4c573eabafa80ceb3c3b37232bd64f33e5351be2e50dd79827fb72fc706a84edeb0349ea006e284af5ad62570e100f0341e416e6fe8cc440425fc3262146a7936a6c13c520f8cc4f1bd411df051f3fca897feb53b39b70c4d897e47519ded782ec922b59e03930c1ccb1ff440d7b7976a981ff77b9baffb756cec24cf96d5f9f6f6c334f3a93c9f90366d41f5073db061df3022b96d7a70a4f21a2cf1206c90e3c2da39792363446a9d203cdacea7add8b57a2716b64cb683ece055866f0f661183ca4430753d771ea8dda0ca32092f49af3a9fdabc2b5aa532d31b6cf604b56db5e9060c472d9f2310d8b087018c08e02017c4d91679bb4091564504b3baab1449f1c3de8f3951d6c4b3085a2d8a92303a6e0f08c49d3fd488152993e8215893b5cf898e065b522859ff84386b00f884ed09d4cffbaebad3340a4e46bdc49fe575e8b79eac8552a629365b3707556f82a72f3aecdcd0c8688467025a90b2883939789f486af7753af025c71ef15fc34a2d3be055c2239b2e8cba44a65435cf77b038f395eced7d31c24b516aa2134885edbd850009b9d0fc8f45517f941d088b729a807b42a10defcb7595a29e067cd110dd5f063d3dde62d31326ab890ddec5e84b9d9cf47f241f5df907d88169c131fbb7c481f970f79501e126ad38a4c16328a5cd0e0db8e4a90551fed1a771e997fca09657f6ec7a7cfd3509c9cc09be78164aca56af9d89b257df92b43537866ef66ca8a16f9bbe4cedaf0704d0b5b7bbe923fc27e107bd8c277deae576e75f6bf284b9bf83cad88bc3a22c9f73d8d4a2eed92bdb2f03c5801cb841406c813ede53d3d5bdd286dc437d734e2e3d0abcbc8e3c18fc1b92181307e8bf56aba89d2a7b505afbbf04fdbb5026414bcf3deb5330d3add7e68ccd49482339b7062bf69a9c7fc080410f778784ba2f58a4ecf9dff295becfc786a31b700f2953bdee83899efa0e69cdc54a42ff4047c88899168b467e0e3195c33f9a5cb70c35620562477cb08bfdc66eb25cc16b4309b37a43f968a451a4f882c61c610fe1b737f8d7e88be7e371649965abcb550deb22ca2be5b6573dea017f1e9a47f4a77db3de17a78f549a57c1fda14a66540a5d45d324c1bdf1376c1e25dc68428ba86a22636de468167e203efee8feb562207367856920d33d634dd73d78a02544f4d4e391353eaaf74d0f94e736ff25172a3c43257512b473a76e14f614a5861ed2d1239a7006188978a8125c573d25090f740855dc298da27de0b29ba3d91ea2f4f3eb10d2b817c9bc25c9424826bb6c665f18637b662d2a38fd2b8cf021350565dc5e2045e1d698262f2d3df784f9dd33b62350f6fa2f51ab7f059fe7b631287a3d0ac7ccd97613fc4d0dd688d033ce4ed89be0b7b109a17a5208634e1bc739aa06097a54e9f6e7a29f303df85e18506ae45a035700663e1d73e18bcbaea846b75d97afa0b69c75500db0925f748d6e2d28532de5d12210141897b2650a65edfc0954c40c5bf24262d54792c774b3db28d36f712fb56abd37e1e956f2c46cea999b79590f5a78043c1e638d5b2ce4ec3a50a223c7c0f1437f56cef935b684003f4b2b46ff773d766042ff00c83487a859bac1028b0e466375b286a706d3b1ffbb510de98b55b315811dfa9ecd1e5651045628310ba3d9fd94f67b9b622dc31b7b55828bc6ab33ae9dc57ca296245c0b2d1f70553fd9d63cf396ee7756754a02c8e7e4672a19b89a8b4b57f2bc002df41d5f2b85ee21aadc5d4fc10b24e405379cbbab6d673b082a15cca3b650f7e34f1cdd1aa8dcdec119fd5b807fab62a00f0e3b6eac4afdb28babd816395d9a987600d6e65c10790050f99690db24f29ca4ad13d28b5e9627a8d506f19c1950994d01b6090e447941c5b4e956bacd74261dd8786b3e90ce6bc0d911e573adba13902f7cc8339754cd4b0c4e33835f5a40ab0cfc2f9259fd8e3982d5f5f8b697ee0448008fd38d487ee63324cab1dc0b54f4a13b43342abd116dc0482f35aa91ff0f0980e3f009b66f15ad5a8660b750d9d14041a8bb42a17c75a2eb733409872af453eb56c1c74f230665b666a437624e9f5f5aee7bb57856f581bb021458e21d7b31c987bedb9ce1d7d8b1708ebf98c0417923c6115685dca5d855e839c95150256a5c70d5e2a5fd91fdba926dac80ad11abcf776056764bae236a3ad101af9c895a07c590e0b7a72a7acadca20778525e9a345cfea5edea6a6df64e9455b504e655b16a50ef89aece5992cf689870db57cd6d62a7d55999ccd6268a76e253a0390318fd36a2d97769dd1e3a34c66548a69e7bf7eaaa54dfe0685ec6f72f02f3f22b579582addc4e2f4e6e6d3b075e7135c76cac1ff819ec0d88936e5d0aefa97f6f63e4277cd1810b135fe565b442b008500ec1682463d5825ce8c9507d5e654b0ef5eb436bfe980737d6a55988e08f978421a7afeb71922318f6927173880caa4455ce7386f22bcf75d1a7b825bb6a60d78d8c4e78e9c14c31c31857887bd44115bcb985f73904e798b17e4a9d960eba1157214ea17e986750169f5fa7d224e855167a074e52730e72a863f2575d3893d619a8e973e6e93b200a9ea7c2418bb106b52a82b3e43d9c50ae15eadc08dca9f5c87b50ea15ac8f34c3367d0a8771576735307b3804b53b96c63b29ba26953b8b199d4fa59d4aa693cb8945ed663e4e05e0bb3f1b532f0400633a9db37fd56756006a0f67cd7616e8a33c992d1d9ba84d9c0461ab100f5f0a3f0a1be8326b8b29b69086bddfa53eaded7c1fd6c2a106605c9951ddbebfa89297922f35a48b97684b56f03901dee0a63d0bda52ec922766bb8f874285895832440dae8a4a7ebb8487da00f84201bc48f9aea6294144875ac181d0d1ce94a3ac1782c1b935c5d5fc12536764b8a5be697603ff15dd388f3b68a42d4b2c3d6cc157f29ff9d7df158a18ee04c419cf84a74ef865b6fb14a85a961f68498cb9502f7d53e7756c8ad70ed48ae67f56e4cbfb7fb77acfbd7c6c03aba5527ac55da4423add75b9bbeef2e82e82a87dc3d3cfb33769a0ac10cfce1cfebd854398c6df9b974b0ea8916eda3f6d1ab6b92631724db80dd865a5d18044c2acc3f6baf1d118c96ba1ccc58964ef97063c6a59e6b4dbbbd9e2ae1f44d51ee22a63c8fb459bfd4a688d12a0dafa39c3997b802a8c2e46df74a812203a3a0922d84ef5f79d60d842ab8080b2c482988d4103b67782ca3351224a03b28c83141c041286dd8d92d3d9a60295ceb5f175686bc44f9f116601d8fd4c3c4f5f81299814dc26fba0c0a5f7e1c9feaf2f6ca39cefb78304b270488a1978951093844238fe55173e313bbfdecd1e71ba3a7cce20c7e650ef5192b54375772f1acf45582aba7bc6f0823e3f31e2ee4a5e874d2f332a33bdaa3f3d1cdb624b72cea6a9518531d3e33d1f268a43d88df3ace28026b65b070d1b340511b5eb92dd0f5c721c4bd85734c3bc0dfc6a77b15694724e86b0736a509a746316d40e7b0d2299b42e9fd99d5abd1fed95cc63d901e1b430cc8926a593d88041629db01c9c9508db4b4178e42de72be4812f84b6298d5be4157e9b253bbf6605a49dd3425987539e45515216683a2d2cdc4767371a4d629e79d5507708c5171056da19506efc5ac22666884318e9b71a8d69816d0adc44af88725e4097dbfbad7ec225c068313e025b5c8d0f80562bda5526029f7d5fabb3904172fff014299bbf882a1869c109e44f41b91b847ecbf764bddc48ea62c1ccf25a13824f4d6397a517200a5e06c44272d724de9a3893a16b30bacdcdd0f541cc927005d168c316234718dfd7b4fd0449f6e1884ae18638e2808526c3f2b5f7c5b2871119911710ef3185cc3163473036f73e76462ade5a499eaf043d4496c5f922d794600965c5c10d012d3aa0fe2847e369717442944bb9b40e520c59cab3c7a69e1c7983ffbfb7c1e65e6b75628832279cc5d5341b4c6052ef249e94edc9b629eac212d7344fe72baa420464762a56af579dc90de2e9f9d72f595eb85e1e0d02ebabc9117874fcc94ea48e3cb3a76f1c631f3166c8078f84ef83b5248a2f6e4db8f74c02ba213a5b2f5e68aa92dc15b93d5976b1d9b57ebc91b569f119d0335b59b951728e84d2d933bf101aa5de246f94aad06cf2cdb51fbefa0ed4935b373ebd90e9f9c6b018365dbc879e7ea010c371b9cd8cd43f6688f4cf21408a58bd80028075672e52f0f45ddd916084ab185cd39e271973f8b8586eb56a4a8232cde06dd27ac08714ef4c86ea3d6d3b1026cda8bcec6ac7a60c92c6c530723f3dfac74736ec49a6824860b62e09c8fd2601e166a3baa47d2467a2d59de2bcf93808cd395157636b7aa49c0cab020c18903c523391d544c1fb655e314a65a3e1d302050a799cab32d5ec677f7109dda03500b93f5cc50f2eb76f62d50729084e71f613eb34b4c6692d85509fa1e86e98c404575f77b692a0568569c23a6b836f7933af72e7b459ce26c7302d0714ac26532c2deb0c2441977169f01b8d6181ac808a4e1a263beb8f2df9040f1759b1f47c3df9e4759de7dfa6aa83df89de85196f29610c5c7f61c818219085296ff776cc3c199bcd5bc3475e26e218947c30cd691497441dd03f18d9e76ec36cd4e84c460dcc113be8d922a1b0f0f7e2623c7dfc9ffeb53eb7aba0fea2ee7851b5a8c024077d8cdfb610c6d80beb6e68c76004aad65c32b5faefa57475d9697dd048ee591991cb3414a5df723c19b3d426585aa771d192bb84986930cc18a2a04631a01371b6bfe80c16768fa136f2224e485099649a0c3da4a66bb620d5ca6b821d0a282f890870a7af6fabf9edaa248d521f30e3cf8f71464e494e872ce02bf34eabc306429c66482a650ef00831fc6bacd5cc6dcec1c05272b49e28ae763f9271324067d10ef0f88ade6038db38c95f790e7d86b10cadf93a1f71bde785d7df9adf951aeedf0be3c18ebf5f7d43a7fb3bb43771a2d434250a9537bcea74d98527316927c917794b6437b6ce12230094fea4ed40d1c91f808ece8012bb33c5682e93a85d7a39730d3a54c772da3683c8a531eca87db245fd3db6485b79d2e0f2ab23986d7a55f68aa90935d4aa371ab31dc895e4cc196f91835295a5767a14418335f61abd29926776ae58eb53251457c6e4895bfc7d9367609820792f0745c441df4c07b5c3f7a505b616b3c38365a6425d1d62c36c9727773328c7b4ef31aa1345652d76ccb2a3569ec73539f88f3bbc31f82b2b99b77291129836a817a9783d34685b782c4b07156cee6549805a83c32d331c08b6b82591426c24244001ec4d451569a6c802afc4a2ca1158188f2863b0bf206f1a5bc8a209692910ba04f4ae8ff144f817380834b4ea3e49a932e5b00bdba3702a6992bc52c9f97e948d5d32f81b29c70596d4bf40fadbf7d64ce15d9ad678912addf27ab419a0aff17b6432636b62bc7e29385f33022303b584977d98129f52b44a7395a1632c276604c2495fd95f6cdbd83230ffe01af104d5b1216eb7dd50c6ace3557611dc6978bd1738a24b53db18a8351d4e469ece1b105aaf75e0c398127f71cde0350406d74335851f1b64b9bb836024228dc17c5fcb5be37f50b912dd2c8e27f591ecfe10416578be5259863c9fccc551c9e31de3cfa6c7ab7ce62dffa6826ed1363b8cca185c16a97a086eb92e375935ca9327a5fd74a297b40d782a8aa2898cc6fc994af1300b2c4c24e314222c1702761f255783f186313e2aa6c3ddd83be2151f6acf28da9e82de73deca9032a531edbdc750896e1c319fc345c3660f52f6df0d3a93352c2de2af71e3d57c272a5dbcda6ec967d7cffc24212d8aa1bd86b10e8619255360e6b97d3faf560e47ec1bf8acaf99bf82e93625aa479e7b14c6cf7d7dfdc2c89b413860057583352780879ce229bef10c7b88d4826d29fda2f549b4421f12c288a4df8d6726c06949ab212fcc7b62d7351744ad15af1b9137397d63f9ebfe3d0d90838c694116c1ad9c2e550da0ee59f03b32abe30c65fdc3c8dd4bbbf58dc25bab2b9e10a8e48144efb48cbcea8dbcdc892570abe2de16f4891b0107c5d2491275f96e4641a7486b1b9d8f0d5d0e286fdab0e078eb67eb3a66576cc29c18450b6dcaae240a822ddaee0ebe1644384ec7c22031de32999ae44426b33937078c80fcb9ff366318a5bb8d8a653a5988349ba5032112bee79f2fe2fc95613b6cb10ad5febd005aad23b86b6df43da98cd2d960cb13ad03275d236d1c6b603352b41db56ac26658f90ca666947e81434d8793e4dc3b35a69c01c1db7a93e37e91c16a179e2f03a8ef7b3e5ae5707e20f51295d09f2f7c074efead7e0e937fe52d52a780c3ed2cec80998d28f40397bc368108457ace75b58ff15419f1921677e6676d0e9dc61860fc98ee0b2717d4fa01d530f2caede720f64ee411881e68b608fe95fa123d684a1848916b3f4c1297a5e01600c4db07bb6f84ccfc252fd8840731d9c03b8e23878213e95c6a32acff75df306a78544d88380c1f5824fce6e717ee757cc1c92b88e5a895f8194854584e780bc979cff57d4adf14745b4104da1a2173e618e56145f2aa25dfe82431cba0f61a906998a437c20b8ad22e42a965b494333211956be382f75e346518fdf0f174719cd3e2e1bcd70404f5cfb353efdff5f28517b165e94b40a8bc3a88b61969ef7072ce2c45da66742288b1851828f6a27c30377415b7381818e779e3e7966e39d3550de80b2d1bea3b9974001fe9932c897cc9710858ba79f5504c9c1f98a8047d305fc845cf6171283b1e7d84ea058edbdd78e71b438d4a226d47ea2505984be847120ac2b3b011b2dd59d03105ffa1d3bbe78355a39f68ee867e049a48545e11bb47bd7ea853b75f3df99c3200a45272f8c3c0325ef5b245b0b81723cef7166a6caa669f195c7e0b005d89878d776997c4246671d9db344c28bf84b3f1b44c7103eec305e6cf00496afd623ebc924da4ab8cee1f4972c35365bcc8b3456f9999d22e13622d1ab86355a721360f78c056528e564b8a537f1ea6912d45ea5b961c389bf09192cece052e6e8c9a356da41c87595629b664d5546783b597d556be5b3b30a40f922eebe42d6e949501ae835359ddd54686599274498a38c88dac06badb63485e3fdba1a24b2b4ec3c5954fc29c462765c1ded8870b93525d226aa93bae429d01bb5b7492eb472be98e85a1b27fce8b48b9d6f3d1e89f1baca198bda998f72c2fdfd82256d25ac8a80b0c49571b26485b9c6d9c381362c92a3106273bd2f21bac8763bbe986a55534d546313e496f6e5ce187cbeca37af72aac734707062faf463b8e423286598f304f24be649b191e6c9154f18b37716a854680fa3e9910e627013e3fd2a51acc3bc606f4223b340b45864a5af4e79d06287b6ffbf21657bf2e94c6518f670ee6cd56c7df282496622c4454bd42273fff56d1e664341482113f1c716da9fe1d1e29f8e7935f300c0825c44fc37f4a5f3a01d6f4d3cbe46c978b1b1fc72914066a71de2e77196b251b3f71ea19ab9f1b278dc1d0f6fbc975f11f7a5fe6270ab4841ee1fd6bc6ad77601341abb9de3a45abffa244bd9f027877a40caf74b67206e9739443acb1144a803d99a8ff53eda6ed5901a3538126e1ca7bd4d038f03f4230dae66af4807aaeab077c422c0e1af6af9ff8b6cb7293ef5f496dd4d3db5047e2b1c5063d869ad4c95f2e0310b7435b669fd7c0d3ad980adadc405f6ab93e931a65510f96a72afcf926c6614e2a5999f7d73ebf3f506c9aee30768f6a739a4c84bccb10d29e4bd3bd4d2aa99bf32acb57546ef51532a8af7b85a617f3caa96b3f0d5a67f9812ab11769a62c08553724767d36f896f177a11e3fff27e95082de6709352831b269018b5fb8ddae216d595df815c43ba74fa6b38dfd897c9e8defdcb6c2501f3d37098163b89f6fae5e342019a6c9deb71fbe91adc567ce9e19b1426c13b6417ace700b3582f4b48466abe9f430ff71fa983273afe2c71c4eefe5d321c966f108946f021192b3df2a3e1979e51a7ae389c3cd7c590f2198a07653c559f57d3d899490656aa4076b78bc3c701c2b1a166c22badd1ba590c0ea0b01d24c9fec5732d288e90abec90ff8a2b13507e11666f611de48ce943232a3cfa46f18e8e21d777056776b09a8ae8e76ec2fe32145e19f399005905028dd1cb1b35381dcdde55b362c97164b6fc0699bc0ca541d3dd0a29e2caa1f5eaba72c88e809286b16f7d6dd42de065d0771a82a2ecdf509c84f5381b63b45729a1fb2d4596daedba285202ecc045ca3519cfde897d582b8bd1707ee42c4de9b9d605adcc6286636905bb79787633449a76983535f20226939fa6cf69c84468dc55786bc96070d955534ba292507076486897a585172aae41c7684c85f9ddaa600346009272be63c35935ba92c7447186a55675d0d03b093aad6e690dbcf0af9141d2d16c74a229d0760e4704faa3f1746be30c1ed313a97ea1d55f0aed22e7a5c5583f325569142c7d13a53ff215afbebe2783b174fa35fab74b48dd73b525638d23d670731207dfa685e9e999e36d9ece6fbe1f2eb39afcc7e6b6d6a09c1fc1d3b9e41841a4aa8d6dee222b1a74443083fb595ffb35a651ea4f2dbb0aa59779ec451b0a1186df50b2d242021de6879bfce9d3b7cbc80cca5752b6ccc82106afd1299118c0d156f4ed8afe3a1e62d23d944492c830f653dcce51813d1cfeb783d504f2abca652f6bb35cdac785f5778e5c30cb3a77a67c0aec7f379fcd20bd859b541f6bec204715823b03d2e34050f7078e617db7ebf7103d59499052d2ff6990b06c50bfb1828ba0870e913e937af36c8d26ecf39e264b9a1bb360c44238f7b9c59393f497da8b9060944918f5b966dacd2c2586136368e5e1578ce68fb31e33f12f5397275f96fc46b00100c6ec7b1b366e3c1440de71e0a5fda5cfcf3b93433e31cf4cd5b208efa7c6840e5509d799afa0c0318d16312effa6e6b8946e87165d232de7f64e1ba47b8d673ab0257ee8de4181454ef9b84413d123477a6058681a6b894d32dcdb456646314e031731b61485a29e3087370d7fae7d85fe0f946cd31bf51c96bd63255b64d291b674874516a0b84e6816ec4ab2db00d380f835999d6ccf19e7bac7f5ef62e62250b69b08d8db71f70c6c6c07ace4754ed25e3790158510ca47b72c44aae3c0afc0bdb323ed6a1ba658d005c8698c4f4d2622a1a2357f150029b8a37b0120fa0a55e77c593d4bc94d5f59e050692eb6a7bc19585771ffc2e348e27785c2d0b03554a4a21b97c1feb7b40c157e5482487deadff2d97417caaa01507758a9affce2c4521629fdeaec204826b7f25a76ddf80d3661db6da1f5fffc7217d193e1241a7214c79c5196e1e92c4c3b9c4838e72c020e74e31e3dfee7eeb642f2351f6bbf8507b3fcf1b623364b4c04d3a47563f6c180ea8f1dd7891bcdbb65ecd0b65310a4b2643baa426f5ccf4bcb4078b48cfe6e91094e35e032a4e4fb3900b7b23aa99543134f3e123c92ab0a0755acfaa1b4e036b9c9f9978cb360ab88160419b4c5d63f17a73505e48aff957e3812744fb90883fb76b6b365ae5eddfd0c00bcab9e122bf200e62edf4c7f84f3d818688be960e65839d5fe94c5df57c53a6a72ecfc20b51aba770dab5cbc8ef1ea873054803143095249628de917b436c7dd88cb97f111463a751ad6a959d00430b6c0225773567cc6aefbf604160152bd27dc648d8395fc0a0163d4cd13eda342ad3fa3392ddfbd84f74f5576268976be2c46dfb1d295b089be462e0fbb30af4fc3624555540cc115398122cbc1bed86d5aafb064c54411d82ae07c658d9817becd3edad6f1438011060b56f4cf6eccab49a6dedcb53bb1bb85c43ef29d3eedf13ec532ffec3ab6b469ada51838cf284e319f6823a55102f3690b4b9eda9c63a3a5f2997f2de58c2e63195819412d5429afab229935f2e649b72e0deaf6fe97acb8bbb3a822e445a6d232218f92307e80b92bfced41dbdb547731bc9e9cd4260b5d3d601612a3aba92c3dc0507e6c9a57fc640d7ac30c972d1168d60afe6677ff9c2925fcb0d7935bc678443865fcc24a9b2af91fa579bddb59cea1172ffe3ad993f0d46a1a7f4966577f6413af83f65d918c4b6c520b4b3318a9e6c5d467a61507fb29330951bc99b20326c464144b0d8c7289d82555c37a9fdc2eb90fbb0cce18a763667eacf940a53b681b4d93b80f35b38095b36f94a49dcdb77f7cda9f6be87bdc8169b40cfb9e6fe9b0934db7781355115665ba47fdfb3adb4214f2d5bbb222fd382ffeeff55cb96152060990f1f24b1505e5fb4087d7e913e4bebb0bd000a67ae67999e700276b150dd7878bd5834234f383d9ee47fd5d9cd67120a3dd0ba6a8f11605d45e6904fc925787819dcec7a9e77140135a2b3b782d90401975a9c67ee9027287a4ae41101f5f3239612c2cd74bb5b2e31af0e13d2bf98acec485520950b9b81e1d2505f068480790937520b05df6c4123e6a80b0f4e588ebc155141c7ec4b5f151b790cd0eb50ae517a32dd2a0d114dc69a9c3da690979a77094d8345170bdca2f34a65b98c3d79d23b9064392e229bac2bb530a718b69d32ad685ae39b01fde78f14f66cfad6b7eb1c839a75062f28b5c1b412102f00168f8749528f9407cd1ec021596caa4bf3957d5eb4fc22473fd6818f3a58bd3526ad15518a1f4316f0699318710dcb9c91dafb26f96c094a6d76fbd4c20031f7be22a665bf5edebdde1987913afda4c80bcbbd16686c1b5808e5f31413966cfebfab97c04bded810f5bb4e6d3cd2f3cfb40e9a595642b9158f30c6857a39d7e8feb3e510d2729fb1cd839b208d86f489f0252deb9c6f6b288f4653acbcc3ab5239f5e32ee75b7848e08d1f6643af8d02a019aa66c94f263a685a5dc8770156fc1cd53dc1d034d32e047adf6f0b5656df373aadc8fe2ab74ab9e4adc24fc92068731458c18e11749831233d444f81297a9fd6b21d4697e7172f5645562184c27f279c1b509d28264a76b95783f26dc12f77668f7fb154fd1259a17bf0f59356bd1692ba16117311fc6a2659247112d5b714d0cf66c41043015147b7bd4913031652cc8bc2be0b10b4e425286c0e943b71a3258f1c84172f58c3e6b64636f73b0d3a2fb36a4bbe6411225388c5cc5cc2e1662aeb1949b02453f1e6f5144229e52d9b70829d0ab0b8e90e0810f5ba551381e8a178554297ab64d053df0b16a4e303cf95767bd975986dd74590d607ed2fc7b1a44caf361f4a2133361baadea5ad4455e918821667001becdc3116d69f8606b573ee4c8a41121d4b0d70b881aada0b9fc0c70546b3f3f8477563cdad751169f0f077a81264b92e2998bdac42670243f3796ed3c4c4c7f8949dc627e171c2d5c48a694bb6b185711adcca82fb80108e4fbf81f355e40b3bf629707637de0c812248287786e1db2b601a6b736921027aa0733402e492dea2ab23cce9f1495a5e18bdd8cf0f483d0eba1b2e1dca874f2a911fb53dc20402fc46aa5ec67507bd62fd4500ce6bf8fb2e28eb517276ad9dde36b5801a404f460ff29493dd5f603e4beaa024ee0ae7df2a736decab52f0655bbaffd651efdc0727010faade5d5e061749b7fa2db528a2638cf848c4ff437dc2761ba6abf2888ae454cb43221220b781fcad906d9ef02a3c464026ac0c58e1c0d101894a606c7b5de12564be2fa3449b2df846e11d455a353c756f46d2bcd2ad792a593693115330c1a0dad99f2b703385c169959f24952125f30da746a063bbb53d88ee7fae86699f315944e850533d79583ff474101f3b1874a9826a10acca1f5187cb251318e0a83de2fdbb536ffd003c2bacc1b6ee410b026659c035528f7d0df10248bf2eab7967cca03e2eb4822cd9445c846e1e6cc29e4773a285ba7a55a872f6acb0579a05dc50322431fa1e48cc2fafb7d0fd25f7749b62ba83d34a75b13ffe3c6df61f0c54260ae5dd27ae12edac2b409c3572cad418e30272f535a4e096c9a87ae42987382d8fcaa83cc92e9093956f20c40b82f6f27c8c3bafe5bdc98b657200f2c6e2c521ed11a7d5896bb3882a9c28550385a7070e339058835ef9b0be254c3e623a692d76db64c23b90c19ac222c8313c4443abd1a59f044ca5a788ceebdb3b7c48d1e23dff1386ea026e0733dc72f12d8e3db0801577b5a22bcb16998f2c1ced3221e3fc856b46a015800da8c7f1c61381ca72d9ce52a76a19409d6bf14433f12923d158e407bdc8b5b3b8a727a5370ef19f4694feb1975b8128f5547db2dd3fccc6c8f40d7dbb87f3ebc281d59a143c2e052756d16d30bf47ae05f4c6e49072f10dbc20d7797e09d4c7aa9c9f915822f62a0e277b73ae07d41b68cb0724727598cfff09a392f6d106f884677644f456011b9b4837996fcecc99adb35dfa700925509b3400a262348625d38f78b8850426cc7270c77045ab79ebeaed03de6d5cb396706843ad62ba8fd09e209967cf6e414cd8382fafe290840d0c1aff4f8fcca4d71ab303f3416bdf5278d332ff863dd069e34d27e06dd6d25fa67efa73506d88ca45885f1da3e472115b1197b8b339cecd29c6a1ddcc389b373df0ae1d1857849cf58dd421597fe795f6aa46a1f306a5440dc41d40d425e3f89c5ba385580e1c57a0a3ad10b39386c8c26e79d1915355e33bab207612a4d2acd2257bd993a834e2a326cafa2ec42b629f806fe31819679d99c2449984c5d4449b909910fb678ae9e5ca596dc3e85bac5c1f3febb225758a10110ad746947c1e9c8242990c877ba226264f194a059532a306f32eac67ac435087ab73545c3723e1e7e454d28919c9efc061e541720c66c1d42a5333226dc4c2fff8b3eafa27d35fc9f673caf2724976cd2fd15a5bebd628bd8feba0294d463e742028600ffcd21dee73cbfed317f1375008da835739fd8d04245b934786c7c364cbfac3c59caf4352f1130f24fc0aa00c7f82b6fb5a72a191f153f52cb66f352b77c525ca3564ae013dfc21a9e95f4a402a30f06db0a5723a8fb9141afb1b12e5297aad4dcf88cbc3d4fb0cd6bf7ad59c179aa67ac3f5cb9fff65f5481fd1b9d45deb1b2b084ccb0ca67d94346caba31b74bb327782ce8e277b4c871e0b859bc3aa5bf4b42e8f009ab0263459f77dd876bc801197c40222a06d8913259784b63c1a5d9e67d20a6a14112daecf2ac685b0617339aa3d467468efd73915a4ed23b63ff5d51516c0ba7b07c5781df4577a9fac4bbb98d730932ba5f9910de8a20a0a205e6ce7369ff8d00c053abe34c6441ffe8d473e1242a4e282105d17d632437a28db2951747d33245a34ad2aedb402331df28a0c038ad69891d91c70b7aa7386c3910e64788f6d3e1a54a7481b51a3f2ac8cf93ae79ce1b6d9f44321dc1e744c45866680e3328ad166bbb5ec2f84d34fd8f757c6b2237733e130f7369e828cf3339367905416ad2c6a65942a51aa37f6a8a000f500dcfb9d799df01dce5d04e1f3d268c9e5e03f2404737ceb67d40405794c3969df5ded7b462a4e6473e582a013b85cb42a72df9187069452327c1514e83e47dfc7f77d044571e00083ccb5b18f687afa32e3082667f7e8725e5943cc0751a0d4d4c70bc9bae8f4216b224c1b0be98d9c4b9b824106e85f02b0d4fda7619a65a9f7472a383058945f2f69967855667e1004ce8959a68ed2f7d22e565300bc6308d912de9727657b034bd56bad800e2ca7a3df55ef894157286bfa28cdb79818dff4f89802075df868ce3b2c4a1927528b8399471648b357cbf55e283f8d7b017db63512e5255edb31613e4ecce776542a826e72d6c1360c0b7f390704e0ac1f77a514f8b07f92b75953476678d72d8f1e271dd372bb6d43b94197ea54250ad2108c591484491d9944bb11cf58ff52e757cfcb825f1a43ba987aa10cd1fb1d83c3c72e77a5abc0230942fee64c895699ebd04be2171c8cc5de9744ab23ec90947603accd66f9e46d5b27f5d449bc1eaf7b0aeba9f70cdb9596e44b5bd3beb6aa821a221f5f8b2a51090bd0fbecc208ef6515db786d57faf2412c630beb8785f6d559a43bc555d8490b96f968531a95bab7becd3dbef61598e465a4ce84185858acd400870e531b2353291d2b37c9b29949cf93279e79b7fc4b0635aa35d635ae5cd5d7971497bff318c6557aa2be9117dcdea0892f4448c73e009244b0b71d7dd7b2061b3dcbba7b4c78c115e39095285e0c251aa795059b072cc88c62cae8d0f086a5ebe390c4033bcf9025ed437099ce2077111fc3e4bbb1a1575de3aa187fd2cdd828c846ec7d37c65561d71df93c51aa211e3a15882df5d1da8369a1f493ef295f6e858016a62871a2601211aacef0e4bae63f3a78cc5b87e675f2621bc3e298105bfa263ca00cdd29b8987f84b2b3a8a52a232413bd8f9257e371e9d011d24d8af0131c1f460ae9b4d0d228359e0f272b24756e3e18a723b71c8ea1c3296a8be2b7ad7cf75d2065172a04f5ace9893b532cfbf3538245754a329c523110a10c7844e6b4509d02c01a64b4a5192b5b429f8e947faad3eae02408e3cae3ea233232a6eab776334924f278ad2f8e8475f5f265a483816a76b3ce70296baabac88a8dbc6c310f7d6d2e65c038bd64a182b66a2d0c4f5dce2208cd65e19abb6fda70f0c4e0a5d20e11f4085c712cb81a410520acc3be0a0a87773aaec91ac9c4280ff7ee95637185423840c9566cd5de7991da293badfce125956011b7fbcb0b9180c941385b14832abb6efb7d05e72a06bd41b06877eac48e5462f692d2cf048dcbe19b478b219529a1d3a0ebbbf08162388acd1d0268dca4745923b5a08d5449d73e9fe1763c9256a9f6f8a5faafeccb87687fff7c34a9e421c3c90f9e1f4d1663b224bbb1afec4595581945bd6d157b9918b4808c283504363a7723c0bfd9a07743eaaf37a86b1419c815e85b1df491a4d094d8d32c581d137992ce32080bdda4de3df1fcac70323e89d93be3272749cd5c8931003d5e77904a4c4ff74a16a44d827c5ca3fa862e17ea03d6083173cc7a0357f70fa2bbfac99f396ea57d17c583cac460c328924af3f7f57ce4f2b7eb32df04c8b216f42a69fa82948208e305346dc4191020585534cb3c550df8c33d72f16c1fd6ea08cf5a583b956a47356b112a8cfbb0ddb72a9121b174cc1fba2212c2f3388b0d1c07b5583fbbcdc8ebba0dae6071b897a70ea548e59adc3e278cbde2a09d32e2867c2baab0a234b321eb69e4091b8ae44260fe1ffd18d749e367fdb2c50d63a44cad3ccf87f1a21b4e43ddbadcc6bce94115af43af08b04867eaf151bfddc70fdfed2680171ce194a03589d94ac52aa1443030308e3869ea6faad5c5e657e3c4321cf8c921fba5f430038027b5357abdc401fde8578bb52f02564896d02bedc0bf07794be29fdccf9d04e11116b7b24d043b73a7745edb8ed8e58d5165af2611977787020b2a7d4e72460e63640abf358b6e68003865b08eabb37894bb7c3ae4aebda2596ce3ca6c3e3302ea03322364616d79ddc34c9aad38ea959cbdd67a75f726254fc174710f7aa67d8e5042ae1d501b3e8d9ccfc9a09b2ad2e8930f46406e7cfc18628e8c405eec4e272b33933d58298a34ce41dd2e4429356565ac03ec315b18d8ff7ada8312cbc08d48d755520f0fc9850b47964ef515320827c0b09924b7b2705ab4e546e8ab148d609acd46b25a64a217b5d4f2be36442e334e0b78bb248399b388ce38f9a697b884c80b9be450bb126100dcadeb6b23cca2cd6e50f34417bd5a166289631c0413ffe69f994e04021c4d0f81e55a078cdccf6266a97f1c72cbd63475e08e84eba5a49c83e1ec1a9574089d73f50180b61627f3cc7bb9d34c546ad5df8c5d8764720ecd0df82e59bdff5d0d25c4a4abc452abaddf6c08f5affd9197cf3795adc37c8ad67f21580548d88a5791996a26f281eb1ce97f07de91e17b89e4e4ff51824298f938ad630f7b54de6267e7277d4848c17cd7b86c8bea9781712212ccbf77b0600bf9582334e2f81cbcf36b79f13665924c27baea266c649a93853f335efeee98e1b77492129a3536d41ae7f347579b0118c9bcbb2daefc5298a5a5f2346f82e616f03e4b3a2548a7f47f21e5a7ed71c7d7d4fb9c3abb066373d987538364fa342e9e91884ecb8952f1eb44d406006f81dedfa097a658432604dc0ab1723812eab508a55307555b2939042a09eaaad706adc68e1816b53576fe8d212552d5d35c58f25ea13122d6686dc12cb1100fde87fd30cf6cc55a351ada944394ccfd7f867b02f6b1184cba519503933150dc65a22c1ba677b00d3fed939c7476606af6db2c7f426229ebc82808c5f47e186039d2c654535d8d2e6cadf02dae319338b53734ac5a6805529e9f5bb65bbb5686b2d8894991cf99382bb549a9785ec64da38960f0f50f040ca876da8c80bb510905cd1b7e0f3abcf84f6fa2ca0ffa8d18019f09c4ed8c01abe63012d959d1dee9f29b9a47efe6eb4a561ef53ff66d6fdc819de5981d8a539143e02a14059217f7615fdf5a773b5191f33c01605a5ab7d7111d06c3875f1be4bc5ef76e8aee1eeb9d45cfe1b45119eb6d0b1d855edc5e6e6eb2f6afed9156e9463b85569a9526e4eb85067e30ee658f6271e0a2adedfe30270389a9e09f727217cdddfd5a9ca874b267eb8c9da45c8053a7a6d85b8d566f3d771012d9c924ad7dc821d95dc7646bdc4425d33f097217da9cddf40c529fd5a63238b1a2a554df0035de8d0a9db70aff58c037ddd3e449a793cbd45869393dac9f0eff2bc4238d99dc08d4232964b5d4e72ec680298c92344946bd4c0c318549ce447d7a32092c1bd74508819d4634a44efa333141e3217081edc6bf5e491d44bf3fa8061670c4812ea588467b67dbce4eaac54487e9ae299d99bb3556cc1f248c32ace9341b25f469675284cf6c89afa1928c48717a20ad34856a9a99dce86cda471794b955d6542ef9379969a24f7dfbfcfe54577f578f322713b17a936fd44d0601762916d340e9ccc06cc0e8dbe9eeff911c67422b71e6b1e3288516a11adb640db2311b90febfbf2482bc9452ce65bb8ced1073938bb3c6179249c7e715b2500189db07d1af20a5573f74b8623b668eb9b3eed72d367dbba6c9c38328b34b8f8513850b97b5647d269b11137e10ad175f39be79bbd9c53cedd386550d7e510b8819b75402e8e57930eff3fa7e32b87de3f77bcf97e12c12137772e96f462dbbaaee2f3bb591cc0f63efdca28531cc32ba333b1fa614e41195a868a3365c92e56ffa4766136242583228044a1c96bb4440a2e5e50ee0d0e820c3419a46668e78de42cc616f3fbfec98ba336102d1574b5750b170bcfd36c691bcc5507d1fa9bb832e871d0402e5a6833967e3048c3e9d589b0e533ac042d16f98dbf97c475048452dc90f229b245605a20b1d85e8cfd5faf5812ebc73edbc8f3cc622110274666182296f19a8744f33d273c875f77541061880831fedadf7f0ed3863c3c955ae09bf3d495b03f04335b92bf384820f6a97036fb5b9d35c18330377eb1934a096f525a11d4f106d40e3698c04b6aa930e49ec38b7dc0f7a5c3d1f3479f3d78c978d0c22574683305e11370d0eaab5ad69b9138e62e1fe9d49f682b496f52a0e2539d69c09d021337bddb9fb274a7f272d4505823b9fd1a7df74ca9af7c39bf488ecd9642d08024554999be5dd0531b04bcf4514a5e52ec40eed86397e9e9a6e245116314917dd465c987d097b8a3201c97a2c84589176135b2996be6eec83f3aa30d23a5e8ba6a6d1af6370799e8b1ad3deb2025a1cfe07e09d0f78aa9b31c4c9920b9a129c6787eb7c687c431e9006da9ba8161e48fb5ad410b586af996e49cbcabc872fccc2702c8165318bce82981b5a790c29ae1b001ab312aad5f5cdf420f96b837205edeeb6786181b24894d06566915953da1c08974bac5d510b98839baf1d52127f7d8b1d0e7f8d860937bf5adf31182534af4b1c73a4a303db2804812a1c0d3297beb58e4ab6e75eaba9383045b43dacdf2b5a5d2c76d8fd7bf22676ca7a5c1251028e04eb98359f614e6398879137c8685a73d6a5d5e9723a86cebe6e8df14c1024f379147d9c93f54a70085a7e16fa67324065778c913f9ad12e9c51628639773ad609101cfc177c9dd31b136dfd95c1c84373eddf3fec07fb8caaa49c144bddd04f9555b16b19085fee767b29a0462fa9d8996215af210a5feba54d3ac41261cadeab01e84a188773c698b7c53e1968ebfa79d999665bdcefbe1b4728a61fde475173caf8d91e4286e15f8a14a322af41bcec98d9fb3f8db73fe0a82dce4edf7bc796cd641412914862f8aaa09907879a3cf93b358e01de5b53c5dee03c5247864dc6d70d652c845537db2c75cc781519aaf9d8ca1ec487fc07f800f9bc0425ac466481f9c8d88e7b4ceba6e4991f4cc757512ccd458b0ebd67bdcaefcb51ada74789319b9fccc58c905a6ce953d44fa20f846e9733c4185cced940e885582683133794885e6904907e74bd763901e9dde87f62cf5350c21a851b62aa6e53c2bc5c7e35413c8e07f5096b8440e36b8577b38e3c3d19c86504595135010826898330ed0cc11c07ffa5239665c8d4563e11d889d0629a6682a2d5949c99396451412fd7231e8ef7def6d79e70839d549c01e8cb54d0c7bff9db7e0dd1272b19f78be64b2de84e49d057283762c615546fb196b7245cada55b2def15dd717b4e795c3cbf4bae644c5f13dc8f61e44829438375a36ac0b9d4a030c9910777cd4e2d83e0603bb7a20d18d08f6f9ef972a329b11ead9d0e87162349094fa576343a5c873b7d35d2bfdda0ee63bab5c49e1be55ea9c4d44a4519dd18a00d45d11e07f65099b33f8b8e7da7178037ede9fc6693acb4de72cebd5a83a29ca2f4329d2bb8c55065a861acfd7a23cc270695958372b9c643f2493aeac6fe262b7329975a82661fd607393439395f7bafbcb43126732c2b30f998957282552f5784aa4d5534138726d5a25bb7baec976dd6e830ae23554ca846ad889cb52eb1166567230e1bee5fff6f2472220cc6143f64b77de6483384594a5e6b484921098a15c1bb128f1f10f365203e5e6ca6900858392aa22178e2057c71c3ccf6c1b745574f076377e83e2d4cdfd30640c85ae3a92fc7b3c755b9df7fdc32ca1f49b8c454df8f19dc5776f6c6ca5259b8b7f245d0e4613a1ece09808027c66e94c923b306f127496826a911923ed8fdb99b5f9ce6e037d161b7952461ad863196c6388cc99156099fb8ca33f49eb0c8ad9e29f161a6ec3958dae9a9e6abaa7786230d2a73fc9c2d529e5cb4e88d74729e3ed6ce27bcbf27f380103d992dfeb7128d1d7971b4802bf3fc3d6edcf00f775aa7436a2795555cb8f7cbc031678b54c0259b9ef0e2f74dc6f2ed4886504ad8e81d38451328c2c89da8ea96097b6f8b365083c672703107485e4a9cb5b7cc393f77f2ac6300bb2f1f3e590f16a8b36967943484ddb679524df6bdf4f863a349e8c69c68631b93e6f30e90a854937af283bc1237ab2169473d465418a387bc558219ccbf82c64667af4bb8b82225f587ba6a4c19e76a4554619d961a40822f526b11e6811192412369c4ef3c91b058f5ef11ccfe54b44901496da7c7de29e6d2d7d5ba6c9f261d0f1d16dcde73b3eb6e00012f078c972d5400b87f733131a75899f4c1858583248a2ee2fd0b0c7528b3560790759472ff117cd770cb41e9abb66a5f68009ec185e069564ea00e3fc0aa299d01046a4fcb0d438a04a16de849f7aaf4b630528e6020e8280f6f76a0c4b8f2031985b1e27519f7870043f69a4c65a8dec65b26b2b41cb457f40c6da08cdb82594d8991239e0816adc7ad35d9e6eb5592967d33698f59f827c5c706d68bfa5474c9ccb6cc47352983e8703c176b9060cf5a8b74afd1500c909d23c10d57b55bef705aa79ac142de2364a9fa36671642767df5dd869bac56b8fcd0ebe8a50de1146ad72cfb2636828c201d218c4751fe6342deb083b0e639ce3854cd5f7b33c293624588777e2ed2399fe4ca03da5a9656baae296d9f82d6c5caac6bde55e72b2cc8d5e5886863aa9685774ae9d14f87c90c789dd3c693904ed8ff3c322a95ac8297d29c37a94fa7ffb33a3a8333674be7aa76a327e3873f5268f8daa46ff0a29de958d454ae4ca0d1ac9d22af8dee9cc69c2c6fe65cf507ec45b93e014ced432dc61e163ec35972a729a73beef030ce07dda22022beabc6dd66ddba23b758fda89892b168e16ffd850d173911a5a4956741392d0f1ba95008f5af52b12b5e84a5be45c68f9a2a29568f090f0e6c456a079640966ba0305de1b01083a8cbd4dd38be99ad546246b83ee94103a9abae7ca88aaee78b9e8fc80200453797def4310380f65a6f81178ce799796e183092f688a29e55f16769eac3c57cb7596efc030a889cd84eff66618a7aecfa31e7841ca0f98e9d33c20a6722269056e0f2cbfbe547d8696d886b621dfd78eac7c038dcd85ca60dd98bf7e563e216539c509ba5e5f7ad3d69cd6017da76b00fbe0ec729ab7cf3bbb6f1b6952a4f9c719541c18de1bfd0c0eec45da858441eb0008154ac341e0432ed6a67ce4e0cedaebd3bfc63847453eb81736fbcddee2b0c5fcaa512a4bca3a650f3bf39918ea02a3970e1b05cd864464bd725871919786746c39452b36a9aa65b95469ce855b28c7fc8f1cd4d670636a6fd56e10d60165af8d6e442624f0c6ca3364a25da35020df8e1729ec89fdf6d21630596c46d4a698debae1d15582c20fe11daec9b1c118b0b7d76711fdf9bf026134fd1f324257a0e94cf4ae054b94333ac8a135f4e45c851933331ec160959c49216ac10415e231da2e273c64d378ea2fc116f1bb273e435741e46ca745527700f44b37beb9fd978e13bdf30a181bc82999e1c28f49192c6945ecbf94d4d76669c301c17650477e3e3c16c065b2ce7986402d05686dffd4bee97a06e507761bbd86c771e857ec2d297044bdff998f76b65b401b28e8de384dca7c631c1afd1e5e14dc162acb974ced876f9f6e85279c8dc47eee4c7a549bd79924ab14bb539e109159f55ef8041d8b53c6aba06e39f57560cbd6fa15c01d48e7b5f85c5e64bbbc92081d308846ed3919ea50f29356b86a0807e535e3154d250dbeb400e63babb52090944b45ffcee063b21799a92730b590e3c88dec0f69ed2575a4b9f7f7a7fa6406ccd0551f5ca4eae77a10d52a2b5951670b5c5864829f5a5509c814a1fe0574fbb101f3757fcd7d6eef7d7454038fda07c3b37d29479f120ee64ee148a85ef4f49531ec381b85e9c2f1f1dda22ef19c79f13f49085b68d7c6a4994bdead4540e29c7369872a865111eda22e6a7dc496fe35ef24fb66b9480501f6ed2d51a0c00d92b5a4abe9f90c9c7fc766bee75f24a2862af5f66e5b0cdfbebfc82cdb4b7305e6c8469c28de87b70e95928a2e6ca26b4c9bc0cd0a662c842c1b22a7f49d2606bf5523b564c96cc33d8bd10c5220e1cf0118e79fc0d4955cc0a7a0a9e4c294e0f774516cfa62a51ec30123a0c3bd4591bdea9071903a7642cc5f0a6144cc03fc5466b69a067b1b2dc1b2e68a422882d4ab288542f3f99ac8767a6ffd200d309d831cef78aad97c1eb4a188286425d5b8d6b3e48ac53f60dd3c0b79f305d131745b0b8ea80b31300b5651a0d77bc55589c124384ed73412a96a82af0a9592133d52ed37839d875f9f3d5b6474adc7acbffe84c1b03da7f43fdf3f91ff0ea5b641b366e0ba67964cd00783f1e4e2f9b21f9b37087bbe94c9e866421ae7d26630e0c13c55152dbe85c303f92b00a79a04c9c258b63a21e5c4ff247b445ab6178115c9808102995b92ad0c7365704a3bf85560e9d736e1cee74fe678734c20de53b3859d1570810ab1787b12d996f4d1163d977ef82d1dfc80bee5a2e1e65dd1e1a07cbb67baf70918002a6add22b36c45045ffe51c3e3e784d233fd2878f106a645e7a4236a90bda7f511af582fff9319b019ea112b0358a3c34e2009cbc39ac1eb7865604f9cfe3cfa6faf563e4fbdbc876bba1cf468bb8b1fa1678d904353a703e035ef88b7156dece63604424f55d68fe30dfcf45c98ec7350ea3d6411d881082649d2a5a37862ed420ef30586234a08e22ab6c77d4bfe4cb91c5ce982e866fe6a17ddaae60a0bf2eb031f1120d618aafe2318ff1ff933c71afce754bbed6726d9fd59890a1a880f40eedd5ba809f0647e3ab675bed404d2db092bc7297742250f54ea5317367f726a4a59b554b7064c4d75a784ed9be3974c5888cadfb4461c8b4b38eff0ddc8c5f0bb204196e91d7c50fc5648aab69b59a7f82d12f8ba767323737371d5ea73350c82e48aa6420acc5e0c6a42e9d13edb23f8c0237b49647e609cb0fe007308ce332626adbe7c323db70f91032df3735610b3e9a58cbf02470a0b6542c89fb43700edc23a32c3464519aecf48b36adbe7ffff2cf33f3fb5a2d4d124be8f8cbab0e603bcbeea76be8f5941729f5efaa6cb82cd4222be1d92e5f54b6ca152fdd11c47b1ba22e15d42657c3dfd41b5b338c9ad50623a5b6bb453ac8662d18fbbc205d36be997836f21b0b5d334b7fe300cff6b59628aeb7c556049173fa07e13bf16c23f3dbaeedc798e3220ca9109e71541f897bd729c79b09a8d39f2c8e2e85f57436452f88f170c8030e8995f5ff784bd9bb5c7932c0d06e563cc85566e47904e8cb56cac835ae402bc3e5e59a9228194ab23e26d778f75884012d7b09787bc9a18ee897b00e94d0a731eed245e44f4d0cef461d265e0e2e2bcba60e17045bfd2cf40997b26e690af3ce40ec976a91f5379e6d4666334c1db8dab2a139f5fe982fc84ccc9f61f4c8d263f8919745ea03ff5e3c1bf0dcb71c74ddf0e3bc60fc03cacd78018d61412e0b3b7b9646e1d207411793f451aa992b2e1dc4eb1a2fa93ae086a33d1a10b58725161e70f5210d3f15c7036b3e3f8452d47896c5c144de6d7fd086f5ecf45ee6fe21614a290eb6684f6501812f18c37317f703b2e3fbf455a8dd710cef015e273528a0df518ee9761873837de6bffe0951edb82ee3e120c4abb41bb27493e0a41892c1e5abf0ab0096c23c14a4ff985b9e0e77d766bb2cae282cc5cf29168afd3fe83f42e5e6967fd58dcc093d361e6617cbcbe45e51c83f3a919a66f6875bdff1f2a7d8cbb419c4ac11289206180d52d96140effed5ce57ca029eb9cfaf26a54c13e24794e21e2711caf1ca5c706e5f35e82b7ef26e9fdd748851fd46ed9a06842088b82394c920c5ca6e61aaee4509d07b7e6e9c7478490fb18489f2c7d1e3db2f6ceb24cc4d194f834ec51a2a2a3cb083ce2760a4866839082cb77302ac50fa6a3cf41e18fbe134a48b4f33e55a09ddd1103c15904f3512a2b18d6b4453b200ace1bf6da4de38ee5d3e903140712ebb1a899f89bb05de86ab6b5494895a85fc0a5f9d9d18eff3c30e0ac93dbf6b25524652686005890f685bbd0d81a69d7e6fc5fbf9d036bed90ec454511813d44f5d33dae7f9c36e6214843f564884423a302c6473266fa078fce020420054a62951d2c2b60191f008020a51c59ebe2a7b56b6ed26919192aafd741744858444587f274f1a94352fa87f7c4ecaf0e12d09e36167219d5dc0d3d9193374cf67080d4b8aaeb26a1e0491968fc171403487fc27ac0b69be0c6d082b8a6abca4ab8b4fe1936b1b66fa074721435666ab0508937c84490bc6488afa4b851fd498538117f57a32d72611f878cf23390c181f683831be033b9fc8361abb22e090bb18704a56c3e8b634ad3871913086c456ad6af43e698276b0fe67f05f056ccc89d1d06bd71f0168a5b85d61f0ad4f6a39bae4724557c1cf320db599c66177aee9da626fe348d3e270e27ba2e7415318d62ce02476ac158e0094c85cef8f26088e35c732d48cd1bbf0ee4e1d42595b6f44d60cc9b22d465b4143ec37d0f0510593eb63567d124d9400c682771f877e7917963f6069178f6ff08b5d2a03e56bd3bc36220641a563a9aa2b18f862264fa1ca2bcbb119f3f154e5584693158e14910fd3e502288b3426344d357da3e89affcb2e25bef21539c3c78f7b3889f45f094919d9064ad88f4a2840181a014b475fd7ab433a0418bf17b70c625898cf054361a2072cf4e6c278d1a3a64938b59fcb5cb012a47812991e9a691a974422ad07d1ea3f4c1ce86fb9c6558c5236dfa85ad41a3895eaa8c25402b023a2dabe5d09600a45073a6fc2ff4855572167e7b5421367871c82f64140d7c76000cd28398964245f66fca2297108e0e429c2a300228a2c0c6d73484d7862b941495b35b373b4e83a55b15686f09ba99b5ba4a290cfcb9155aaf04886c4809f08c84df1d1338925a34bf4413770350583f0055c7de265dda6e54c20d31c51b3bbd7d30f6f256a50f452bf4ab820e892d8b764739a10d9bc79f7eb2775ce0d0bc8226c1ba1912505805d19ac76b9601923357dedc3788b60a5da52fde1e93c9406448b26af9926964508f92a372988bc518ebf7de8354fb926f476ca9d43020bc77436898f567f5ebb00f98f6ca64edbf4f79ce55894ef2bce68734de0328f3fafbda40a6c0863071a3696c75c870ac5e5d89b79dad330403f2a4cf5709c04f9d1a8f45df49e10dfb92b10bbf26f565c575e62a1c92eca454f14fe0190c9e51bb0a38137d2fc1d77cb2d6e81fcdc39158eef1780752c1f34ba8fa3f33460c27f6d54b76ee247d3913e4390a06d1d3aa7f21f25fde16a5e93c80d49bea0bf5403a370703d4d9533b9ec5a6d02537a5c80d7966ee9f4442c835b72587aedf2924961860cce7daae9d8be623317f4a00c8758ac5b31cd943470b0345da37712b1e39e3db68bee71f1b0d651b7dadd5a68cde89a63f922ed273c851e1d6a48354caeff9431539cc89c5ff0327124f418f47fcca5b16b6b5e27d85f9611fb1aa11cbb5dd11c76d42aa00e3d172649d94c7c3c74c84a06352e1dfe537bccb3eed64b089d52ce2a0bd5f6e21564f3791b480f419ec042a0c6a867b5fd3705f229a4e4ca13e8d6d655ddd4610e2e61b9ca97c1579d67b144ae7d17d41ae78eb0007c7e93dbb5121f90d16681196ba777afa795530df6099cfd510da52321f18f119099f1c52ac5e541888428a131c645b4cea816349a0b7880f77e35b074636083c4be60e12254f5020c53449a414a280115f913507cb3596fdf0800ae5043ca4324f1a9183c915446ff48fca8148962e423dcb016503daff10c07f9ca43c9065dca04d5cef82dc4490148a3a2118f95ed22a15ea4ae94a01172244a2a24760e870da4277147fc4f909511b9b714a1448d05c37e857de540931ca2be560c53199e27b80a1ea1ff50c331b45c024f59ac21ccbf718f45d6e895b129cdc0910cbe429936b73e1c6663471c840571290c7d0a6b8f9931176a6dcaac8bea1559f50c2ffc69737c1f658b96576a26cc486d46a6a4c989e93d8ee2803d33bd3ea3d5bd77e25c8a9ec8586d1f17d1b3ed212cdd26f4f9a4ceff35c6632ac6f46faf8b98c70b15218db262590d8cef1f035c5c1d2e4f53629edb161e8adc6d495fd16ea8c55111bc5f86c84c43cb2c604f217f7036172ecddc8ee6f63de67edf5a2b59a7957d731c941fc6831fbc18337e536683c4d2cc460cd1a59a5204774cf4980d2c5e2b4074aeee17b605dc03be5036942e8ca1d0c1d40f9c4f7bd1bfc0d158cfeb3d515bbde3564db14ad96a49a328e184d42e558464d6cdad9453bf8b2b10e946ec3d200b5eecebbb6aa3da57fa09991e51770d6872d0fdf4da2052c502498bfa70502457b43625a5f4ca72a0e23cb8e896e325867d802a44b3ac2433f63ff12a160457dacf166a519e5c76a7e83b95a04867a9753e85904d7675674d461fb3b2831747764ec7f0b8e32889129316e2c5e8cacfb7ddcdb32139580e6cf93405aec6e0a8260257c7c93f907baa1a023bfcdac3c89fe265a9d88e3ccf21a870a3dd6badf70a9e58f530ad2748cc0227b95e77b7f4ff01ff95a2902d1f7064cadf7167187ccdcc5c502ff19d35c24e6cfcccb5f5a093475f465189d1a30d618525f5aa75240e8b6c0dad30bc30a98e0ce9bb49aa84a5be1ab16dd7f7ee886e64ecdadaa396eb86cb9bb0d9c4f655c931c8f445fa20185f9bd0f749ebbd4656f8dbeab1074538134d18cb0b4ae552ac22f386476390f3a0f098e801b02cfa6ba4d17fa95a2a182187030df5229485050a74b393bc0bd7896b6f27eb607efd3a374105a10d06db9507318f0df98fa3af3ee5b30f6a9fce82a8591fe26897c61b5644b723256613378dde5bb52fcf955f238a74de6c1264b2be29c82dea907a5e4518c30c884727ca208ef03ff56cdf2d4f82d0ef700c88241778f592c7bba6f7186e49afe720df2244feb4601cd8606b5de0e66c6671dd7f499e14f61b79378b22e184af3df3ff6c8bece3b56f22ad103a3540e63d890eeaf208703546de83678ca82788f6f1ff399887747316b96ff938adbf78788ef5fd1bc32ab9145eee7114b5d1fce48c65814a8d009507432ff10b4a55b7a07a24a355475022c0a3f9d64b4154b7994a3a94b1ce8e7f48ae42f738480224c05fa183475d620a1edeb29d99fcfc077bb1981a1ee0a67fd24e6707618f91205cb6411e51c5747ede700c88d3c1847e681af2cd3e6f711cd6c0ba6b1754bc7e86cb1d383d11f9f14da53fdd78428faceb9703970507a94af99ac2596dc609482e243721396f2aeb11d8493485856aa113819805e86fce0972f086bf1f166f7427d2041cdd923de0fdd621f42a6120e46cebbc4add8df6a5b05a0b2e215805d3308ec3e6af77e00f870baeaaa4b4dbffd918dc7374281461d71f4bf9668d2113f5c88b52a4fadd0e1a7d3ed8390345fdd4404a4cf51129490348b4a26c0fe67e3d6a0f8e38fdfa5cdc1f1325593151ec37dc54d4e550804572451d344166390a1d3967172cc1feb732ee02dacdeabb697e26a7718e4e1d4991667eb05e139b09f3399ecc635c201cdf7cc5fa7c5a752aa779c744d055e2c445077c4a08ee33310d43e8291fa19e88f8bd80d5ce59976a42342e5d0322570afc4d1e9c1208500f85866f6e4c3951808268d0be36e5912a2522b6bfb6102bf5977650850d8a9cce8a8a24a67a7501b931d70b89aaf06f4691b147ce3b953377959254a5187f1945fe103b9cd2e0a322d4879688fb6ae2d9c36379683fde189efd9d4cad34b028fc641b01406e673f2ff2fcb327027634948dc6b76e55310bfd0595c067655f16299b639cad6571e1cc15ee12518683f73ea3adfc5c681a40b0eb83180c5541cf86f09a6e9ac241b92bf740962b97acfca2dcacaab528a03db16949e11a5082ae40d7b43d3af9fd45627cca2c60cd7a4ff1eaed746e4ca35821440706294b5eff54bac6c1c0058f5e212a24e031aaa48f5de50ec8f9ef526228599597c755561f5011dc3c06bc093d4a8f7e7ee313ec830f8727a74eb6a59f1449ecee773e7d30c49d02f937d15e7bb87cb58f6aed6f5218dfa68523e630110e162f065937ee8e487e14dc6a2ef340e084a9b5274e14951c8f03fbffee0c42853b176d751d7968aa8be1467ab1e62e52c01fda0785321fdba19caac6552c3ec81b21f26c19c1b86ae0fca1fab62c9493a9205064e7eadf058dff3ab5086d123e0408d8de5b9e5500f972a33e2776fdad4bbc7602fb66c63fc55a35250b9ce4818d650cc828887be6f901cc8fea7a07501665305b67a185100fc362a6c255ec8d4a6db77b20743ef3df2bc5d9a32e2c4a01336136b6f3903fb20eb33bd5d7a1d96d461d3852ebe3404d1246fc02be746710a596be6cb74b729a1fdb4a7b8bed6f0ce26c8273a06dfff3ed895a0935cbd8b48bfa977f68b5c71ddc40bffa30a316576a5645453c666b02526448fe04d361a46bfebb932f9fcf09af21a4c521022adf3aba652e8517d0fa8130d8a16f9dac0897ea706964c2b9f1f24e9e427b516792e30eae017df07fe041a9283b976f9ae0d11cb3ad21d5178e5a181ff8d2bde04dd351d16a0ff4b89660a416b7e0f559ba930190acb73e76829da61fc4f582ff666e146510b2f2839c4ce7bc4a120d66b1e5b821629023be066eae5020ae2f4458ff23c75eba4318d7c406503044ee38622ba2d19947bdd8ad27b84e3ff1608117c4f82d61f96e3e220d8ea523c052d6fdcf550fc47c580164b6cf8da6ce69fa20b1d1677189301e8673e42bb0d33fc003b3b571a209c57996d8259974f6ed0e5f14510f16d19e240db3d8774ea396a5ae71f51793cdc79f1b854ecd42849d35c7b6a04e69adc866502f802f25832b5deefd67b51c420693a471041d926782796957b1b2327f014226882f35aa1e7a512ede386eec1ac2ad9cb9231049335e8e32298cb145c3cfcfcfd4c0a28196c6de8cecb1b389b1e5d776a859737858d0cf753265522221c9522d211f2f898765bc219fe2b673a2ff8d67f625e33b12f50b365e2acf0312880f55527faf55dc9b15e73c3be28559e09d5a2e83c90d2a32d210ed718c14096677b27afb9b8371f6bb6df76465d14e56c3bb61e517923f6089219944b420a61660e0bc828e51b6ca39c3de483a23f126f2f01700495010a880ddfcc0431456a92c25fcb346913148529e3f60763e264bf519258ef8b378dc5497ca889b13a38f9b138c86a728150ec2758023c419cbc03a243794f5bd10477a6a91e8a2475f4c33ab0175dd7823548ec66702071479edac1274eec7ad3bc9e5c515d9c368a2bdbcaf33256e79e9d32828d6ba9d76b944aba47b11bc4d899855aaac6f94160793876cd1597f377842ce23ffa46e8166ee2de28b86c59a54464883aba34d35b8ec5337dfc6281d381e6c98d896403587a977b17e0f6646585e0c910e54d85b0a337c4ad092dffd367b763556aa490cc1968d64839a5f76cfdd5c20d285bb2dd55d78cf59dd62a94fe1db5e876750d320a878c2a537891871d9ea156713964b9f17c74deb6f31ab2c6493726cd286a226ac7a7f50f8c918ac54d1319baaf92634415fac5c1eb6fe4d89320adfde80254ef3377de478802de25b4bdbc1bc6c060413b60a7d9be8261cd5afedf1290f3b678823a97016c0fc06f70714f77c3204c87085d0ebf12928c93ab7adce25c95d3abb9949a335b2d7489c7730b6df8d0d0e7e53777768d617e601fc44691f459650678e1210bfea173826d0da394550336e77b9b5c117d6ec01edafc9560e7e2595e563fc5ba60f382e1b0ddb72cc0d3a3b2c2fcbac811f3e3b31513a3ac5d341a5d322979ba72dd78a8e1322fb41b301557a7cc29b8b0778118821899a33e3f65fe5279842887f8882598253a0dbea660d308dd3dfdb04b5b98b7c972bc95a2b9e3d6cc3b0a2c4ca0a1f67103c36e92e9bcd3bfd30150729ec55e87e844ada27ae9ef252b8ce88fe2a95580d7670ed0c2c9c7d7c0b70cbecd8b521f51044dd507a52911cfe52f8cf81bdc40e7fefb73f9695e95fab92e59ef355b3bf327657ff655a2cc8c7677a06c35af8349b72981e4393c23ba0e6ee33c23341102d578f9cb8b33505eb677e67f9c47c66d4085218b1da68756f57bab40559ce91b614c525acfebc598d8e9ec262d89602deaa861437583b2a2f57e911c8ca7b1ce8bf109565c3ca99f2c9a34eed340a3847f05d15274646d3bb95061463752de4f20504d1dde5e576ef167085e3da01aba299e05c5207304d807b73d59befe919af27d86f9d5a65df7e63cfee9c96147b1bfd3d385240043042abc2780993720072c60463e824b5c11969fc9b61e24ac5ccf62d65b0e07c3953e8793e0984aeb64f2298cb84ba424827a3f3e586c23af3bf197b12fbde582d649101bbd845c6623520456bd6a180075da46781d7c8076e69f38d11c8eda349444fa72d5359fb04950f1736061c70f76115e1597689551cd568e7410650f26b8ed6c5303446c145a14cc2e0a91a74b1c9e660b005150c00efd79d69524d77f4e0603f1aa392d98f7fca4f522fe38d0999fda81bbcf0f0f70b578d2dcb7663d1fc1d7bfd4c5e641e766cc2cf54598804ef7f84b24a44b3ff686c5e55b8c361b1a1829c21cb241b64047bb2982d46aeb57f6d13e3deef61d1b7b36d4a05407e2b8cfdedc5d9b9f40eb8fb4fdc5b33c3421ce03e5e6555ed9c1e1c3b64af4c90a49ecbff51c822c3fad7ae12116c0a0da578310cc305c99d19b3a2d83e5eafca8cd1eadf0392f789f43c74934a151fb013ec102f1cf73c6c1b05dd1c335a9f05f6cd8f74b2ec486c1098ae781eb07d8eff784b58d55e338a8adc3c691eb81845a9722835e41d422e76f37439547a7bf6fef361e5c8686aa94b56700c014983c4477251c9b4043401b3f0813e641729f9a26b0e701da2a34e60cc0fbf254455cef8cf8f497703c9d040aeb583460ec5ccd26694d976b1ad0c99f2f9e62443b7970eab700da03edefe8b942478247c34f3aa81fea4ce7d6c1fe74fa767625255df73005736082639519a169460b38a22219a2e33e22ff6074922ec781201b0da7d53a9d6ff001252d6ac261eb47bba0ac05c87d676ec8559ead6f7044b0215326dd95fe19cc9b741bf816887d91cb97c2c94fbf9d45e4b24bdc55750c92086344fde16972448a91a16c58211209df4732925db2e86479a93e0cf115a573f9e5ea59ea589515831705b615263585191fe1aeb40233d4d8a69c2831f4dbb2fa71711601b9c9e84a6edb133a7c0066357466177382f703b6961392d93229d14c5d2c1717e00b6b17e17738e44d196cc142984be7680c0b1ef52dc68ac3d5a5af7c652e3df629b056b965e70b547a700d758a564ddff8078b86f6d206443703a2e2b34822c3776158830ec33ce7c63746eb34f87d3d41f4092d0c60156c677285c0402d6579ccd8a6df20b2f4012f7284c022da90e6ce502186ea4459e64b1074f1746d9f05d439ec0b6ab13a2314426384882d763414b57a87f27e651328e43e66d2af1dbfd2ef70ceca7b97eb2646bbe15b4d27b64accfc3cf8758130020564d3acb82160fb54d011ba96768895ca2af116f2d3ac2e3ed84d1fec7062c5f4bf2d5a2bb56cd820c82933291403123ea00c7e4ad0a2354b0f3fa8d091954353b692dd38aeb11db5ac64e9fb754447fa3e00420ac1485b8014c18334b61cbaa2eb531732a0f032be1779bb33f897444d122afa19ec02a700d624b75b2d1841057b8f8020ba89c0ceac3d155717bc56176c11dcac91a2aa674f0c0d0a8b20bbd2cbf0b9ee3df4e54979d648193c43929d5ebd92402f426a8a814cd664ed2a38b51c3829ed1a83003e9dfcd86289c9fa6f4b213405cf7054356ac6c95c89eb04f51f7883c02c9e9f6b555adcd5bc6eb967665380524a0a34e1aef90197b889a602cbfd2528b755cb6a6bb29faed504f3f24d0cd63ddc100a056916911a9059e054609a903f86e2e988389b13389332b9878cc7dc1ac4db4c6e6b922ab95cc50550e759350bd3df9235fc2330bb02528ba079d321df6dbfcd044e471d207f3218372eb49f97f9bf5d15f784b4b040a2a09bb7e72b61ad3f4cb0d575ac077693c4b495442ba0447ce0defc10f0c534b3d8a2e436c023e16669357af949b5e8e5bd70db39bf5b18f88da2334a33988a7fdf9d4e001d5e9c374a1f47e9edeb9fe72ae020d6c2fb2b6d08e54cf6d13ec678ed22709eee3f9b476f66894e0a154c6a09213bfe86a4d549a6ea6baf9556d5ed4e8622cb48d337651d18631ec4f90a1fc223131653b7211a901e2bcd71d00c78b47ac320ae62ee86d3b5f5d647539991b882d24d4860e6fa4cf68de68de7842c3849f02a85fb8d6beba0f6a54cda10095fb3ba45655f82210b820635d91b2378439109c237918840ab3a521d0998c92218461126729dbef7b90fd63a29c526cda8a63e54b310cb555055e7a545753755d630bfd9cccf8269e71fa79e784d2c819cef530d0c1efdbb8bc70a6557475ac1d211c38eb3d7ca65f282c7c37c18d39f0a7d064c0a6a468d9346368bac020294cef284f96dc39a2dde40ee705f0ccdd754837f07134ff1b0a2aaee0a3d62fd1c9f96738e9ae86bba5c8ddb7325b4746813209e79523df5a733d629ebca42ebf30ff1dba44a4ae0b3cf548b094c427a8b9d76a0d9f97cb413dd13f939c8a02056f1f6b39e554c4d30ec0b794026d297460e8dac077b64527f53923e9021f6ffef81d345f8dc569df4eb34b9dc2f078d4fbab00dcc57ee95fb2b1b5acb2157c88920849ab1ae702dd42684700fcab232954bd4db5c44a2e84b7ac639027b058f350c554f40ce6483c5cd5ac0fe485339c6ba88dc849251ae253520ebf5a0a870ecd86667b5a90581c1644c7d62a22c8e906f5bd34e3d542d23b05a5637c4a1a6dee66530593dc5a357b7e52cc31137f398848036a539c7dd0fbd71c9a5076ddc3426038ff2165a169e834a9d7367f595c8ad1f467247705f650971177b3ce7f2fb5f24644844a46e22d90f456ed198b9ae1d1fe5a3a0d49ee13dd488a5ebc9d9934e93f868719310e2799533b92f673c9db6e4d27905b4ba7edbcabd117c2781b44f443afc081a9f5cfd87482a10e368f4dea5ee27b52801164a772dd891ea62de6c2125e49f55e0e2dc00829a18dfcb6e7c0c7ef43ad79b0539947981ae78e1426dc66f8c03d67caba0dc3976d31b27b9af1b577199ffc94286a97b25200b0fade620237c903940558359bfc655f97f407c4517d27b3c6e2ef406342ea52036e863f506da8fda0d2e12e5afc8f9db074eb593d7e8040931d3e99d01b599963af225a71459e76b3542cdff2164b746cbdcf595c2539a0cd238ba1c60fa20a6ac1e0dd341fff1940df2336f141e6b5f5098b70a96795655bb6aa87c118ba1991d35f2a1d3a95a4c914fdaf34bcc3ee108fd1e77534389405d161b89ce80dcecec4aaf311b70739f8c1820af8a92b3ebc38079e60a28eca888f50a43547960c65bde1a44e80fa895f21b2b93eb5088a9f1bdfed53b4b51aeb8548555cf6e8d71a0d6ec16e6681d8f08d0e4013b717861295855c600bf31cab2395dff1348ae7d9d44c9d6a04babade0c8a95d3a26f796e1ac6a58b622b18a259acc78436d11ffa910761727de97b84702989be91488536791c031698d9098a34dc45b2eb8ba010b6099f0ecf9426e980beb6f4d547b28edec1f5ca1f35b15e0553ec7456cc953c25c8ba31307715c4f2e3823990e35d203be5f9fd3938fb4c504e1abb3151ff6b882423130dbbccfed87b55cadf7fdbafe879d68d722dacf1397150bc0b6b4c1a305a8973eab1bd8cde9bfc54202df175af9d13e3d97f41559fe484cccb98edb0acce574102419e717797721b728b6982c59cd04425a5d566ce5bfea27b25bd65960c7d2ff966db1f05465621703ad83870bb6754992c7828535dd327b392d7df13831255b3bf28d090038b13dbc287dbe844cf3aa479d16601a454e2a37a014466ec5d661f4a8fc9940e8660e6436cee4f62a26aa6e94af5481a11e501f3a20c7197841d4144faf31cdd3a25cc45b86b5f67b7b6b2942dfa5e23cf81fad8a0e1180da14d2849f67f959ff649c436dabfc1850b6f533708b10c39f17f728769dd62f8c35cd30354171e1d8ec08696675e9981eeb68a368834de3d8ff983d25f3be514aa09fd48f52372191606bdb97b9b14f03a44fae6c2e072fec297db85bd182ce6b74536a1434379ba0588ab67b3570622ed7684ceea155d12f07fdd7e0f56e8bf13542007d5d755fb3a04879b9f5920410da43f793cc210e7f34059bc1e9d7d43ce169552ad0ec9bfa49a7894c3a9fbbdfd046e758c155c76a51739a4c0d52a8bac361117d51d17e535a07c756529dcee15642af95dea6c99ce4e5393c7ac7eefa53b834d7581ed466ba88a484c3cecea74928f53813838bc945aa27ff9c50add3506f6ea9f1b36ca7a527efa34de99fd02fe48c94a82dc6def703beacf5b157f6a01f92058e3e8de13731fafdf41c86a2c3d812198adc49f69eb1b5fa7fa58234e3445ea3e3a4e60111565c32cac0e95ca7d1581a571e72976d47f5202c6b751ce0b5ab37cbb65b38a2151f9fe011d0296d650723dc70886e7e9e500e16413f0c24a73dacf5cb8e8884824e1183427fbdcc903a61f27c5cd27399e4ce7784f2c2115a18222b29cf17d5e0101cb7137023a655b5dc4012b7032fec2906d3d7fe62d4734d359b6573b1b15a01f8685795beb002a944898f7af0adebc0a7fd711e11201b6df84a9d41289016195c2c9756fe6afb8d1eec0314e665ea31defd824ffa43a7d6771fc8864879e20a4060a23dfe313a4da29bd15ed2d55a954437b6caffb11e7be87cfaa68f09f21c10e25b18920272f2699164c258ff50b4680f1edbd7d3eeffb6160d3d227fbbaf981a48cc0c93d1c16b382a8b3d4835514b42c2d963a5992e7cb11b3a1b0b97333557e67150763944a148568b6310cbd348a2b5317ca4a33bffa5a1c3a7266cca8f6161f5f410d1bc1ec832f39add790d605856590b31af34ceba8467ed4a284ad0373d358614d1c420d0f8ae689dcc5f03e33ade9ed412aface15d3e0d9546e3fb592415154d6ba6f1502254e6a174511c6ccdc61d76e430acf8dfbdf185a3d23ba96243c900f25e59b03c5b8e2613c3cc6dd50135c1e76dc13fd0df41fa6c03e2729728728b0713928f58a03c4fd549a5df329439e3feafea4f9b45e0168cc090032008d237eb60c5a9fb0465afde756bd0d56f54b24978f7fe59bf278aafd682dc91ef90be29ccd41041155dfab0bbb0f9ac87b374d3f76601cf9803d74eff35bb477115c06cb15e2966554225e87654162d348ae466f4518cf9b051e6c2b134c50abd480c24b161d82d7bdf257ceab69917eb04d4b4082f14eb3d3e9b28086982eaad55aac86be6133d972db5481224ab4c7aacd50b9bb1c6e4da15359f2a885364cdb3db25969c1e86a379ba8500f94fc134b6f0fba8df5fea5e74dc7f6d1f121c2d5910cdfa2ba6409c33f84146f2e210dc4a4331d016c99b708171a7510ba26d30bcb8a2b33ff9f0812d0d484331ec6c097d83b295868c305d18c768191812bff8a9fa5e322a767950a732c7baa2176846ec80ebc79c47bbbe7a3dcc2eaea2ca6f8d7cd9c2d2625f73c0d7d2d9b1a79c2378f2405abda7a3e688908f8da2455c367e6ed1254e155902dd379aa37bd25ecc71b6832bea87cae0217ad9acdf1ac8b4773ff93bae215640f390018c60255f9ad3f1a13ed7710ea24c4845815e180cc0560438da761efc414c89d7e168cbea7d4802da436ebbca3eea71c442015d6c0f2c593be9bf310a50d0008019f01751b284815d07de98b1cc1d45da440e84610c5f7307fc62bc26edf9af6dde1a1ad39683f48e7a31777cc5a1d2302a24a7687e508a85610d48911d46c9b7cb17a0573444659a29b8787f6309f4fdc8368e68f724b8606754275b1941f4dd0f171f9732123673789ea5c960a82cea9f6fa8d306ee4e9cf81aa293915af4efd60ceef1a805637015196922720e0307324f6e2cf0dc22cb420cc803ed6756f508adfb4c595ff62356eae30b527200f14bf1968091370d3c437c737b88f135cbc6c4989601953267f9525020f864960173a958d20415266434d1d2ba17711ebfc5854014a0e153f5a239284a1617c8b6c61318d42d6e4507da03febc071e282b87d06333d1c8559f4b189e15fc809654eec1469479b57033e7512ceeba12ffa35023c99c4deab14b4a857e80f1c9cbeea1b1380c64d62da576891301fdc8c7af4e284699f557b273e787ee8df3c531accd650b06e4e85edf24a7adf0dbdc7e22423e5e1716b0841f44e2506a6a792e60bd98ab5bd249234b6cf9b02a2b3b51933420e144c9cc04a191423626a6a03ef6921107145da8977a793fc9ef63fe9bee40eab4cc10cd5164aa5ae0cf9867a9fecd0f7e78d7a01efc839b199d0f6ee57a3d03a3691adf9732735c710467a98ffd070179a0f9813be902be261f711f6e6a378efd9786e60689c524ec6832b94bd8a8c9605a519283c53aadde0da36c6dd0079eb3ece77ea19d04be256e903e72b33fe44517c676eec12e7a5573c888322e55b7f171fc6102fe861278f757d731c79930099408ff373e42e54a8059368483f4dad5247091a4197a2e6cfe868d9978c116d7bebc83f1d4bbc2d187bcba77e5f3fc62ac0dfcc41f4f558a97b3d8619131ee64ac1d3d5a76b063e4b2c1a4cc2ab286b30d832a36254f6d96e437c21244bf4f601a4cbb95f0031c08dc339e2fd1b07904826fae506368d21bbf0b01a6d0cf889d99565a82dc2cadd773e3356d6cfb04a5c8a44ba43075581cfd1579bceabb61c9fe4159d0fdf6caf69af5a019a75018098edc66c1227cc44e642b8842a3de32af0083c9289cbb3350732ebc38c7c2f5446505a2a13379a809019b65430e603da391ae8a432580da18d4e88a7109ae073133e8ce4318021391e8c2fab14abcb67121c0ea5cfb77ef5b0abb8ee99d72399e315486e4bca277fd32f225712cc2780ca365aaa0db2b2ca5d2ceba9b7c943299a158f5540d17da4391491d29a96a0a7529c6e3bfd587208b8560f08f0900f1c952cef9a04988696b4e6c82260e6641045ff3b366c3acaef6cc8616d309f3187357c34742f814dbecd832a97f79e4738f0665b7d48c85a040396cbbf0f52eb954f5e966a36e7ae1e13d7e870c2db262e708e87fbc46b89f34a891051f179d091bde04db6364d58cb9623341a81962094ee5ee28ab7d96163d5a7f5ddb1cfc08a919871945a38c7c90751e542eec2e0404e24914f1182466d3b7092d9597c00c0053d9a5d2d8797b689a06f656f39bc1b3b461e93554cd09d7e0f82a71cb1890d72e03d5d3031730a6f725275c90a09ba11c2c746837dcc629f277d68bc787f99cb5d34d51941ff6f640bbd8fa1f32777d29447f1f138d2e040e56b892f7b883f6c3de11e70d1953861ece8c76d16aaddc265d2d5dd9d8d9cf643cf72e3157b38e327cc030ef6a49f6de4602807fd27870420b560c25bfbf8a5232f629cb160794894cfa137e999843caea62771d826289c7a121df072b83a47e0af642929db5087e03df010753e73bfa5fe0d114bc0f8fcc9506054527711a6b4915c74504bd5e4c8615642ea95ddce423eff68b88bc2d1d827430a1523ef21fc64d783c3f51d9143ed6d12a91a6e65b6a73a73fb4c07867cbbeae482c940d72d3abe2fa0faaec18dd51a1bd494bd5af6009f3b9b88478ed91467bc764f5d2942d3f0f0d9c5af0da952a557f9591f4c37ecda3cdb231eda8351bac7fa39125f959b7aa2ba6ab3c0e7a9425a5366a05fdbed54088db05de1983e2df73a696fd39874118925e9e3a93da1c54b9f89eb5831b6969f26b76aa6e8dffbd72649ed2ffae09f9244e77684ed021d417ac4e3c9365a89cbb4619e945b22b0384857c3ab16afdf1d80899190864d8616c11cd483d4212998a33e7df873a5e45127700338065a0062499b34efd03b9f184177dc8a879c3344d0a88cf28dc22b66bb04195148517d3df08296a39923ba59ab21d23458e9cfd0438b61bb0d22d039c8bb4e7e384125334360e723b54a5e0862bb100db1fc3599c56942316e2b01dcdedb2ce76e86ae74e0fab5d5af91ad540fee41181d6631ecebe8eb767ebfa09a70cd46443d14afeaf12933424587476977b0ec7076ed74c6f04c29264b6cc505ad01325190050d9abffb7da12a0ed86fe886a610f5acaa16e78940dff5a1018fb411f3a59b7e0cafe3600ac767b1934841c321378fa364abd407302de3292ae010ca5bdccef2e31ae4856f4012cd3d60234ffa90776bec93100547640fdc09104e93e80a76f19337f2ecfbb499880e5d09edca6a60a132ad14c9989c5f2106c1bd13866bd4ef349cbc7c3b8760a12266d9c2c82ed0c574952a1471d9f1dee8679ec198b42f9d13e6e6582b646a585906c8a5b9f476f3eceea55fb90c990ea3e022865a45271232ca878fc2b323baf827945c8f520ec35ec57fa8695cc2b3acfd8666a76ea695b872e7bd15a0a0ab105dbeef45caab52bc6ad1709f15f7f4fda32dfd76d36b76371d88d623b0e86f5e302aba21d60ee10afc79c3fcbcd229ca39e55ee13ae6ceea0a34bf8e4a9071626c9fd8fb9fd47471fafcbac3918c3005fd6f95abca211d2a8ea9973d247637df6a379859d4c4fd39ab44c13be7f30f69d61e19a504d8c506a2e0a807ef3195964b4e9b854973414e6e39770b1159a04a7cc4062af38a5d77b5b8ea55ef2eff0363e3f6068c1b52fabb90ef6f5a655271ed03249863071c05218e1add13bd47c70f019c07034e5a2356c5cd80e893b3bda1a9c8ad0e7ae15f1257086ea0c2fa39689dfd08e97bf3d9ba572a27872be51862df23d214338b99104230f3a888e4b2480aa6a4e7a6d6dbccfbced213578c9f0a001aa13f784592d8532dba80ef00f45d1abd9ca0016386a62ba18e7c62e6673f978bd5f2587dcb86b1d738f27b27d924e77d7a131b5d6e2e0c2469111798e7d2593a40bec4f21c85a231fec3bd7eef19fc1809009f946b9f2f2604d3bbbaa8a35e43a2acf80d530153e779b5e77e62445a978304796bcbb2232274504673bf0321311b910de45188e0830270f58a7a71da0acdd0ab73523fb2df29602946c018bb948f6826793c42e23de6a5daf07f6593fa6765acbc6284d3b1e3e8d977214db86a56d5aaa35cba907fc06ba303754b569a6e2596664e6cbd90a2acc03d2329af161aa61992f8eefc38b7b9677482126137427d78d4b8abb8dd6864538b427870db4ee997cc21531ecc6f5201f2610cee454a8a053dc24600431d45254977e6ceedc0ddc1f1b8c5b87c87a069379531edd339f0506c03d1c8d475145bffe02b5830f9d7bb596719cc94c76ac6ec6d4eca06de531e12236952c79ac0d6a3e804ecd5f35471215b6dccf118b2b40d5f155a5dc5135add57287353579a7b793563e04c6c1c73996923d4778ed17f7e1d6c894925b96f0d7624a369662891815d510317abe53a8b40e277cd0a79b27ac83abf87346ce7dd05d71c5855a667859acc32d8f65e85424bde68314a2fecc64c61cd878c2e5a81740bcbab8d915577538169379879fc1ebe38b2559ac919ce51176963942fd3397cdfc4f946a14bf4f9ecc94e0f923fbb7e59e928892d62352f1c1c51354f11b2b7f4053f581ac7e355fb11fcb77f36512d0f1b9eaf5d91155c1759e97d78afd9a022655ced41bb077f657b1f554485498b182c53a706ba5d9805060d932454a2d14583b55f761527ed8adefa441812d1b170728f8efcb19cb3061078622292579c174d6866d187225fdbe05750987a0f1c9a573962920c195f2154c14cbd0ff575d8c9cf29699bd3515fc7cc1f77c6094bf751873ae6ae59ab2e39974e474be5945c9c165d87301f071841e4545c8fb901bf18b6b285ab9e3845123a3ea2b4aa9363317d98d5214edd9c2e6f89b0de1986672cddd470e6927008650d3950007e843c8dcaeef7589336e35c84446f89f1465a7548dbe26d500303f628a41152d6422a7e013f188f015ba4c50ff17c34f770017264396edc9a51138cff562b8f8602b802c6e0f09d9057dfed4725ce632de806008b78852e19a43beeb1398336c45359724e40f6dffef8285bfc0c6d28e5e01f6f190a11815aef2d05aa7f8859401945e0ca933701dedb323e2c75e111169df7dfb858c038cba408f2b352bd433c62ee1b957966dec0927628e4dfc27472630b7e22c6b0ee8d61132148c3abcb27ff7777028ae6c49601989fe3f402a504e3f91c7e965d6d8d7124ad266536189c92fbea7d229522cf84ce395b8e77592018c2b932fdc4f586972c908815d97431babde36c477b6a617d25bc3d8b3f8c6cf89f7143ae0cbaf79e6a86652357726377b3fd045afc529716180bd3d91b5c4a24c0a4ca9a7efcf1c43b40f45abd98917790912a915fec8df45c3ff70afd5aacf02cdcf105a9ee927b11def4681a0359ec0309bc6ec23a50a9a092f322b9a4060c28b576adc3561f605869a67fd7b71414768c0b52f592e9dbb11f8a6032972c1a476dacebf225bfca65c5bc6c8fde8d82327ff03c851454e2e88c988e44b444cf43c7631592f18cb213a700971b351471f3a668d2c27fa74126a9336adddbc55110d78062f714cffb1640bdc0205207ab623f91eda21e2a2ddffcd50261e6b92cb63008425aea9e171193560bd805bd7145949752bf67cdf47f1cc984c7929dfc106af031bf4f77a435f84c65cce0802dc69fea3c9a728cd5a7ed29f23b2da22cf18c4479f4dd845b36ad3fee04e753109f73618ece01c93191e69e74e6f80218799d920321882f812526b8311d1fb034950e0a29993ffe7a6d9c2119046f01c63cb359e6adfb65406d5263a4d56962f0e716976ce40d50c858586aa5a58391489a1086fa9022833106f8c3b1c418d56ba62d82c25c8c7583f8e7e406ce376bb8e806d7f38e78841069598dd16c73307028cac1f58191a0e885aeb87bd7122c67fce4b8ec9ff2f54f344a1154cbadc2bde3e49492df2c9f2ca60be1a5515741b1a94bc402f13e5e2f0bf9a69e39d387d6ca6eef6f1d595a4f777ee5180928cf99fd4768f07ad4035113d20051456266fd0926cdf14ef7118fb1cd4810b50c6bc998648d1000405d0f1d88a67f6b1f877a08f6e2271dfea5a028d2fe708653dc4ef1be8d52a875d38606058b94f8be067980521a9c94efd3c405f362d25991d4eb96901161adb4d261640daa00f217a778a46a92be39cd6e10bd9aece8e81b5cece280ab35e66a65316fdddc1a4f0934122adb2b2382e6cdf635e22a9853ecde760109cdbde26b6c992a8057dd235a0cf51cf3747ecf799d73e0af08413aec22d675e4cc199668ddb03bc5b6ab0ff73187ed17538e7252452b47d9cf17a0872dfb23879dac7d763ac2039717068364f41663ab73659ae87261f9330ca978c5fcb8b246ec4dac1d37366c650d78e4ccf5215c629a24e8fc23f7f6a42c0012f19fc93a958ca1aa5475d314385f166833a809e3cc488580d0462c931b7a6d2f57ad27b9efaab8f669d03634618f7a4748a4467002db2d4c31e73e926a0d5cda4fb0c0095fb24c06897ab3216bc09bfb77bfa61597c5bd65ee14a451c7cee36474c0525a176c62abee46a547cbdca4abc0b34d5b20d1c48fd67ccb69625e396382bc1d1fd912e06b05bacb19afe34635cd49a3f87a207a106b9a24d36132d2c64bfdc764bdf302640b519d6eb16862409cad9f4431bcb93894415c916b34215598d86375365c4a572d5f6d0a6c2b6076fa82fb272bb598924227874f40d5fdf6380f0c25d2f950a325dde8571532e8a9ba4cf70c96afa0f615c903b7be580838ef3b844b4e5524e8e54b324283db0c6ec8cb049e934571776d8b416f8fbd2b09fe8dcd3e76ef625449c7dd1df47f2af59e3d36ac7cc5fbc4b708a7d3731c60d61fcc2973482febdfc10d5a5c890e3ef5c5ed70f8215133fc9afb5f3d21abcf8d6d2265f02e6976cf2563f83ed6bed82bdb043c3945260889f7e68ff1b41015e89a496d847f15fe2533bf550de3650a162fe3e73bef3a966fee2fadecfc649323417f823a557e1e48ed7a1112cf81fcf5179fec161f73e238cd44a0163e36856f87848c2c1c673a656db5523629d035dcad201746fc8cf435c3f57071de2f09d8d6f133a8a774b37a91d06936ed1cde11a2d706c8ed4ff89917f6815ff6e69ffc4fc6dcc450ecb9f98d10166d5dd96e961bd915c4f6be485756b5f6b5e6888c6129d4a358f491d1e834715953f0d945ffecc2935edb63037608f0a6b572f79f3965a41ee0b202443da6888b2f574385d940232ba1d858ebb458a8f7624e1c8e316ed5d803428801f7a95f1d6513c45e07552653aa6605520412385ac0b1e1770ad97da70bc06e0a3969bd63615292645849c84222e1ada06fd09d626e954a4610ff6bfa980a91773ffba31f1ebaacfdecab5548232fb461b29ce2b2154eabaf93c75e3e0d33c3a1385fefa6565371fb0a4e299197baf28aa63733223dbf4474d1f8b99e26064218eae8bb3f5cc65dbadf6dae4ee7644ea28dfbfb774386f7dcea8253f16b0450a112706a9163fa41dc423e7c3630a1245564156d0d298eed3fe08a1617613e5bf7ce8e866f5ad20b6ccfbd84dd02d5956a85e4364576f370374f56887754adc66f0c57b01b2e4859e03ce7decb56d61bae178b3ac43f1ee9dd03921b5012ff026e109209ab6f5d3a3a522305eceab99d4c9934531f7fcefc5690942003922744003970654a1c18790ec8453577cab3475d9fee223e4e1a748c797241220532a4aa829e835f4effff1179851fbf36529a9f3306dac462fbc4dee74b2efa121942df15fe740a54edd0f1e5a4610af2e7c06ed131303c531da33fd10847dccf842c760a1898464f334206408c5d98e3b325ff2b2bceedfe22453dc81438d8dadb5083c6fb22131610f7af2a5e68d4f4114da7cc0779f50f437c4a690b664a9583e2e864ed3d5b5fc146bbde80324554cd3075a8f7e7867277c42ed2c18028b65f0b1c7eceef140d8a0c1b67259f395b7e0bd96360c65593928d5295c74e2475d3b832e774c174256354da26fa274f89441c0b90ceaecc112cd64908410e379446a665dff9e3842f05f9d96841df0de339db5ed0743d06feaffb66af1975f90de541af2a9139c22dfae11d21f2a09b2e35c6e4ee30e5a012401d62b61ed4f8577badf4b641aaeb28c63d4e6a0b119e65e057f0238649570dd45d10c5b70ca22065975669a09f562ac5d6c6d9e1d7c7f5c851acb6b83a42afcd92d5baa1dba264eda9c4299f600469002b84599b62d2e60567769ff0e3a05355acb9e3c5df4cf49140df93209b1636e2c02753b8ca28a80f36d58eb8fe7fcae339b7e6c937acd5df6eb4563005831684afd367fdf579dfac3206790bca45fb148f6130e97484985d055af9757d0e5604cee09bd4814eaaafaebfc90bf71eb4163c773ce8768e2b66add9ecadbd7a1deae881495628338c0654655bf1fc09a4380abb5a4cf654d4baa859f94f3d9406b41f6c00feeea6761d7c4603f9d1f63463610b6772dafe3d0fdd286e2e547604adb2871c710e42b8e60d8bf53f5e7bdeff6403cdaad09fad81253288fe62e924f02785378da3c5601cf59c70022e9314d8d5543568900d60323ffb9adf67ca066086b3bff512063b65cf730385c5a5b706e6a814976b3d91376923717722495b040f7ce53b8f54de7a49ced7025f610e53bda3b513861984c7ddf035394e1a3510e4e9cfeb23d18fd622b267a2ede4333c076054126a8387540bd8c95255a2253016a4f76b55b61cf52a362ef5a76de28825ed375030938537de19321c359f0f1f5a01e10e36dbf453b13f67200d63391b3d021d34acadd80a5c899bcd402c3ded7a0ff01d9b0bb7b770044051bfa83bc55437cf60870da85ab9aa7d509b4a4e3b7a27be2f5b7a8e863908bd91dd9a6ac5e496015ad959c2154acf89870bad5ec8f92336ee0e259ecef1affca142b2ce7a0ac5456bd44b548a5a351a91a5fd6f4d42730fa6382a5c3a4b13a684be7f4b526c3ac4531ea76b547ee3fa24ebb41237b07e390e176ba67f3873d37219ae378e0238fbfaecc595a1232ccdbc3a995b89b11c47302b127870b83719acc7493681017cdee85afaf434627ce66877934ebd567c797d25446317b81e9a9d74df314b6fe0b2670b07701b34006097c0b609a98a090085983238ce4d88bed459dcefcd7612ee8a5924107f6279a50d036f2092a9f74357908567957868f311794000c8500d8acc65849446dd4399a912222061744399d1de855e62211b24a8331a1a5e7a59743a366d596761bdfc1c10af37302dfa3fb98ceefcbb8549b62f81ba520d54a48790d92e5f37bd25470e7d646008c44183dbdefd935af010241f9bef1acb94fb43dec6f8e3caab5c062dbb3b0d685a80c6a16d10051a34f0a5fcb1e248e14f007d81d1168d8f6cac4a7a36e30ede6c1ab21b440f2a8100a8ce9b7219130d788849c42307b245b860b899e9251263c847f263bbd3e55e50aaccf9d86c2040cc3bbbac8e396c21eb392ebabce554c9189b47268a85cc059cc31285007e449a3e8ec424fcfbf23b0522c9c89af94ccfc5dafe522d858056d27dce17914b3cb258214a11e012907ff7943e2456de668152b09056eeaa9add57a49e317fd6a0d43658290927a7f77a785e7ba37c850863dfe5e75fe94670bd69afb4ebc72883ec362650c55bd20b0fb66bb3f335f337a91197575f8603407ed33700382a930ea379c1b1e2a6045919912176c099181d04a0dec2506fb34358a6de40d558052d48c839ccd7bbfece76933dc704cfe40689db5785e653f5f9b989798d9c1f876a60969b96b3e9c03b2d2128b3c4812b41ca83be868449e21fe5b7607b474d0e1c8c74693417eb7c2d47d6dfe666293e22a12e2647329cf043f77ca981f328ff8dff057210ece50c152dfbdcfb5c619057fbfbbabec745a3b35f0c041347a8bc9eeae1b6d0dc67ff6da59efd2cb5149d6e78e3e1689b04deafaa1190101585326b1f723e7226538ca5a3cb3942e3180d5f953a8dd805ed17d74f599cede780ffc6b1fbf44f09acfb3615356e4fe40b8e60afd982544cfd62b64e30aa8d7578adcad1c663662a198dde5dac45da77f28c0403b149ab7995a1c933909b9f6b1783fd3e6a65f519dc2976315718d9438b9a23117dbefc0825da37c7192464d59eb03ea8d946cf47279d935f4dd10d644f0b63218cbf2bdcb436804b80b1a360dfc60ffbcb0216512f42c8422f27b35c686f243c3a8256c4aef2e45294809f6303c13d5abdc4a31552a92d3f8a590329103054af97f38d4a06f8f668d52e5d5af614afffd0a2b9d5b2f082219cfef9bbccf7593740ae8ef88b65f3ef3eac57657154491b207a9aeba8de84643560fd29b52102753199ecf7bc68cf21bf3c5a5fa8b0459d0da562e174d7cd623f4aeaf9fd971cbcb37129f8bd4a706cdef27881966ca883fb68c6218375bc14927e807014712a0d6a869e8855ba581eeb34d23ece392b0ba6865354f676b032075d1ff8d06e1a94d66e2f5fd125870268e5236fe6acaadc94cbf4a4126c45ca5d564c18234f6c977f5ef56d2874226ce01efd35e3a22b17ec49aecc7421df8f68273d9a29bb478aa216b0fde1a8754caa1afab5a64ceeaf08c525588c1812d1c139bacac9f75f37174baece8a14c97db7173134aa88430779f69b38f38d6abb45492e40979736881cd2c056008a5732ebb6440b93daa7b4143e21fc1f07b463d2f730d6b9c93ed787ded7cc8b022a64ba946e89c42009845e93a04ca604a084602b6012dd2a648b46d636b0d436828093c2e876f542d98c62e77eacf65886ea54f9868f2ffb5fff98ebf3fbae9766f5a80ceba10e9a7573f0ddc2426f14268d7ec4fbba8fc309d225b308e62bf20470a83b33fe6372f6ecae845f679d9571fa86a846c9a8141b51d8f84d97f4bce3c7c0fbd387f404b051066d27dfb752c8b34a5d7546d67c0f5738a531521762309daa85adb5ccb2847b6476de242fa8409af5f1e2260b795b5d25e014fc4eb7cf2ec2e95a7f840d394aa7d0a3819590b3a430d48262a60e5c66772b7976cdb0d0fb9cd26e530666a6a0509dd0126e3ee94785a627fd13ac593c75c9d08b4a26e7dd1632f78df5ced2abfe9f7d84425401e7fc7b5b0dd611376285b748646f4a8e6624cb4462beb5a4a07b4104393c9f8c5498dc68bcd553195bfcc28345e130e6f2d7a5a97acd9303f030d72599f0cac6c16d6aa61d0fea4e3945ba67f06458c90b3648fdc96d6b9a6d7f6a56e071f748cc9dc16ce043662d3d61a24bee72c434b35a37dd7f86ef9a7916cf1bba228a2c782369e5fadce30e74b6699330e6189d9bc811065c43e566f49d7d8b365c59bbf97964757ace9250d88c858c7a5e9f80b87d21481135e1a071b4b7d5d7876937c6c056916103ad10b3a3507607ca24a47f76de08bd83175e63d5b66347ef31fa09b94b3ac2610564a179b4c352e5b6e6c16bc58944e9e46a7e85709986e67c95ef95c1630d6aa7333014d3d4af172e195c28cbb41611b34c1df64f53c1cf5e680cbeea37574ed72ddc61bdaffae7e9a5596967f5b7e52d4ff5b569ecefa215aa14bf01552f1d264f75dda0324696dc3adcf2611da33edb2dee1d767e4c0ee43efba89ff175ab8d61b5cc0da0cca9a66ec2b651d17746d83404e84988477367099729736492630619da108feef72e75cfedb892d8b593d6ab85bbfdefa128254e528844597b34594b9ff16e8d8c3cc9277dbfae012611ac6a79f2d132e9227fc6456b9fe08a5e2f614ba3db095005488d602f9983e95de768dbb8e9b87d4e5028aec25bf9a331804261ac2dc9e16b62a0bbdba6fb13f04bf7a8849eb0760724dd7f49ded2358ad5523eb35954b71c7ade6212250a4aa0ab6c5661815e17c9baeddf43f1b0fd8dcd02ec878879afd1060686a34bc54cc7905b60269d8942954a4fe8baa54bb9f26316400ee275179ad8d1d5c24e8413c36ce90445e9b37c3d039957537656021fe332ff376ccce77446e764c13afcbd05eda296a4438010f5cc70a732fa73491eb11eb58c946e6dc8fbec92a4b566e472f1b9ad5f8c3605885d4bd3f9452ba45b854e2c439f466415302d3d0cb0049a8489ee61f48fd00b2224aa6a01cd8d851476235850634b60fa97687f6dc2bf6625b6d2f80c3a3de3b2ac7b450ae8396bb02d2e268e1ff58b2df144f8c7f87f7c9b2b56cdbd4320feb10c12b1f035c986440f010f40b14605c8eb7177fe7c5fbcbe734649badc152a935253944a58bf49be7e13272216c9d0f7b694f127122d67013eeef78342cc9aa7fbac57f7de9ff5538f2981700c31cbc3c2647e7944cb116c084bfe856ba4ca31964d5452a5603cd8fb5e4e2b98fff03db22247b0df91257608a6e26f63e39c166cd289665b349e4ec9604ce9bc99d5020667ef65dc926ab4d42417d7e6cd92bcf6be51bc9f6c45290673e4be960b1cce784f5f2d943aed86de7ea3537d6af4926973dc695a07e8657f00bf48dcb3dde9c6e5346519b3e834cb8e15887b9701d466091251f67d8ec4004b6bbf66bb6a007c64709ff963de11754c40080a8404bc724c38974880373823194ea1fa276de6da2e1704df47a2c2407ed2ba5d9adca2c120bf9158d561294a4f5b0d7b8d05e89f385f6a4e1a802ae17af61a40348fcac4ae075ef4021523a23e080a3232934d7951c51bc34555497ca8e4c18a20b28e62d54231868ad8d5d457ef17871eb6fe8a516d9a6aa9cd6020531d4ad0941f91b80a3b076e1fc039ed30696b62ed3d23d3777b5114c94d898c683f905e39af2bdf8efea34449fba9a969658bc17a368228479e95f17ef5bd1a57e221268c171861949e8620d67c21c91be13a7f8ab08e9a8782a41b56d82f9cc32719452ad9896bf0e8a659d0704149369ce1646fd37c32ef456945d9df20bf0ff5348e447994738c8024350fa949a757880d1bd67239df8310d7e30efa9b2726abe8aa3b5db79a9514d568e39cbbc961d788177604e5e18ca228e3a4a18b17f3ccf5ce186d783933bd5e9fad3280ea68c3f323df72459f63a9df2646444e4b9b0de9c56fd87288fcdeeb195744a16b4d5cb7b18da3a2bf3aecb9b464ff681703dc7ff5302b19da3cc3c271a583a22a4065bd0365da5e96a609f1b9a8569f20cbfba2a02d66965393b97b2f7119f1a80efaa7970211296a6d69ce02337f1c39868815a28a7f9d59684dd79946fa2c0b15321d01cc6ab4bb88ee65cc3631a54de76e6bb72d618cad7a0ccf5729d6432301e709f222a416796745abf41247824741fe8020c0458c96f1fd21402834c4746d3249a6da2a849883b7e1e353e08b474718b0aafedd02f3360047a66e6c4bcf1d2f9ec538d16a2ebe3b7e669dcc04d0ce88d65f773e6c2df0d76bcd7bad2c0532d3e06280e80693e48469080deba1edf607a48d21ff98f5f9fa7b9ce29301a30292fa1135f08221a656007dbe84fd4052386c5979ee56576fe636edf42c8db7212609b5c0bb134ffe0d5fef4e7621ed580671156091d9b1af03490b29adf279377c5eebf069badb14aaf4bf618fe3df8e8562ba5d97691848df1085d92d3926108f5c40e43e4da7e248c4906ced49de582a97250b93b46ed8c24777c83918035a3dc3457f02096e4d4336544a1e83a1209f9422b33b3026a7783738f831b68168cc4e534f0f0914b284b91f797705ff81ee2c13ce3b6cac90bbe9058f85eb6e0731b27e48cd5efb05fbf67f61d8c988b0350117d1752d3544a6731cde9e95b30c208a23678995b15c753c7c1370643b64fbcf3eab3b740704821c756348ca2c4316f1c8b5e21460483843f99ad033d3311a029d54c4ee2d5511e96940dea340448a53c2f294fbb4cfea2dc5ed148f762ea2fcc9b31915b3bb2138652179b7eb539ecba9c78dcdfe86a055453c18952e68fe9917431c5ee45924946a17a965640ddbc0ae0816cb6aab9cf0d5ccd8cb3ba29914706b89b7f35e44150e68971f4b4828945d9006319301b9f3c48774040694fba5b872b4cb88cf409cee137e9d19202d4c8742ae749a18b7b5666dee6b16975ac534b46de22a1a17bf106aa3a1460d9a2d20630eb35968c7c78359e063e2c633879216c105de07676a1a5c0595b56c6aa738dbb8ba1095412823835a646a3d7ec53395b0f54c27e767233510497d4dbfe5d125b73348d259691e2caf85570db01f3c75af8aabaf9accda106a302b591ebb5cf19dda984eec109ef34c399109057f0947e7084a92d6e1c747de956206a9e7ab3490033211e3a740dd3996fe5991edbd7013d20acf10e08efc22b9f67145b6a867079ba36ed9b866973398211c318413a6379de48fb70575b7dd42082e3df456c621d89918fc26b6142ffcbcd82b44fb8102692f3cad3ae07bfbc07061317c29e0c5d429aa960db07173d5e86501e2275b7e479b78834a6a144552b358f5c0ae79dc0b46a9086b59b4c6d0b95195320b54101c06f35452f85a50296172bbbada59a5351efbb27118fd04ffe449fddb41055a914c1191f9a6d86b0ee70f6cc59976e691800973567b6abc83c7d594255954f8018e9dfd1da676638c2022a2e6c427e745b62ef8f1a04bea2161187f6f5c1ba1ec20ce1d8946dc2d29ec1c5930e49ecb08d16c20d89e06ba47671257b050fcbc39897a76bcbb9ea9ecb8541e66142ec564f231c75eececc9179ddb8580aac2b9ed4feea35328a6215e42406725fbd05b0ddf86c2dc5ae4fbff699832993d9dcc74d2ac1b4e7ae3d3d73b6468f6bb5cabcd8ffe11b38a35374efe3b7bfeecba3c624761661e1bb14cfd791321b9b9124bd7a60f7dbf423057f15af2de0f69827238cc4ff3b61b1690b78a5ccb244d40146fbcab2294929d24048aa22dc9f5dce214ce721d5170a8c6d4fc5d0f80834abd36e4a12098a09d2ae3f942800ae67482eaf0cc3a56d2d09f64021184e890e846b0608d8ee877cd09e6611cc2b327ab156ce679c06a7273df79c819b16dc3fc6bf3dbf89277c8b99692d1cc12519163a65bd9d52989c45e5ee8c90dbb289b1989740c858074ce7f38e79ce957213b0c2ce77e2d4c26b7451c09e05d473926b015f4bd7979fdee2b231108808eda4a654f82b57e5b207d127b741e508fd1de48bab9747b3012d16f3c0362bd672692555e4e0044abe12e260285e1484b5144020e7ce304c579aefa45a97cc686bd4387b22bbef3552eff8724b3c11a606935785aae02d29d1c337e7e144642829fe9c7f2adf00696d0b3843a94786b73f4e8592732fbdc09ce6411ada0e86ef21217fddb65286239ef7f231446ee347a95582d65c66088887061a67e5fbafb0d3503abf3b149c3e22b7908a28b115fb2ece4747efdd3119882ef905caa0de5814a1328fbd8a851c2bbfaa3f39acb4535776e21a355c8b059d0452007d31de8a0cad214e9bdd4ddb23f49162d45c00c9f2d406a36188ee7c2d51c97b5f28cd94a7cdf581eda7947631a70c9a69a6a5427a0fcb898fc6577a2e361d5c238b970f9be13ba8800931d8ce18c9a624846109976230b84075c530129dc75a8c760d4682d86ed1fcc5823ca7ebbadad2a351503e17f486781541304ce25f61fa035e0416564de8fad093eca6b38d64519f43554e646869ecb068a16200f619506ae8cba95c11b114149cbddbed7d2367ac53fe5eefef9970e105336dde14fd5b5dd0b64532d7eccc268b9d5ff4263fa06893f1e4b163f1223df0f44d6a238b1edf72763b3b172fc8799c64d600796386332530e44102af4897427b06fc84344e1fee5099e519013df6e649733caaeed70be33b1f47faaca6ca54de167094a1affed45745db5b84ed31325b59e1b0a5f2bd2005e8119145586274745ae88b9acf0868df2a8d8bcf5c19adb41a653d9fefae70ec6a0b539a30012f37112574d5795492363fc4ef1e0b65677a37904216881510904d79b77deeddea2cae8e44de48138c1b8c9284cb17d1384f1813a9effaecf1751bdebfabc320098602a2711df36e243c55c308054e0fc588fd3d73062247a623c2ffef10ba258a6914d5b90ff85e37d3ac5b69243a8b20bbed5f999d1c7ac6694e57f5348a7dcddb0ed4e99e20cb8505c5e1b2aac58934419682091cd41ff33bdfe54d7711150c095f896241f06e4cf7334f2530756c6de23d47dc6edfb6d3e53c3b17ea1c726e565ceb0842d1ea9cc3da68ef8c1fe009ec3cbd9027de2cb7208834315a73264fe66f496923cd49cd5701ce15ccd7afda491f89682aedc6428e820426c6d494e8d82c75b987ce12983d4503c88e78520dfe2a48b80ac5c3f5385ffb587558565ceae2914b07da8b47cfc52511165879da8bbf1a52ddc9b4a0e10013edf8af86e8a9abb1fd8fdb6de4a156db9d4e903e1fb54cd8ca1f685ef430d1564ebce015916b05d13019e4c6e114e81f045caac373bb677596993a0315f52a9064393c53fef4aabb6d5997fc12f89f102ee1c86ecce6c854faa4d7caf6f7127be9055b23317928ae515bc4b70a31c16449af7688abbda4f9b742213d74921c9bea2dc6e9b21fead0e17eb523bacfe31ddeec3e2c0ca70dc44ed5b38ff1c16da65e39f176a54d169664b061a2a597f295db9a74668b3f1e781f240655d537896a71597abf95c9f126650aa8fc2b6398b2c8522fd586f1d310485421f096a57d887645a3790bc18429dfebe95da6802918f08661e9a1c107c5935d1956f4ea486986727c28f34c3a152ee0a86b213d0158d34945da8722c2ad9f203049d54777fa2bdd62a0ee226fca26cd672f19b34e509953a80fc9301aedb4163877153c82a816353fa084e15f1918d2538cc590a8095aefda38a3098d984de0d8e1669b425e550dbc6c81cf641554c65e265bb28165a66aaac9579f0230134ead33c09f34e5157358cb8da02749aef1ce952243f59dde320d8049566edfbf2618a800e4a590f8a4b7b65275e38e3a4a30d9810f98bd0266dc930c981269bc2e7569e58f1ff0cfc98b9ccfb342473818c95175a25bf898278cbe2ed80f19d2c8ea06713f4f28c8b223207d225ca7c354ea4cd1bc3c57fe6d5ae00d45e7cc06dc4d8e724f1aa7acf5be68b79c381509e79600b44d879d99f1b80eb7443ce25d733f6d344b696cec12d9bbb0dcd44d4482c8b801270ed024f0219bdf8d1f56c79a72b7c4dac3a13b637df9bbc5393f3e3514e58b233d757f1cf67492efa7ef253d9aa3c0bdafb5f2964f0e9746152acd1b4ca8b9fbb49463272a7fb09747528c9c457d073b2eb91bdd90ed37c3e6a2b9fd5cda77747479fe1a2e08f90d6edbb84ba4e0329916eb53d9f67feb63b9e705032c0796175e788d0e3698de515762cef10628f6bbc9bcfdc0ee467713cc0e436c42add781809019cfe55a37a109cdace166a737352542469a9789aa3a1159d175c761000a2ecb3f7d061007b75ee6d6a0cb7733c380cd90aab65c413d9104972654685de035e79b70996b579152bd073e2772723ba71f1ea079573fb2c75ae95f024d5fb5db3e02d7eac80cdd8c7bf61beaf57699a77d2c63b3e6f537696719fd044d39e354106e93ccf3df52e2f86c98629f34b37c501dc73b5f8fa7fb2067e03043dd42059e3cda717e2d2aab440ac05f75f09680285e97e112563052717c5e2964e11b0435497292737b2e19e1c96b7c3de3e04216ae02327b24e512390f931e51ad4521db58a7e891a589e8c0164da8140531651ff0bcff0f146e63b70c67e57275e2d359af528604dc1013134ae6d4b4b398dcb608227cd3f304195b01c01061a361c5160a7b2d9f39ca2f6cbfc0c0ddb731d50d1a27df4043f973d59e4e636289b9e789f8edbcb7962d0b897b136046703619bc4465adecee01e1025a39fe4215657ce9e16ba2cbb600ace834a7791eb06d244c541007702db25d545fca55b14eb43af2ddbee1733337c9a204a90202321ca6527cae6a32b80164fb4c3da8f56647f14077ee1cf84d6b6fbaef85a8df808023323ee0335fdf368245cc2de85d7d51c4298ab83bc67f672f8753e1a055a1efb69e048c523ce022401223892b1f898ac01b0e3159b8064bf51759c21dcde2b6ad3cb413d54760ada57e7fa1817b1710b90a8090e7835ff237669c3ef3da61235d184a34e15e2184e78eefd7f7793118f2b4d363a2305d3b19348a386ea59559d8192ce2b0d9064f04b23ba8c8280649eefbfb3469a1fc965db3e898371623581f5c7cfa63c117c2eefecda6054d77bd0904d3ae0c2ad0aa2b9e9e808becf13e240e979254dd8f65e5a4e8b5a25e6591b5be0c87ed3444f24865ca5c6d87a5ad504cf1d125e3275f60f5c0191d3ebbe8608fca98a73daef9c2a166b755e21191a79b3c16a5c5dd434f868752d9e7be6abadbe9db2af965e57998ec8f4724e4979b7851bc96f25324ffa39739b2135fad8b6b60dace24b81d0c15034dcf983171ea089d05b0c8b81b3c6937f1ecac81ae50569f4f08cbcf173686a799e76c81183180706f9600b5c2d83544a339757806aa22511af628da508d56242447506273161f19384e6fd199f29d2332c6ee0be112eb7197c7af5093a3a361cb5581f2e50a3461296cad45bb7089fe28b0c47750a991c15a9c64189ae9582cef11271247e78a9426b97d77c479ae37352cc3dfb49ce322c2a6828c7faf53e607d8ea32d29b259ce35d54cda169a78537a2b1ca49fcf20bfd2d8075403e0b88b21999d198bd9a108f2e3fe6d1531964962c5fd6a3be0b2173b96c658e0a37ca63cf461203ee7e7d064c1279a2679b62a08553f3405647a39fa9ac86cfb38819fa6e3ba5b5d3b72c203aac5f85960d8759e5998d63a12ddfd39dcc92347e42200848191451e75f34cc6a7a062d4157187b36b0faa6ec45d764842f5cea9fd4e46c9493609db9084fdb379f87391f975aa032ea1dcf000cba3c53d1eebf0f55ca8714fac2c2ddbbebbadf3f24fdca4b5eb3761bdcc3ae80bbc282945a7be155facbd601e4c3f4cac2eb852ee83ef28039cf940621320efdb954cf469d41e07b626f7c99d05e4bccb98fbe985d782e3ee2b78efba7896d7bbbc6e6d72dd5ae0057e80b0f85a019365e7c0eaf785fdc0c728aa9dfffa7fd0bd9e756166d04769835eac2f1a6a562562c41911be91799fe444096fd1dfbbf7ed5234a7d36ae3a038798aa074228c4c69b38dbb71ced2dfdd24193a9ea91a1198fb3e79e6cd69c11401f1cbc95615cb1acaf3dd288f222587f6144747a427868740676a38bcd990a2e2165e942260f60586f278563646b652f8ef02827daa4f63849280dee3685742fa201af71042ae19fe3525986190c780bbd92506fd804c49560fcff1fc94406fd4dd4da702f3e7db7a5dc5a7c6d636c1b88cfe11ef097d18c77e4cec1da493643bcdfaac1dbfd041817f3a00dc7aca8eafd4d927889ac6fbe41b7b102cb85b80ff22834a095d9e8c5b838fd2a6116ece84a770115d07c41a871499cb98e226815e6bea622fb923ce36c9b0912300a7784dc365bfff62a150a2aab9b612c756a844d90d87545a551d46db59f6617c58d20b6902017182658c22d54995b9388b99d60a04b64b0aaa5c2024a447d663dcdb817f360b87d64ab29e2c6f314c955fd2dc5842b120ad0fdba330c50ff0854c01d697abdf9678c9de3ec3518ecea2a41907753d4f0556e06498848aec23e8dd0ba843a74fdb5899466d750c086c27613ee12cdea71a3331c6a7fec126c8bda75d14e1e6e50c67e8d103b23dac00ad813a59abf4c51bf8039cb1b390f1ce9331b18322c00d21b022a877c4d74bb6909fcfdfdec25947779bb03df766b189f1e353100253954e9f9c8922c6a9324b0d3f65c9fda79017922eb14693ea2f395661afe8103496896f84c8de7cf89968b2ff52279db6930e7e57d328d5a3d145c9275ff5cd1f6be778be86cb82c5bee42ee543f018d1386ab573d5bd3727e1cc8b4d0f6f44afbbf72d452c0b94bc3f377504f5991c51783d36de8f1ad1540a7c8fc0f41668a1e26b8db1a5a21c06201279fb841f51b0d86539728debd3c94d17f19d8391656cc33c0855aa6f0328b4e15357887604f1f1408f74ed316f8036cff7bbb53424f8b06531ba1f44c82bf135d6a1eccce480bd4da8175e7c02bb17832bba8faf6c6c9a142ea86ef2d29e6f08b6413dfce723871044a219f20a60ec1eb61f3f61b316168bcca9f2eb2f6e3830ca7c0eaf93026825d82fcf7ca585eb77664a3c34d4a1c2b36ac754fe6d135ea8cfec948c810b256c3c9c079fb43b1da1d7a938634b44c321c678943d2ea91c77484703aa7ab7a42654e58a9e7f11902fa2a165f3231d33bd20246d1e729bd25cb8d64b21a9e2de0fa3e72665c911859ee40becb9e720166b3eb591c806f1a3d6f3dece1bf222000655f9772ec7fbcdaf485c2ec3eaf24b2c3b93d3cf9224bea69690de8ed8ab7a4a40e7961d49a0aee955fdd848eea5b77213558af84572f3ea4c5f76f4fb299a6a467a6ac2bb43c7ad7e30a096b3dc54abb700f966fb19ed09a1a4a46de8119c4e57c59847d15a1cb610626fd7a2896fba313238b4d922ddf6fddd821c699b2864ae52fc9697b6276273b797c3f63f30591e7333b3a947c4a2b18aa93a5beac33650a4f8c240ec2820107025d904267257613be033aad44974bbf460294889ce1db05802e9e40ef4e7093dac19caf760dbd1fefe6efb18f4c3991fef1ae1b85d47aca2de8bb0012821ae4b40d61e28b05f74e16952c07541475a7bfb3a9c7a4d6a1049812631fbe353040d55cedcfd5cfdf5449c9e7d4668c1f73329384bea604998facb3fd4fc40f6b1b1e00c86f5b49fa126cdfcd8b3711a92d57e604e51e45b9fd2bb0fdfbc7412c3af437e817eac06258909c0b5b44fe18da0397f012c3d5c3985ee88017c6aac658658e67fb6de4ff2284f37d533bb434796c486d88ae91182346da3b357ff122cb4cdfc2413ef2a810ce630d880bdffae750262dc37bf26bc742290098fb40b827ad12791573a6377ff4838d78f274d8768208b6a43f64bf494a7bbca2abaf6fdb563cb5faae45bc0bc8033af038bb04f72af85e4f2d4567c204ab0fa7a77dd80e620cb56c4f9f29dc9a84e46554951fe6a8548440de71890baf923162a1755e79932192b6875e0d8135bb01a4898787047b180a566b63cc4d4422102b87430f046de296d537388d49fc95faee72443b8911e5144981c00d08cf256e0d45b217cf109c0de0f686d9f500ece13dfc3801e02137387804f72af80a4fca6b4805fca117f17a2a0126bddc4e28b4feb3d656e92fe573eb858673dbf76bded6ec4228755892a5ffce5ade1a3b21c6474131b9e1708519590485cbacbece649e13ad6ae5617f9e1edba948cb01e4eca1e9dc236d37eb8969fcc23cde31ea10a4e18295528c6e7c2f8f947b28e1e47d31d2834eeccb11ceb79be4a2b75b060dbf789517907586c3da22a35effc0ffd43bfe14176f705eb594b8c399ae436175f083732d1e4b71e1baf4c074a6ed92bd4c615b6b75a6be5726d7037029fe6119f6e391f24584d11e982edd5d51af5abaf52f1e1c7edcad759dca35ff355ff9c2ab18a4fe368f54f4d2c610ed9ade65fb7e4364a83ab4ef619f47c691d7a7a701c5084bd29f6b37fdfa0e9e051e1c1523a82f2068731f70b1204521dd461a4b25f5036bfd09a0883ef8dee6a07943b367bf9f4e805999ebea715120e808af595d1486fb7d0c173316c377c5d60f793ecac21347467acfc6f97af7b4000063392dd2c79f1d578a855f2f6f986b0e5a8283d26f6a74ba6c63ab3954b6fcebfac722f8d7b899542875809c87d69ac1ea78004d3ecd01b6c46212c6bf44738e13923edfd7cd5d2abfa2a85dd5170164150609665a9286aeebe10c3a53f5edb3163ff2e60c1faaae1c1331c5ad929e2bfd9213024fb0454df06c2c9316c65d5803ae7112a10ca68a561f228ab17e92142977ad3ead039df9be00ddec670632939c242214471d779f18b70648ed9420ced1066dc822ecbb2584039a252bf2f504fa37721c71f67ef26bddbe35a970cd1be8c3c529a92012ce58a5685ea7f052530c826edb83957d47494fce5f20a49832808599ed6b68a6e59032e2758c851e14e0cccc50a0310e11715c6cc944ec73a57805f02e259fa7e25ff10d1b6924c023a92bd76a95f0f01f3b66fbf48eb989f600fdba8ce4ab07999ae8ea45c611d78c98962d5ce3abc14f806fcb1cfe5d7edf99a83f5ca164fab2ca8fcbbef9047bfedc9334592c139bbb8782b63c53ea76f9503d489f2e4c0769531cddfa24e4d2a65c5a7c9e6065800bc5e387351117d03fa4c1b9e83de30f0900e0be3f1a56299977f11cfbbf5ef311d44bce4c5fc59824237f61b67a039dece3544d8f74755513a34b14b048b0e58d0d5637486538bc7081a82b5c46753a132d788dfe4a4109af9bb805991e187cb05f098f96459bdb72836fabbc2f317cb7e8140e8b53e6a304071c5d615943f8cd99f1762f3af73ac90b9b27305c2bdd630f68778036b576467af6282792af675c250dcff9e2ee6cb9472c4be2869d5aefe244a201fa36c6a7f184158ce1c6f775283f69773e5a531b33d99a2621e59bd43572f9bdef239209452580c03f36c1b10eeb4ec8512f99a34f687c897db80ae38e141f06bd114a1795cdb91bc53fd6f3a968e680d8d50ad6aa20fdc491fa67ad87f3f8090524267a267c5d205f17994d5194212380f95f6aac9e9fb64944f54c7c54e4bdd574cd33ec457a053fdc1dae47435feaea02e132e4276e3e66d2b49e123a3d86981300981af0b29bab52a749928204625fe1fb85c3e1bbfc86522b6a2f45c9ac8fb170262c0ad057a3faeb86e993653cd4ee1779070d3190406caf3c5a3d70806de60fd3ea9c4cd0a7f6b9899aaafa565f19a4c423bfc52dc31855ad20f9adaf9e662b2a52c7d2318c11679a1a85d46af29d45c7cea3357976c7b139dc496ee5597409cc4c9c5f9c6051aa121c7963f79c06f62b0681429965509ea034e160fa8d631555daffe418326c79576e7d96a6f6512578a52ea87a601ba3b2383df8b73c71f960f68792d1fb21b1afff2910329571a838430c1356a0ffcae3647becc729d47a1121b3515daa804e82930a6b8234099e19f04204fd3eada78facd6749509e0aaba4c8d797cb82f96ad4846b95463631b282f210428dbaf4abf1b3124a0af66002a53f7a481a1be381db04b07b2720b2155b608f928cdc9fc73b2556a8389d36ff3d10ccd8d20fbe40950b9c5133c2c58c484f64627289ccac9374df4b47c268b2f445f2e8d9ab47160455019ae64b020c5c5968bac80611fd15735b6957d75097be50030833e5d1a3d9e46a3f26fab6453b2815a4a7c70e3ad4d99df298ff272889558245d8f9c2774a39e2052be6e6310d35834f530a7f96cce264a2869c159e065138973c5e247246a527bb60d7939ebf097062f8ec704277c5acaa67710be6f5f1e96a7bd7fcacefb4c1e4bbef74e089124236c2adafff59f2917b59425a4caa61eb2928d0219939d11e5348f81a83827ab2e6994b95da9f857cc387051e07861ee172f15f9fa3759bfa5b87bd39e59f475c8dcf7d555917476141dcf2176e9d7eaf161b2c71006e98608e85fc20b86f1b2a8580c4744544329b4b576666e0cd7b03ce6084c7d29abd6bd9c4bf6718c88a5fee3affc06522b2284b4a613f7e8ce1521e82f3f6881b4b057d26c0e4e0244b839651c97dcf69874777449d5d89fe6a7e32bf89062ed79910942ca9c37a318004a3c0a710b7680bd5fb223b2430ae5b4935e886e6a845f5540444474cf2fef9ebb9fe302899a2c6cd998b104d7a773fa5dcebc6c1203264fbf7b5f47872d39ffdc4eaa70ac2747e9907e07653730473e1225b763e0063db6bf7301131e93b55bda9c83d043177658faabaa151a3da439b453c18225e62ecfe5285a3b0bd4c94ea0b76457b7baa8414b3fd46325570a2539bc8358a61a591fba457f2ed45c41095977dbb030dff877b12964e0752bcfb252c342cef6d21d7f3d930e8a8e16ce33ec7f4cf4f265dc37383c06004d70fc19728f81cc0914fd66879eb0c8b7b90c6e2a2c12b051b4fccea22e9f90698fd9fd68c88d5b0cf5dd0ea410d8f216677bdebed2f3a9cd1b47e5fc7401050163ba4781b61b67b6d1af7f25c13f10878b5e6994c78b19c60cec59b1b2e4d436493f57abb7f10fb2ee9417ea90684046206198dbb8d49eb19be3c5306fac2036a90ec8a561a4f138079f61b417c6155c4072f151b8487462257fa5b8c6cdd311cc4be68e3c1dbc37b0df65aab3e35c22192c90f3648fb42d90dd872f4e4f458216cdf72c85e4a73bd8f24710df5f38c5c39383cc07e88b878ef56b8d1d753365810cb5bece7f61a7786e8f7e47f3d79d4aa233c13199bc40e17584aaaa1d74088a1d18f55c4556b008e59c83137e297c81eb6c50c6bc090a78fb4a6f94fe452ed4dd4caf5fc4916d7b7ca088ffd23963e2dc341030a46c57a0a239402c73022a2560d7402c2f2ed676e760b39012d8b0f070e2c609617e47da7b47308d2641348a03ecb3b7ce9c4ed8fe4d123d0e333a08dba945d3ee5771bc5e32f2b3c3924454ae7eac5a5b920b82e5ce24653365d56626a2456cc72b095507763252a1e102b99f0b71f36e3a8fde6df689056c14a9c8c39ab2a3ca8df60daf807569b5a2123c9e9497a0a9809a478be7cbf83957c4ecafa1a88b8bae92aa7a2a0ff6ef51e66299353f49f42bb615bac4b555ae0116e7b015c924b5a5d43d5a18e0ca59725d1c168d9287f7fb56d0323993ff8bcf8b90ab5105740fc4e2fe805a2b37331329e6ff877a8b2162e747f5abaec0352209d1125a5a41b24cb6754d3af1941fc445c8547cd56ea25ed5f0b0363391acab02c0a3f25e72aefa5d80966dbe13a1b39d6f6164252db95f790f0d807e12b12b2841ce6b0b3517a9bfed14810333a515344bc85adffaac8ffed27a936bb2b25f7215aa820ac729f52dafb5d8ed0bac10f74f665b4dcf40bd4109358c294638fbbb5749f6e3a3b4bcfa19a837544544b6d92a10352a0b708134e2745ee3644d9a2535cc987f72686fccae3bdbe9b73e535dd242a7b3d5ca7f6727724a00fd0789ea328df44f373be6c7dd669c1500631501abd5c12ba1df1397aab07e0769a0a9801ad907e7d45542b2e67495cbe95894d3dd27f1c702694669bf6c53f02cee0020651bfcf957ec5e5b89c4210ddd6c4480006c328a0a3410516e15fd6086a98c9a0d6da7a09af286ec67fdaa71d3b002390cd18b351ecd70d690a8e19f19cd5a582d7fc5bf043c1ba60d6b4d2ebacad301a3b92fccf15a6d36174b2f90f32c307ec8c56a4b1a58eb553b8de293c7b141da05f9059659eef5e59355c88d16b70d9bdbee5665821c7930fe80a57befdd14182055e8374322e785e51facb781a050a0b4649a2d9b49c57e410f84b69b7e356d5d380a93e0fd7b63f0ea558a5ae4429aaeb3c42d61fb0d46108809cd064c9695e31bc976df534b4fe882b131142afe88aa6298f95d9b0dcb9d57c6d031ecf010d83abbcf4f083fd83d16289c46a26f942c95ecf7d32ca99899417f04896b924945fdd6b1d0341362c8f56b371cb1eef253ccffbc72e44eb19e97dea6effff5c61c6d2a783f47979372d4d194f8e14a7d4337d4ca91d8d3c116e0dfb0e1cb80c9a83a00ff28345e80ca29259e8ad6dca588f60385f3c6ab860182541465bf24fd773d04495320428d81fdcdb30022f1de49ede48e655fdaab1c5fcac4ea8d037cb55c7e8361fca7614a61ab3f03a2840a3caa7581b9588280973c492baaec515b240281484a3c41470b9bca2dc2402c2977779dbad7ec832cc438fba18a667623decabbb182718bb737bcda8c9e6af5e2d305ee5afaae5854537378a5a60fe6b4fea71d26ab3f345782536667d169d191fff13aa78bbf449c69ddbb88728eb483742104f4a8cb41b6de6c730ce69dc52cea666a1411973dc749742be679dc454aa6cc4539930e875dcb310d2ba07c932c95eba9d62b1eefdb24419f610194a543cb89053328e5e203b385fe4c13434b5054a55bc5cdbb8939085977fec1f6766af7de3a60ffe1433d204cd4e01d4a62b1fcadf7066f70b1ee292bec2b2a6f63ec4ff113007f14ff5f123f136dd6502b8631251782d5656d5f106687e070dc660d4eaa269da728c2b2a5f8c8e030d658075769dcb8406d674fd107dc27b8964483464698a272939206fd95020698fafa32dc04e40634584b62610b7b59ce0100a01f65e7ecf22f805a67aa30b2582fa49a0907418158992e7445eeefe61c6f716bb5e3f23d13fad41cc9f0c33d67c89e2311ec7e5c34e73ee993bc3ab03ba346674d7e71837746eecc4591f83320d1545d01257e08427d40bc6fe38e9fe192a5552e80fe25d8a43fdb9d70e6840ea4c9c5127a98fa000a2d0298478faf172c80c823ca4e045a335b234830ee1f8d62a794e9b4cabdb4952d98ed5232509f6b36ba338dd1a73780cf645bdb83733bc03dcd6141d773c821038f56e8bb647d4206e631adac6a756eabc7f91048bb2ab4bab500398bde16da25e4b2bb73060eeef41299d4dccee39bd7dc7d853f51c1e430ac55c267641c7887796e5d72d88444e6d2e700402fa5a4727b4fb4fe4645aa2976ed5b5963cb0c6e0143b89297578b66e803dccdac437bfcf36d03673ea3fd4e999821f0103571e3fce7edb60d531480204013ea0785402e8efb22305f0e5bded0bffca160e7b9f3cfd43880e7db0ce2ceecc3710a22f27b0ca3bf0c215971e129271e88f1dd62c4888b8026deab5c25209469ad46f6faabf751015a9d972ffc4a58b39f5161aa8715491bf62fa6d0064640f5d8edb6e7ce7260172a9eb15dac551aa38507a2132aa3c0925b8e6e2c40e72bcbb466be04b861e738e6d2eb36cddc01682dba88060adab81dd06b98abd2a32c9447021e6c53dfb6e5288b53ce519a938c29a7996bfcd7c3b2109700d5223ca1c0bafa4b887a0d215cb9090de8f6cec4f9f9853949d5d769d8c0ad2278642df2c2c4f55ddf1edefbf4fc386efbb3f5d21c311e880de7dda4018e2398b55373de9fc91e0dd68bee7619e6ab69427a084d969d523e0006662dde8010990ec368715367218608ae1aa9d502d1add6545fc310dd206ca6536a1b2c9692ae3d63ae053cbf830e1bf29ad8ca6fed1f640b043b5939c06c0a864df6d520b3881915daf48df06aed53d5c7ec62a6d133dfc2f825d5555d001c84ecaeb5492d807bc2fd3916c8f2012999cdfb5de9b0d7d8a19c7cad5f88566ee6b48f6a5be85e2c85ec0367ab6e2a5f7747dea737663f4643c3678fae9025bbe3e24ae33cd4d54fe6d0d781fc2bf3462fcfaebafbf1ad36c4c4e02e9855842fcd0ac330dca2224231ddb0e5686a7603de98dcc5c5ea70dd362361835f91b34714cdd8f8b47cc94b31efe5aca9edd2a339e9cca1d25d5f82450e7cf1c1cbf011de9f910f5bc2f69353eb46e3f714bbd6367a78a173477344cd3174e1069d9efd6b48022da4dc890bf276610db0c02329d794d65880db01a89e838e600f7fc85c50968de83f1903284171b5a63bb06f25a22e1b63695274e229025ab0cab76fbcf236050a3cf25b59766198c43e16a9e49eb5bc1eb4939772052450d22bd5f88e59a64154c4117e83b0f76c22bb5200184caf8681e72d196491c0ca79da2f216228e7c0d74196381726a2341660d8a8e341dcbff1ac2a285efd7b6307fe2e7fae9e0fdb28fb85561236567493044f5b2148aa80c4328e848d6fd8c37d568b4c2fe121e85d7f28e0016539051ee07f13b97f5c46be687e85b1483cc8e3e8b4a729bcc6e1110a85274f52b650ec27b19de3d7adb04a3149c81e2eef3d79b305d2f8abcb0db4428df06aa2a6706e629b2dd63097f5bee7c445f04b7b96983479064f80afbc1c56197fb985af49ed74d33df8f38e6a757aba4cd84347f9d478222c43eb1669ad71fdfa2ca04bdda712ff74fc86ea062926872d094a6329b0476f1386f84bce65c1a7e9e9904e4e5151539c028152bb534e56be79106e789c2743515146c2acfd60d31862a240876999b5e691954ba169530761d9cbe86e631e9bcc9a485c3987373d6bf834493318f7fdb99efa4d737821acada86ad371652649130c7c86fada3bf2fa4e95afa08d26e322151cd2b361fd1c36569a11b1c204321fe3cf86a4591040399fa62cb6bbc6c8a679811937ca1a6a4bfead80cdf9ebfeb126965768304830c80013bd8b3435a5297759581106fdaf6757569897bf195eff4623a9e0d0fa2df850d2d10257e492a221b39cae02154ca177aef79bbf6fb701725a06f42c92ecd517a1be7367b46e0a683f5b5b286a8c50773e44834711e3061ca699aca86d7cd45ce4634b7cee2162beaabe6fece4674f9641a6d4d5b40c9d2813d88d48c9e75d98c46409855200da6e793133d89d40a65e85e580f46ed70f1fdf4508dcecf5b981543a90106f7512c8a064e58d8fa09ad29a59a47377c817668572a0623223121dc35b9eeff25285acffc86ab780bb7c4fdf3a3752ccde05d89f18106784edc61536c9e1bc21626a35b84cadf2027788a8744a2a07b81aff9a82f3fcd8671ee17743fdb440c2a3928a935a2ae0850e62d999fbcb65ff725481c9b6eb55ff54d2fe622f0a4aca9784e429cf5b23cc62c2b8a0d29455b20e50c93458caa9165e1b96757a25df46b05e328dd78ddb9145bcec866dcc2f532b19e56a26da3a5ad9354a31399e74f37d74895a8c25d23df82fbdbdc92feade78bced853be68f683fc911543e469ed871f988bbb67a595d0bc8ceb5b5850077939eef02cf5607a04888e6d4d4d5e1062c89f1ae6b60750177e84aa1dd982af576ae721af72d2066ccfad5411dc9045dfdb6373ed36f133307b130fba84af127cb8723c2ce5594a0927b7af6f6b52bf30ff3ee4dab165f83d532af6aed24bcf218d44950d4a7a7b065afebe12b8aff05ee80c09a4844f0391a18f304af7267c6827fe6fb5e39c5f7743f91072a0d0cec8731b7f71b6adaa597f6fa831aeb233f2420020b5f096c1cedb130e2edc1aab35e9e8aef94384b475c568748de6e700578fec99bc6adbed69bf8eff400b1c73f94a99bec7e6cdc334430f66708caf577f43191484ce0ec9b0e37fa9475be42aaec0ed32ee0ec71df1a41534a9298ff6905e8e1807b0139a06fa1e35396521284dc0f6f1cf0bb4002aa51b9e7fe51c4cdd6fa8de1aebf862675927101aa9c508a56350d2fd9cd92e4edf2308c2065e4b844a250b62274e78692ec13b430a5f39b7a453d8fedc4c7ded638ed1cda89e8f00fcadc60dc75ad771f9578c2702a3f0e9ed8e6bcdf9fb64869b1ead0ee3fc1bbbf70503dd16e8129fe863053b79c5460447f31136d3bbe589462ab0af6fe32c4306d1f3527f54c27e852b5f6689e03e61cdf8f1398c50dffbb1a880f3b1b59cb66022988a1265ccd81aaf8364e674819be458c5275b43a2854be419361c1ff76f0efbf17a460c23796da5566118ee091d4d8259d6ec7f061731b9c24d53ff1a1c83bf18bba7a1de0e5ce8bd49a12b44e2294d98c35870a61eb46f67c8eb512b9a79f764bc52e8a6eee866817375ab25afa1a9b71a1df082170d880a5affc193be89445611e7bd2ee4af333b0cbb0c3aad1ead0160c8bf3790655ddde31175372806d88797b94663edc32f2e4b69f9cdd896b1c869bfded59d3939bd8e1ec3ce4755c8a904a484e99f0afd623e77a3cfcf59736afeb14ea8b925e94905793f4a8c52c46548c607728f12979f6fff9559e5df2d84aca39d34170523eb087c0420b31c1bfea92f7b295bd22ea303524e4425d86c42462e3467942302f76415ccc91edaaf060db90406f1101e7627bc6320dd39a0c3889882b6e3c6d80a84c3b54067c7d7078305067fec1cc46e0a05bf46249c6d5c3d337146896bb3630d0b4d26fe4704a6dc987486b74b546412c8e470f7ee3a60e5bf3d22e94e92230990d4e9ed9652ebe7630481d89c0e3b0cc7ee835cc759d82579a4086379e4676ae6c611f879efa6dfe8c20dffda747c20e16d85720d5997b54df8900c347e373707c02919bd4da7b527e2a36d19c04c0fddf4d6fc4f9a2f3191bb95b348be0c793f2c72cd6bc21cfec11298691a1a124981b5136b3265e2d49994b0906930f098578a99d5fab7d05608dc68286533e0071f1c938e0a90a5bc915e8faade47a10423fc5f0241505f2acd914a0cf9e09b56ee452bc761ec3a2febbbf7665d0b03ff088c6cef5a3c52a38f9e39688ff9ae278861c2fe7639f738ff9de8279b40882703bffdb4a15221685015c67b71aabb3133dd1111edc554a69202111e48084ad3cf17335f36422efcbb5454441a63aa728b7e39c2be83d68804e781257b0ed88858a9f6f5bb2ed023041edb78e2018e255809d75c7f03cfcdf84e96be7f3866519eb24bd61e08a889877101dbe5b8f03e5fce66d22c091d3d65f1d33e408ef7f335567413489fa554d9c189889cbf3f6e8312a8f5294627a1f057a0c247bc923a61a25fb5b2682b47d5bef0f6a9a65734dd2ffa411ce0d6034072807f5ed94788f29613825a07cab7e821a9f56d1f9910fc30e13a328e72accdff7494d6c5015a01a8e1e2f41f748b9ed02123cf633122804cad79005cc4225f62f59bcda5946cbebd37d3f5070b9b08f92b0960d95b094ca79c0642f9b307c7cb89fda76fd8ca02e69b8913d567335b98901c7aecce6a7a35417899596f7cca9d54d5ef75cca3772baee39c89a8b0a141b8896abee805bdc3be2096938c5cba640202fe5acffed58af609a429792995455c430368bb1771ff5be5c47b824d400cddacc43ee2d3942c700a73e3459e6ee5270e23c15b603cb039b1dd577a494b04251e2e14a72f40f0745e484cf3123300e6e5a253ea7253c0c65eeee248ae7a21512cf0f3e1add19fa3e9b5e463ce84f316007e003c2ddc58597465c36725d5d4b2638c39578770a85839d691053e06ce63a53e44ce313110909d5515b64d6f3eb5ee06e29e371eff19ff1a8f97e88af75ee25ee1545db786677fd877f1405aafe794091e4eb22f916e275adb826016952b2d1458830b60424af4f5d54ed421e30de3e2df7ba267406bc016a4974ddf5d1ee580674d49986426a7cfeead9a21426e2f9b2d1251939937890d6d44e305c32c8dab3a195ba0e3cd4ebc386f853b6c708e5d8ee5c6e3f1ce9744cd416dc48dd94e6e18dc3a15c0928cbf276c6f0719d92a04f0708c417d15b6e6c268f55a6dd639a5f65c60b16bb901612f2a0c42286b9e41da7f9cda1de835de60e8c5f5d924b78f29cf4e30621674621bb66a5bdeb991a1838fa71d5930be87b9741c4c111a9b6929892e0bc0eaec4072b106f792038d95f0c6e4db268dad4b9f4e5f1a30e917f2aa7948eb3b2e41b10ece86be3e44557b566cbc91f7f91d48808d1a75f8af58ab78d431dc6daddf7e3c5b8de6fbef12dd0d7381fd5c8a5cae0d252e71a15de1be366c6ecffc26da59a6861c29ac0b3b9343a7843e40aa401df05511a86373f771710a3a10e73f731d51457de1d235316f6ae609e95167fee25e24686a62692114a4fa4b215abee5f93f06a46c4a7785ba610ce04615fe696ee36152c4cc45f56094f05761fdad390f33629ec19e550279e2e420cb8f1b5bb97ca9c234769ffcb2072d6b7d9162f402646aefbf6d23c906e2de21b0018adcad49a318d3fc39496105ce333020057b351683205a0e3f8e28caa13b46a61b397c93c8e96b3ecf48f4ff6b63317a2d407b7800b7af32eb8ff66c56e098b7049856140bf22369a858c6f72d0746dbe84e7edbaa253307bfb2c6cfb3181240fc205acea13af5bc1edba6a1b951e4bfe154ccd58ca9e6f5e176c9d359d58140e16d2cec0a100aa305d4fbf96633886acbd6050bea34a05a265593489e3547854b4079d5a3982a624e258e1c2120d853525fc0cf56fb52f4967316cba3bdeb2c7441add65d2e3e02e9a798b2306f8774fc94065197f7a696e82314ab7c3f98a42debdc192c1d63cd3a946257402e5619e446f30c4f72f7d6eddd3fd2b52148aff4c6c54a0da3b447fa877e19d233ef572c3993d2768c1e8f58b4615f109ec9c5afb4d18443e5eeea2048a4248a179c4cb8da691a102bee4a400167ddcf30da86d673a4d7cd3c24b7a1ed9fdcea0931bc1c1be1d6557829fb24b143388f4a2ba07fca167a042f5fc566f0cc59ae70dd4ac97961d7f71ae859350ba230f8f8986da9b3cbb836350ebc0430700edb5f4ab5c8c34545a2aebb6a9d405aca8c7f778703066b1e6f6324ec10bb6948ee5e11f8c774d027c3a5e35a9203264472cc23b63dc6f4a54ce2cae7cd73b3c75d1be30fb90b7ff3e99d79e2b90810d9b7b7cf86c78e3e45e809fc4404d0716d6f635f1ba46857a656ce785420f3748554a9cd5d8bf8c2ead33ce8dec51da8bc5632c9a81eabc336c895f68ef8970dba1207347ce1854c1633cb00e4b8d477821e704c2c18c709834194ad33a6b2bb4da69ae06f3b8ea5245c66b69c4a13b7483350ce7be497045159c656d7d2687e61aa53d4369a12eef5f9e35b38879ec27a623fcd254a52e1201305e32e8c13260023a40908f4661b30e388477f67d51fdbeb4b1e7709c9cc41af140fe79d0c1ae8fc7c016e13c8f4f04489d9be08c1f95e5946c84c7f07186bcb6fcc0201d4f114c8cce5a6bebb397ff792f6c505f899d9423187d225537cf26ebe1abd48e6ae0ad475ea662257fbdbbf8af16662c75b3ea017c288abaf4e1d4d6fa0dfafb28dcbf14cbcda72e20c1f843fce1f0e3a6c52452823e2d4dcf66c3ec6d17534b5a0349d425bd277998c57f19ed93a20606e83ed37b04c08093a70ebdf8c011d0b139fd844ae5a0799312b6834ef294df573e42c44cf8ea8a37b04ee9f68c5bf3db85ccafd3a4e229f7b0724a898af911977f59f0220afabc35038989d695cadf885399fd23b54d0ba76b7d180501ad073ba6cfd56b958ff75965d521ae981fd5b6610ffc4a1784ef7e0b7551be221b6b4ce83de51a0078e6fca4c1e00a53b760c4bb09a7d47df3ff296cb0bc2adc8d9472852f7607a37c7aaa50adb88947c42feede43af940c86a82f49105d85d260f6a8f4fc82884e7e35cb27d618a1375dfe1050911d1b8c1d9c864fb79e5bd169bf7f1a4be4ba1a51d1ac51e5c01d28de2f5bc8d10e440c89ca1342b0f55cb2f1db48db6a31eb64ecaa3d5faf334ffffed197ade39199e3cf0f202e6918ca38aaa2688e79136eafc5f8c4006ce27b04210450686307ec81693cb8b1ef0c6a44611b5def5155be9186e121c80c255cecd1e9eb1be6b25480642c5b6096f903fe6dcccb3eb590d253828949d0ff34abe24d9af6c18464a66928e2c6f6ab092c57b58d98d195f2f92c5c058239751e536a68da22a53cb5d9c37d555dc30f98890a807478c63181e153a5d103aa945886592631058384fed5308aa31ba1672b044bc749b97d067106d59ff3da5e739fa5f8a254bbcc1bcecd6ff39257aef3ca7a0db9ee5bb569d44de61cf7d42383d7a8c282de31133a64d3a8c66ef5b89a2b396496321ba9c3d3bc9e3743c43be88620d1d147e1e8d2a66a32fc7649389c521c7fce497ec938623f4d30393ba34168da5e914b69bcf06c12f45ed897175234252d68e607206dc6292e2ef3e44b32e6339e8ee2a35be546878b640a5b7f8be87dbc0dd72866d2b2ecabafa15af4b30e3adb768a1fa9c8a6963b63ab365c61b96862987ef172bff14a758215201cbf0116d2da3a8455347842096ea9d65e98ed6380e1a59baa7294aa385a91696cb0729335fb2f69762709161c3ab35f8888fc03abbcb835c6382578e0449606a931ce8e2d6abdccfefb8a05188bced5a59f630baa4a2a1cd6f64505e712cb856e089e5cb7865f62a6f21253a83aad5a0aaf5ac7ff3a50d1ee9b1252de09a14dea83bc070d4cc987e4d55cf4338780aaee68e56093883391b12bc15763fabc03399aae3d7470c3436eb221c64727469586421ff6e79ae32db30bc503ee572e1c18e75992e61b906a4b8dcbf5c0757953d1937206844dfa164f7625e7e914c70b087f59b6169938a91fafecb7c08f2d958f8016d1167e91d32cd133c24b629e2de118df19e127ab970cf47feef9680ef1f55b30234964d11f86d2f54fcea04dde3f715424a1f5d83a1c497ae7ac14df1557901644b62bc5b92a56c66a64c9c4d8a76e7936972bf3589dcb1b762d9a069ca83f5d8e115b53c3770295b5ecc135597ae83c99cddd2bbaee18868b40277a25682f5034e39fb4afc17e9632dcfca0b38c29b0db0f406a2ea48546187c6e55afb8578896f0d8bd4dbab8534cb4d78d81e5bc49904efade8b6153b6eccec52024a969263d3a1df408ab4040308f37b681ec0705daa90d01b0077ae56cbe2c5d8f87b272779e86764173ce63da6a158a8d16f1a66394ad161e68875008894364c9020d800847a2ea49d92a50b1cd343d98d8e5cd607061a7b01d58f3b340f933dd8605bb2ab5b110fa44d7e4320e12042a7bb3cc168101922131c9e5c57ed3527f851643d2c663c1cf6a3ae184745ea7794d024b52dad0bcccf55aa0e527e504b38fa19bf86d10839d3b792584c901958a5e6e19cddd44ac16e363717a04964b6f522d188e7ed7073cbd3db4379e76e8d6ffee147594f759ef644cf5fcf254941c47bfbc9d4078515a0345d6914dfbe65850d015c77ae95195710ba79a1e4bc64cf414d13146e78ad7ac99801d6e1e2cdc805d18720d91866de1573ad42eb72c59a49a8bebb5d8d63d197e0317c19a2021565b1a53b7ab3068fe038fd866c966a1045dbfa05c4fba1aec1ea1b9c7c5545187983d51122e51063d21ffec37115a37ff5c9616b9760cbd6f01d5838f9d4dd753120715678b082c8c6ff3068496f9a3daf011e00f0bfc7c234a86938db774abcea5e70d7e0bd9fb4e4a780826720155ab9756c8028e83baebcc81e98b23252a4b295ee11680a63eb0860e99ae4a4d0894af240deab897d65104003af15c6aebefddd18b4a44c036d6b498e4298c578f2561373edd2ae07e253151a7e408e63dd77ac83289ad97c258b8de23888fd2da614fe699362831935a5ce5b8e490317f7308e8d9a8a6933687af621970fa19cd26a52ff7bdbbe6ac6a37465e15bbc6946efa416a418da9f14b409741679ab9de9bff57069a4b20240b29206b88ece108c1e87fc3e58f2818fa8eda8599b937caaacbfb7b79d6f782b414862c689990bc88cda9c731ec9ec89dcf973e67acf24a84cfd356fcc25bb82939805ac767224356d05df6f5ef6c9849e238402a5e05abeb06ccfad115069f8b54d5b484a6cdeffaa1f938288c3b60d1c49accf545dc3fe743c8879433679460c90479f924f0dc2b603fc1bd87786ea1ea6a9ebaf1f9218dc6487632e4856f5fecc09a39f8f068dbe106574aa0ff18483cdee1f74a2e680048db15de273579b55871344ce1e8fc4b78f98a82883eef725a795ee24379ba74194a62806f5a4a1460f6b44c0cb833e9e758a33407a4f39d793b8e40d347936b5d9daab3a35e9dfb1fa5fbcde13a7f82a39b0509bff2c0d56f1268cbc4b3b898ae4a226fa8417d4be1706bd1412409463dc9cd1c5e8b880343bd3733e602c4670544688db7054178a236072c71d025f519a4ce38b239f976e06ab34b263eaff2850ba06a1d6ece7a8260e74178a8ec79e46cb5f11ccd98187f9edc2854f04bc2f71b2c0eba65cd552082429e8f188ffbcda0e19f843a298418d5c2d890cd5e3fae0d482f69e50b540085433757f3e7971c9d54eff7e5d0ce84bcc6a21059db78d51d7434ed443aa097c645c1c4608e3df3727ebaf555d3e8e3ebcbfe11c61918075d090a03e1b494440555513267d04585a6be6b3ae68ed5e518abecbf6f0523c8c000f9e20f95e222e2314e99cbdb23085698e59e27a6925c05e991110d7ff26bc06e345b5cb5a4de329fd8f09cda7bd11d71b3d38d4c04560a869a3862536d53fd376846049e1168254027919c2f0647492478595be2a591a6e39632f6d6a0d6a36957fd819a059cd6c9bc27b1d59d6c9133a93957d7f57824886860962b9d884219c8f5b8c6855b927cfb69a82f506da504a167a62eb40713b88c8985f99b51f6061f4c16b08f9aa5c018fc2b65f583e15fb37b399e2f08f61ac5069bf0e857ae4ff8ee2b176b0b35349710a7363e7ee4499578ea78b0b23b38db81d7154167db39a571075e389094d7fa08cb0a4e575caa8b16a4c2f5fd7c11bae5d8e26f6bfd63007a74f3ae48016d75bf4e14db4c37f35e2df0d906bcfbaacce103d065e9738964606285a46b64657b69d9caa1a257a3330f1813155db232aeec4b59e5fd899bf325b28ad833473a5dfbaae02a8834b5e23cee8db35cadd6df731aaca38067ef11ef5a76826f91c22c50a19879061d9ba6a0a4d716bbe3d8e20a67d6fddd5c8670b6b37829267307777dd364403857624e8d389f4e00f7805d0a2c4bdfaf61a2c57e2b003a843089a069965c9a54744d1e344c86f2d98e7d96128092c8ef6788f973e667ba99a19a10eb1c0f2d6240c3064b35906866971274d5fee1c5ed201bc4f2a59a18e253d1a33d325a63915cf23aded2d2647524c93d763908de66fd6427fe29559c8cf4139ec0f3d8da3efaa838a78d77c2d542ff50e36b9c4f74b0ab0bb64f387a74a5985dd6ab7c5bfbd1783b1b91b06bad2e41a977a9aab63e8c844120a0cdb1a5a3cd7d533702b349c249b839ad9581ecd304b5d4755d03cad604e8c8521469880509a163e2506f2e25b4686dccf18c0a0b17a1668f0705a24bd4ed0e7eb0dce2801906fc2ccb094ed28e991aea78aacf7f45d9251c422213ca05753e92be031ee37aa485ce5534589278db5d50193dfe191999070cde425b98dc1d387c6590a674827782c5ec337c666f7b6f3fcb780f60489d053d5538a1bf4cc10768a4147bd4d051b00df74d3afe76ad5404b1b59bd4ac2b1dba8830cfa39c6dd990f75e5620479932a1b01a6bcd3eba3915305e4f6da4a367efe55672e2d944e240e44365028f4185b35ec16df17159952105bef53d0989a3484868ae9aa463dd7a69d53c74bf73bda0c0a99ff8480a5191d9ba78770835b958cd3b299881c5ed539d534587031805bb92f8381608bac0fa0f45bc8e5e9a8f8784ca80489d580bde5c8541c2036d45e46f11fb8caa4cdda57253cd3c622ca849e5bf758b86457da7b87dfee13638d2038736d11263fe606287ac1574cba30863e9481ec37c5d4f4bfa1d91c17ec4a64e6ea78bddbcf580951226da8f941f052b4b31e6b6f7f561955d7ceea0905c559bf9afc647412f9d6552dda227a6078a8de79a7c51dd729bf5524c6350f6cc1edaa9215962d7e7e1c9bf8c5d12b162e0e1e6ea5eabcf9b96bb4ec56002276d3de1804cff9f9e0c942c68cf5df7ea293287c6c5f60347589fb9e9284702ed8a1c42dec3dcf2be867239427ab25dcaaf81b38726f49de1d5633ec99bfe12788c8350ad014f059b623215fa187dec9050f8a823a7463aa11c8c9842146f7426a42c859a5cbeabec114cbafec4558493a7163332d89241f8fd2a23cd83fa860907b193ff7fbcd217fa9078a96be54f3a3be26bfff4d81daee9aa9dbb0a1631e580b46dda5f88c27ee86501a638384884b9de2f29b0f005df8e4f63a4aa11613761a4abdb141cd49c0b0a2106e2dc9b7526b2c85ac50afc9d2abd68008c0766e35f2366e8a68ed09dcc4db9bb6980b68a2c7179da28fa41facceb3cb842e275414bc272b0fe7eb3c65ce62a72d986d2517c7a05f9bfba9fbb7852c6773eed95bd3b1129929277d7e8505f9b19047d69f9c9e124e88091b3d1e2b205f135da5341acce131fc63b09faeab9c91c4a36983c4896f3d698e5e42d4f38c7bd5c3806a8923daadaca411d52fbb262c115550a9b3c3c0e4ccd4fda031592679ef59236f9f44f5420061844b4bd443a7e289a3f0681e3973bcda95230e90e03a6cebec6721509ec04c03561a0f739ae82a4a5fd69a4b569493616dafb50767d36c768a1572ba7849407a4eb6e87ff0e238ace749e4645f4bd2801113bbb78f2d4eca8966875fc8805da9e849b9cd48c04a7e39adaddae7296cb82181c197d21a72310e14f73d77f1f67231f408657c408188c72d377ac365f8af58be18b01bdadce2b5036ce30df0dd6a04f7d81c4583e261fc70512759ecc6bfa98a39e6dc3955500e6309c1a3a6ca0636e7ba053e8bf1f615512a2111fa527dd991d0c3c36d54244e6e4b1c8364d61fa46581537f45b4c6d34583ebbf60203fb114acd6378f2d6cf41ce1c86811d573b7575297478457daea5941afdc41ccf9595cb0035bf0f80b9062cbd7130df33336d4ecc15b349d62c3507bf2fd544b7126582b6f07309026893fadba126066d6a330a2a37f57b6652614b608bedf2663efc0ce8f5d5174e46f3f62bc4dc609cb4075a8b393b442bfe04de259f28b6a276a9184e9663a57043a02d55b6c8e81814f79819198adf47affc19d023461ff4f5daa92259b468980a50e0654a33970db716ea41cd800e6bdfc7a1f3ea090f0ca499c91145992b593600dc49a3a1739ffdb23b0d48be152640ec405a92a69d969fdd1506b5352a20c9f03dba07851d225bcee2af17017f73837b1398d3493806cb152b4fe6cf891d69e0f3194eb319fefc942535904940debcbfee4294f4b93d9121365842033178fff41d94eba32d05ac1a863b5191b91adb9f34ca9ee2d914ceb29ad105b5e3cd8740d00bf23f4be0ea3d04be5785312a27c2c60f7c05bb3f5431cec2fc28e714db95340b83ba21b86cb54bb83cdcb51291cbe006caa722c09e041cfb4ab33be6c9d5f2b0d050ffffb89dcc0a30e05e621918ad35c77c3ba49745361540b5dccca4073d6514766205bfe3d4805ac9e7fb33d916a5a03c92800ec79f015de1e159c2358738afd2d51d862c4bb7f4c382075fc3da4e7d714bc2d9a21070963654f478348f712d5c19d9e706aed4f9d82bd1fa4857a62b3927fd9776e3b229346c73ea91f5e53ee602e9b812c99fc1fc1dbd343311ec0c45ef9226dba77283308925822048926b465fad6942588bc51effcbec69fe5fbe497c1caa65a6713ac8737eb761f35c1ba9a86d50c13f4621f255af5e2877e185b73d12309c411533c9caddf932583f65841eb2372081c5a2a082143ce4ca8697aa0b49f3ba3b0822c663a3bc45983d3d6f15632edfbda729b7cf671d6914c2358345ed9f075e3eb52c260d17e1e80aa091d141fd2402714e168123840320bda144f2fd5fb5dc659661919ec7531eec6863400beeea6f195ab7e5ba86f385477bd1b0eb76360996db99d7ba5c95ac54a8f6f44544cf470402a4b5eedb89c8d4b464b8152056a21fe1914685acd61b3c9a7cb0b423671f53b452843bad0a0aeedf860722d6be7a5c591879203ab203be8e80ecbe1581aa222c13668451f4fa8365021f11286baa89a72dd36dc562ec0630d431478c2a5ece0942d342dc9095f8f7b9ddb6a7b7030d9ea22cf6c0d0fcb3f76b63604f70b496a02597818931c4cb2d1ba2e540ade6507cdd179ebea667ee22c042db2d272c200e5b8ba8ae0c621e194a3e602d69dbc96775f1d15a3030f6da0efc4cabf6e490e19e792abc200d6f84f1b140f262b86f231d9412f893aa48978bd1b2a59e82b13ccce147f616d04bd1bd723681b4a306290cf88c99c156d76af2dfc7bf80eb65ea11e75e3161da997b5066cbf6ec91bb4b00efaeca34fc98ea4aad08e7e9365afb9ce0f6b1c652b5608330147527fb44571fba936cf3741171f8fa42991cac315c6882f36dd702847f0a01e4cb41775d94a3d0d939450ef73aed41addd266722cf60cb95ec26211b249c1cbfd73479d1cfef4008ff91ead38988bf016869e1720160cc2bd785bc3cac830336f76c858e503249b889e3ac97509571f5e794a6b7eaa5e913520bcb9b48688ddcdeeedaaa5bf77df5e6544c6ead0c9c87b8e3ddf65de0b247d0280521bc8d0df0f6bddd9305c4f0406c831b68119e4ffa1e5c6a4de66af33f4348f29cb57e2ff4425f807b6883d56e71c65cfbd94fa51a0630ca2c46ff00625bc27ec4e5f593fdb22c0a3498d5448e26209494c757fdad86a005e53bc89933aa426555f354eaca1496950a48ae982581e21f921b29f0e1a5418205d4fa933b9644b8375c492c383d66346463e00af3f4511640a039811258d90e6e56cdc1cc9f9b83aa7ae39fa875c5784ae4d1f654c9641b05f0dd60eb2455034191a8a86cc0b2c3043a164f35f41c699d2785eb45d2b36a0977f41a6a94db1aee43bcbdcaa357a51fbd54b239aeb54103fdb527a73436c8fdd048c2c05dccd563f7a0fbf29ccaf798e0fa4d0c2bdb2168af7658b4fda5a708753ec7d24aa1754f2411c7b93dfea28a75a42eb844f5af9a28c1df57fe32de48ae9569e752f0a2e8e356bd965badbff3cbee8376823e09998a2d52b6c9400db3c22351807afdd5204db11cace1e1d866d2677a013355dbca7fca4529f002e47131cc9e18837c05fde92558891ce10bda432fd035f12c221587f9aa0a16a0d1be446e736450fd8d7b59e75b0b08faf07ba8d7b312732d10a63ed232ef3f05f2a95fa99e9ec6368432c1836d42902016d5b45c168579ed50e29fdfd523b2f6d43d6b07e6a9d9cca3246fbcc90682fe2e3b1d03f4a0132e68389616d07b0d2259e8e093174b826c9036a218ebff2708b607b3bdf0c70ad6271cb96a2c54a5735fb7eb687e064bd13a6d8b2d5bf6c7bc37d3f35bc1e2d525e110e68c35702616d78834adab33235d25f72902c03537d0ac880bb5966831b7a0e181270217f5b4a79b80c4350b8366413862c3c9e02b3896c42d2f9ac727c174680aba51b0de81e95ff855ed0d07a550e368d4c45a8a307edd7ec609205e890bb2fce7bc0f82c38dcbbdeddff0a1e0a9bc1eac518ea281009fa10e81e48b041d516d87bc2a2c615a0edfbb27d82101fe3185332646b5102e2533163328e700c78311d170a1c155f283f27f5ac297779e96c430af8c75fe77668051d0344ff7b08220796fe5562383ef528f58ebd30e851b1aae02ecee8a8bd80d8de210842d5a2fb4342a980775b423d4bac7926d2fef994803d3af0e44f6b8da8a968d28b1c63d35dc48df8720cdb3902e7c7ecc09101e68f554b3387e37fe40a985f8aaee1e04ec50e5642d469933bb957a6dc1a4c335a8919ef443c7421e4a901493754cfa0303f8de499f715673d25128fe637ce3080083e3c0bd5bca1edcdeb5ee4b5c5f4d4643915694700916fc248eb1c8927665ec189c07e1e8afcf249a3f4e8c07c76ce7cd8a6783440a0cefae76e9687225b0fcdc349a86657e963044157e56bda284d23a92ca6705369e50b002cf6003a0145382adc1998054628631c52512c71291df3e8f2df4418f23f5c0121d8a63b614f37278b44126ed72bc5d63a380bc57fc828b3086c5f4d559462f5d7c4e507094af84fa41bf32d40fbb37c9123332dbbfb3d462e467c630952ba4d6193af742e780f8b2d80207a69c26351474a3c997ad1f1963fd17e6486f049cb2a8b3eb77380664dd6a70c1f1da5694b5cf65ef3371def89dd30b517e3affa8fc3040c57d0b24f6f7d2691f6397954eb8900f578d2fb43db3f1b61509853b6040836aad84d3a8e8995c1e67b439ca589ac89641047d41c153d32bd39b2fc83b613e90c005ebbcb4a5f1102c2208a372dfc15a72161b27567f22f393dd0769b8f81603446bcb9a73592cec8cb9e102cb3c5b87f6bb01acfb8d1cc54308c4596b75dad320554d909bb5deb896686fc190fd250eff815a60fd1cdb8c23f4bf096d54f4c688f24868495794d9290f4a160cff63b4798b8b7e48725b9f32ac2d128ab3513822168bbe7d3b55c296b96d9a455d7ef4cf3cdebd957cf99bf2b721aa9cd9942ca8b5754041b577a3f0d235e71eb6b1cc8e7c4972a6e405d1e6d80a9a1e26fef3556c2d34156332fc0d1d7afc52f29281d3cf6850aba8e8fb7542f1e691ecf6c90a587e3c6ba6bc29401e822bc749a186340576fc7be17b8c2216b63e4cb70eba9beb0cae20f0026a76aea4feb49558cea4d0489a6828f2a324a1a532eb1673e628b798839c87ff0d22b99b86706d23f931b2a4e5094ce75be51f11c48ff2724a3ccec0dc9e2233c9b50c3776313d1157f5e6f3423df899ae91a0af9113bea569758fd9b6205f417798ba9bf8bd0a4cb6ac86a407cbb0e0db0241959ca2b71d70bde11e0d67f49f742cc91f4c73c51c2d216e92a0c2e73d2a0525bbefc1eee40fbdf55df196514069f77b7252cb2df49fa1e1e1117f035317845f14392939b21067ed5d68ee64fa0dc12d251371d643bebabc224c1cdaca3a70e51c58a5cc6802baddd7057ebc6d6e50aea41c458965670035e576cb33b4a1108cba6d33d2c4ff1b2a95a9ca8dab3f840683985fc441bd1fc51d2fd965cdbb20fb6e1bf78b3c34b4bb0de53e620424d5d10bfbed229b7b76b304144edae17b7619eae95b82fc968362c2f799dab6e7c4b3238bc0dbc3594d354d5b6ff593191663c35a1c3a4fe34eb5a7bb386cdbd64d3d12a071ad00e10b36b0415a4d4858f5d125cfa6b342aab29c2c41121208fc5a68c13757938665d6b452fd16913715733a4ee2ecf08923ab9f769d089d48f33e0d683e12bcb83744dd3e792f93b5f6232344668d6d229d6c6a46a906c1de774b19b8859550f1e343906a350e1b4cd671ea70d5ab78151edef57a0eabce32577b843097f8c05fdb0f0c229970cc84c510990936b0a09e3af2fd2934375a0e234b2af5215144a9de0c32eacbc0ead46e1298c9c68bc5618e948654a45ce34f0fd55f20f4b5601503c46aaf82935f9deb702be6b1cae2af3c95d25f2cb8866c23c21201e6a8b2c5013831713aefc05cdbe5600ba50fb95ba7e966ff8d4cd7ac6939c6097d7e0ff8b276217d2930a0800da602cc30aff7c12a23993a0d12bbb2db660436362493f2c94407b6346e8e633dc327b24315c2da05c4df45fa5dc3463f4c9a4177c8c6cff89ae0c5d709344726e6f0baf3ab633704a1d6747e7be05fa18969d5ce1265f892973a9cb4e7f44a65a4f5350e807351de8a8ae55aab5f69731645681455015406671bf4179b91837df55817643ae6bcd728420d29135e2f16341a7a41bf900a5e4384b6e7e9e18724d52ab7a879627f49b4b1689b4a7f59df28d516c23d049b6a093390a333b4d7aed26d173e610a83248c08990643acbf0150915901db154c98d3c61212afd141cb14147f82ae423c034493bfaa95e79f09dec7ae0729b2ff1885a2c834dc41f529c009e1edbd5ff0f28bc20f17219ab1b048ca18872d3770b6962bad426732fb23e0484487b6f589667675dc29eb901f78bd06faafb1f869a9420e4c375f36d648e54b5ec088e0e92945c624dcf274555956f1345203c749d36f9619ccbbfcce08844d05e3dc3e12f5bc8a6cbf0f29f8dc599bbcd90757b855aaa8520a82dc07778525edf8ae538fe8f07faf83248844c307f0a623f8f84bf3e6985910e62b732150c84ad15c382890750e7854e5b0c17431239cf3d8e7ae47b62867bacd1739f7b2c24c232e4a1635a75a7b71a6cc4413af5a6547ebcfc4b01d7d96064bc3960a91819895c883945a56ea710c60e80ecabeb4dd10b2822d99157d5d171bb2bb4a8674fffa8f9b37cd1f9ce3970cd6cfee818536f0329cf01c23d63a48917d2f7bc089f14c7aad3434022e0895e2df4b5d5469df324187e09c999790de432cf5ab75bed4c5d9e38a460d528dfca336b32280a34226e61696827c8dcd8fd3c701a4957897155b20eacc4118357dc716b9ee1091315765d9cd33248d9eae8cef5d36e409bbec4063e6360a2fb72c9164634163e77d2b3702f206f82205dc2768fecc31f97cf0feacba72107fbe5d1ff92e8febf0a1285e9eb4afc2bbdb7a458273a2744b4729338a764014d36946b3ab6f357bd845ef9d493bcc16e340354a7479a3729634460e779a69e5b11f292f7b08b6b161c567bad5616444283bcec8a2efa9006263bb4ed4ab029b5855e7997b32a1dcff9490445412ed93faaae72172ccb8c1600f70fefbc75d8c4c632d26b630082073862f075967326e606a681eaa3aade0a64da15b443466a3c34e08d973a0c0c5066de5ff69ddf7cb6c19971811b7c001f2aeb120184d9ca45d10931272d66138b98952340dc23e1772e3cdd4da809232430eb534b00fa704ab59d372ef5d390e1f9439395663f04d757d726b515b104731fbf8794de46aee14c9fa4f21bf58c158529466fb10525d09e8c27090dd1b4623e9842d5accda884b4e0cf3a349024321627e684d14dc953bf3c549e44cb071e180f272e48b17580768f9b3a8222047e95ee1ee605912023bb5831498f35de45e81f523cceecb1132431c0fa10e16f2f94800eb611b88b3635cfa2aea4963c01c92956c579de5e81cc2c35e1b19b17cb42e6fa5a7f673db98dfe54cdc74d50471fd87dacbe6a038b83c3434b63d86f472a7c247c9140243a722d604ee252a45401ea9b5ac5bcf85f4a0d0cee2bc2642bea2b9e9b48f03d1bb918199d8c4ce52c4906be3f983e976a429a7d23faeb61eccbd17fdef21daac7d03ae87b25ffca5c7495c2e134471515225d7fdb9060756835e00ea781587dfdb5061c3b445c1a06a833133a72a888068f0725a7d90ed24269294997ce289d6a1288a10f9eba6b00cbc983aea344f1dba2ab89857221ae18e49cfd9253c8c0881178e6297656a5633f8e1c6bdd87707d504b1e0d086a005f846c757a84693ba64adbc00ce55d16274922ec21c8aa698e4a2cdb5e9230ec758c4a626cf0bf654adbada27850bb5b178a2847bf9834024db9b41edd0aa84fc0870785445031b81fb7cb95a3d843543765705a10bd8cd23501b5e3759a682f0ef8bb62dee3709f08918d641342ad991a986982e9c28d69c8401d2d3ca521b40fa6455733b17e9b2a889b84ac3ae62a41697905751cc628612bc2e1c10786d798ce2014deb93f8147acea45453a4ea63fa77178460cacb6a254e033c39236442439af3a146e351122768a915d5595e1330607b11d68d4001c3e386f881348694d913fa76ada3a267ca4bb733c29d00e0367536ade6b24d0944abbf07651c5e8d90ac9683b6b0e96ebf7aea39480ec2402e17beb326cdc9abb61095833444ae0b75369e68030c1630636d701e1128bca51325f2647ee1dcef03f079e9620315a020c33269afddd7043669e03f53018510c48e2e58f511f56a279ef41e488e0da4b876ee37f6519791952b761af7986b7475b48d87a51c9b50144fee8c5c09d53620e13886b3110c8b8b88d0c7724a7ef45df797b26f90f86efa69d39708c6794e808510df9ccbe6834eeff9686ecb6a2192b33ca18404d1b3449ab9bb29333c8cfb915a7c5dfbf4a0291ea7d711606895a1eda3b2eb9179beeb29f125c0d13f5d1333277c850fdfd564ba4f99d6d6c74040e480d551cf195a1b76c9cf1139d571896b5c4818313c0a751f54f579c49083f8b5bb18e7df5d31c0b197afeb2826862459b712cb405d6537727d28a6818f5501f0f73e82c3cf390ae686aba43f7e00b975f387e3f38cee9a6dc25639629a108271e3b29a2791ccde2747565e37f475df0f018f7462342c9f101e89ebbc28003e3b690b828d3365644920ed24e2cefcfe6ee0bd98eae3cd0321fae1fd4a9de325dcc75ed01b18eaf51879117c103fe481b0a9cbba34c7b561fb164d0a67be80173d11189e0011ab0e266b5294aa2deb3baa7145813c544a1bd72ac46693e31135409bf7ffb600f53f4dd6a8ccedd69b4e97a6094ba01eff341dca55eb520b05c53ccee01cbc921af1b21724abdeed83426ec62cd2b68269c0eccf701ba21544b2fd2dae06ab3222466940da495dba8e11816ff354e5d5c70f0c283b5ca409b119e863c18fa2deb90177ff42d6cf7e8a2cfbc7a0cfcf493dde7ecd9007b02cd3ffa58298337e8b5e5de3041161076015f17723e3263a00e5d43a14ece8361ae33434df1b336c35bb8abf79ebb09c150706843ea683f73bad8f3ef6f83e0b9e04d19af37457cd10d1cc9076d6945a50ee34134250656808c6301d1039e0c68f1495adf612e3437747d28dedba385c92db6921c5c115f9e8a1aa49f6c13ef9f4c1b9df598f83d337498e899ee5bb5cf36d0011c7f0690b0d1f0ce93c96912f06e1e573bf617d11ca09d09e75c56a6fe7f92c6afac2e0bfb5dc2f6f7116d85cdf1142973a7eab4f472bac148fb169cd22e2a6316055234098c56ca100932cb566563202dd1a4de275786bfc48ca1d86ba9f0265cf78e3e7f9de0b1f07ccb39c9061b3a92ec3a1ef5be56a489c392a967bb83d5fac1ad2244bca2d5cb9d193468783bded76d706b03b0f6f2473818be5860eca5f4436059918c1fcef836ecf54bff0a530e3fc6822da71179c66b674ec2e2167f5f3b2d2e9c544efa245a49f9654234c8e948726aa7ee7991f49a2fc7aada399c20b4c456c3bdc71737140d78c7e0732ac1cd6923f7dea997e576a51ee7b56b8dfce77063f9bccd00a731aea70af2608f6e5f743770b3f631715e69b1c8d836faf09df063154072b4526fb89d5d0d0e2eaaf1f5ff122f958d3a155538465deb607f0173713375b9038a41cec45473659589af1b0b8222d2695a192b71b51224bd229dea4793cf1765e9a1d11277ba4b63fa04437293e5fd9dd1329980864b31a1ff9a2d06e3265ef6f3a7bb713c1410bfeb7967ab4496f38c911dc949ab64b188bced6324b11827f351ced54cccf2411d4b04cdf24817269ccdfae563880ffd40aabe52a9ea1ac3ae77ea368e9cc747f1da4d25edac432bd8612747671b9a0b7c8567117a404e3a0ca5c843bbebe355ea7d420e18d4b0685f039139182f5a237f9af08367f1c6aa3e7153c78f80b70be4ff6fb78747a5c691000740c208119576441a6d5d08c96d887f14ddbab81429d1a33ef7cd92c04499909aa1f67bdf17b609620210cb5f99c353c2bead5dac26c3d56629f528b0e5b450b104aeb92892b8978dd5b22bfecf6619af09ad1fcc670af54f06d63b49b2bbfd8404652e71330654516b85744789899870e8878cee09b0267afb0e834389c37dade05711bc637c7c9547d95f1c7dbb3e86f74bcad4126f600cca369a62cd3e455f4a170fdd39133f2017043571051141a0aebb6b6104890596d0f2eb31d80836e02fbf7bef6e11905b070fd9fdd4399592fce5d1c050fb397cbcb2c6b4d176c31033eb65616d541d87ca5357c41f15a2fa9e4658400f6a1f75ce05e7a075ce35b8291ae54ff503a98c0f1300516f576bf82b7f343a0f26c6071cdfbd2eb807ff1bd3219843b6fddf410faef35e7a8864f453ab885c5703714f3726ba41f610f25561ff53f6dd1b5ba3076bd9f8031ce19c4750572607deaec77a201d1626cf365e2952e2b375c17bce8df74bdf9cb2cfeea4b44c0f478aa882f4a14804499614f66ee379c5d7daa750fd7d47fdfc609871940654aa72e5da9459e5775d5394153656b79fe35d5b873a8c9ad7ea1a829f8daed501026ccd3702071374e7dda0c030b71a953e1378ba7ddf9e2f5b620fa0d70c5d907b41baf3b8cfd2112d5f1857269459f096556011c7f9f4dc3a4f839e683588dea50f971cb9e92c0e2e9fc383329ed398b6746e197c57add734b10926dbf338ca880d31a94df590595ed6fb8fa20c5177f736ea31227297db26735683721698116265ce6fb94c61c9a1d72252f121ce64e841f27dfe1aa347f358e1d3a65bb2c5e5272b3947deb82849e2cd15a4e6bd29098e9c64044531dcaa4a32ec2b88437c1e499eca7b682fee1c2857cb29173b6a8fbebd37efadf8347bec28ea699be0b2bc36f1aeb04057857f7b19ffaf0b58ff868b6a3ce0dbca25bd9336ccb404f954b33df11a541c36c32379a140a9ade8ca92644d398a891a155582a22ca4522f7694321509a9b4145a88a6aff6a7bec9bcb694b06c894d06c7afa6a83e2b517788d1345e78b2dec0f02e53c3a53e03abb613432e176fc48dc055e1f3246a6c96ea092ebb2c9c8fa74ac8cee4980cadc5ed685a605fcd11d2c93ef1c96b2ed781d06b70eb8ee7ef1d7878a4a8c086cf7d1c72b6147716a52f4ab4ec949955062256df151fbcc3553bdd50f6314f8f0f03d3f2d5e066fe0f12288c9cb4a9ac87dc00ef1ef83e6f2b1b15854aac7d8d887c1c2178fd6335db884da5b16329a04777cec327cd34ae53200f40b4d192fbb944a4c0b879d5a5fb2ad52c96119c2f2dfb7ffe6f4e95b2b6041d6826269a6033182b4acd1593fee52cf2caa03b1444ce5e0c0938a1e700ec3fb50d00739894ae52e8bd8101d43c22255b6895f559aab64e2d064fe09e91402950ed3932954c3918554e4cef924e269328955e43be53f55029d028f31ec3e358f57e82d97cb8ed9db7b56f0b856b3bf90df2bc87a1b1d79c82b14e955fd4ef98caf7e52913b77240fd0c41a3a6feca7b3145d64ff968646b49e9557f95d1dc9d4ffc45a1dfc7f1ffb0df017d8fa831626a0b69c3058368dc2aa84c700bae8e3f682a29a582d389604cc604b0d2fa18e4aa82ff0a24d2b55d03c01d5621f7dc3800e194a4c3b10c5045a8116b6ec8e44a4da8b75b7f47ebbf29de0a78c74c0c2aee13773041726b47a60bad8d15034531c3da5782c336466ec1bd0ceec5a1e4fd6d7642d5c6cba3bb2eb8d252dce359600b514bf11b26533f51da5df8f589a0b4b74129bdae397c888ca2137044d31d4a436e6e2ef49398ba155341731fc76510189b2d00dce831c2c0d1e3cca61f7c7cf38e934ef90a33d947ac1032579436e8e9ade59752e83d2d1586985f28c361e03d74082c2e05db5535b94f5d9f7d8d0da79d5a921bed30fca94a4b8291f17eab6da07475c453f45f9db4633bbb4f517437d0ebc27c1b70201bb2490c19298f0bff6964c975fcc7aa2e3e5f657cba038cbc8ba80957083cb84be5c777337de6ee2b3498dc86735a445fa2006da49a411cfe444c404dacec27cc0e00e605eea852dd2e9cd5678baa44746c117ddc5c6de2fdc90116db23776255b60ecd3f118078000fa178a64b929e7f5a43002a97d396798171fb8f64edc067698f595bf64c952607e840c75ccb63b7da59262adc86cc50efbafc1340d04a2c524d9f81b08cb3d9c968c7ee9806b5609112d17e5b7cf9ec6d55b20a51fd72f2b214b2e32258536438c53ca8bd66391f4e3036a9be6b50b3adbd126017cf814975c94172bf9ebbc3bd3bd3d6cfb793769e0cd274381680892bd2e4941925cdfd0950b8d2fce9bb119fc3220118eca0b5de053c24c2da46a0f87e82053ae62d6150162ae457341cc101643aa63ffd0c6a5e70ab669979d7de91636fe78255ad8f33b6ca447f4417835c23acdf1ff59b2de6020e7bfd64024fee100890109691112e28506335451ada75e44d7099d9f4e18d2b32eb5bb64fd563563cd905b67333ff00770a8ae6e3e704b5342bfe27cd8863249071033954ba64dd07ce0b6f8800063b8479bc902e2699b9938949294601fd6f11c00ae8b43016a14fb28246cc1a8ac0b5df040828c56387c81b6fc083b6d65bbe8542e1848e798228e5f3eb7da9b7cc0ba23062c860d9108c0c9df0e6b1293b136458920f70ef30d5738561f2509b026fe1faa6fae37f2180f5ad6f68437c5827008dadaeacfd054844c60ca7727be07fed8f0057ba21c88f146dbc9b13df53710dc04d932be6cb113b9204d11583f34a9c0849d087a7b382faa219ed83903591e975231ea37eea0352ea518563008463f4602ae9c47a997eb22a0ad9b4915e7644446899323601fe27ea1b22eb71bdffa3eaf48b5f2fa106ff3a381e870104ff792ea232f789c004fb263b383abaafe5359d6fee46bbaf995f8e5d4e725e41621968f52b4a8bcbabde33569e9f8bc6d013b51c15d6770cc5878b9f4bf14d18566c764792ddebf4fde40ca2d5ad3ad15f660cac4d406a9cbbea9c71bb85b10a992548de9e3dc09bfc28334afa0027e83bf16c0b122f678e70e7c0e19ec02734d8dba8bd62da2937efc9e320631fdb63a7eb460dd6a30513391af439e6331913d17aca43fdb04dd75f9e8f4b97e3800ab92b6fe4f3fcb661637d964919f4824b1577bff2f20c882c3d40bb3f946fde7a3bb20d85923bf792f06a4b1b32d2f316ae6b0696a0752436a388e33ecd858bd4bb7d376aa2aba8d842b63c3493ecef4c2296bd9a0d9f085bc312ca926341b4b94c511c719bf71ebbbf0fb2b1031181026d60692a62946eb3f45f17e83b13cd9b3a2b748b158647d6525f01f4f50ecc228e1e54df0ddd464f0d68e09c89daaf39f0ee5e664a57f84df11895ab53744f2925343bd33942e8e7c9b0e55538609b875f57da1e5ae00c6a762b710ffc0cfc358ba4c9f8a9742d950369e0f1d0a645ae3adbc11c65d982f47b65aec56815a113f153ef1d31917ea85a5a690102d5748d2f2854e3ec9531aaaa5f697266afbf3e9be852fbbcf7db9ace6fa09ad462a71f1306ac3eab4fa6cd7a65d05c660fbd14215e6c8c73c38b6342e74ca1b51fb0dc518983d4da76bcdbd1ae8ec762cf586624f7c242c953c4f927a5d56e733f28ccbde925ea05a5639b8ce3b6107a78b53cd1da4d59473e152d9afb4290756f2c45cdeb469648326fc4317e6f8750e0e30f0bf3d890116c92ad557faa97bdb3f97b9afaa4e460bae000fb4dedf1da5e0339d19a37eb474050d7463cb47c6a066903da2c346a3b7536c40181125a528f8d006d348b3748769abbd60821ee06b281c6e939a19f1006ae8d2cbed95b4259c80f0514f0657fb945ee17660d49fb48ca6464d9de8b65756ab7808451862bbddc25fbe1295740866b755a2e238efb987da9a84d7e1db8a39462c76835ef68373241846956f1eb4f1e91074f277001ea5407c4f3cf541613c66d3054cd83b1de0d26143a0f379654edc571c1bdf3de8a9f06251e518a8806f20222ea851d2ecdbf45d58e46ef318c1d2b6401992a11e24f952fbabeae549bd3eb4b7fe708cb9238371ceda0e2af52cdc9c90837064d137b16171792bdb0a250b7bf23345489955b6a922fc5beba36947da506e3d967358af6dba7fa910a17871cfcd3717dbe221a54f556339ebd31b415759f5907e57ff25bbc32e3d02b94f0d98944f1bc463a5914a8a0d35919ad66cb5ff530aea5a0b09a87a7267c1811f69c080794e5021d525bd8dd7db09bde735b826be5919e94e2b98579d4e13cf091bc40162990b04f6d6dd8097f3a4b092b4c655423a164149935b5d441a42983ba96eac2422dc18f272907c28666b8b4dad5127774105abac4bb5ae6e4a1f16d0aedd92ccda962428b66b6ec2ffbb2fb05b9dadb9df57b01a0c898b69316c2b1cdecef7d0eaddaf7391d03b67b940c2266a2e5cfb35c2486c04201f9dd9a39d9afb640a6538a18a8bd05c30fedbcfba1187dc8f803c48ce846bb08ed19374ac23e1d82fce57615f273058c461557769896bccd1cf233c7582b6e441b578a382fabb4490cd6f967154b56c0715812acf1302339a83263f9e3e6009fb2b584d156d789c574ce4ef25d213036d498b8962194c75534937430e8b0f9733d9185b7e0752e8390ee3da62f35097f8b99c21be2b22a272e5d88a98af1b1776e7e716487da909a149dfe930ad04d60a4ac32e1ef07e60784fca45a14b00c59e7ef5cbd36fd7b68a582025fc2835cc0e2bccf01e87390b148c13e7973bdaef541c56efdb55a7a0ddbd8480ac3761f060eb2a746df9f7cb6c328c43d5267dc18f1555789fac0906d558ff5c6dda53587b1b1ce55a2e8f2236ef8b4edc52a9ad24154af740bc91ee18b2e93965c8f42ce6c1d4f4e7578596d393e86b43c5be749b21b7608aa70307538f7e07f97647074403e46dacb443038680d2e39aab8cdcb6383ae1224f626159f244de8b54e5850b15224eea20070219ccfdbf9df9131d841efbe5d6dfca152673bb461cb3c3e07b6880cc2ac1462cf60ea6e8257da65d15c86fee180265fcbc1e9fc778fa03ddeb5535c9e4c0e204c6ec368e0cf4fb4bcc61d0eeb791c4ac9b01ba6b1db250d6ada651b8f8467c807dfda453a40e1be0e1d147a10250653fa3634568eab34053cc0ffc33f45e71b1015a96a8ea0cb2a1151400fdffe22f350485cc89768a14673d4d64906ad5aaaca4afb14312bf7e0e468f0550fc30b7b1770f41cae9a12284ff75cde048b5f2c73c6cc60c4eace3a00c5822910246a9a2067ed11cc47e22c04a6d34ac004999cb042c46f5553c945eddf018a6af39e4b9f2a5e1448f82e409b1c749d6cd4c8642d21c634e066910edb86636c2daf2edc917be651acd5d873685f83d0dcb06d89ed9ef718f0284f94f50f661216a5b57badf764ed5567bd61a6e84bcac316d199ff0042320afec6f6d67eb3663b198d8b2306b2ad9ea27c5003ca7850d60bc5c378d8b395ede1c607920b8335943f7244def29203021959675003fb87ccd1008bad1b6413f816e98c8015baf12af52726dab86bfbad6dbd65729b13f5b3b7c08f044267959383b8398a9df0ad2dd65beb35455da2ddfdb12b13157688064cfdbfacbc8f198f0d6d3e3bb9bd8c9bf2831e996a168c02b3626c32844dd8d3be127d1c8d4f462b7ce2e6273f03e75637336ab3d42dc4e90dca6647a61b72d3a782f96a81e27934a418e12131e1f77847a8147ad131f4c1409d5cbfc85c4290faf5b9ac67b00e14898f152011f7b4be1facd37176618cbdc1f132a95e291771751018b6f4477901e8e6703cc146236dd27bea715c8a46eb73945c070f0ddad6da1d0d0da7969bb33a55028e7371f8b71b189130e618b822968747f0053a885a1828bf869cac1c4a5412a44c0bb7a2adcd8341f04c47cb5501ad5ff35dd0b5b9016da78804d40a6bbc69b266a8f0ecb4a92311b5869e6a300307218750adfbd87de96ec6090320404bc6b304fbaeb979c2cb3b04d77839b856b06573d85358a8a46b1767260650041a37dfa49e304f3f184213b31ab7dec99a970196d0ce171a48955ec43c7766671d2224974755691b9d21cbfc31399a7a89b14fab869c04b26c7f5a7915fc2ca080e83603499d87a743648ace1ee9fa89d6745b0c32d494347232734f4bc8e6bc6796ba60628e366ddf3de56d5cc0fe0aba310a05a21caf1a59e254f652da852d1a523fd8b7d1cd6af0eba2fe1c355c02d798542739687b5bb3a502b066413e48b4e783613632b1fee2d5994b9fe124c6ed38552e27084ae86c3d8c3910ee58cfc29effdaf1039ff66a6b3af6835cb7bf828a00cecfc0591d959a20dad72a9a92e26c1192b02e655bc53d94c445c6b81d50cb5d5a19cd5994d4f3c78dfa18616c94b4adc3b8affc5f3ce5748d388bbc3216df5c664951574fb44f59e53449226533f69de87203e312334aa15b5108bfcb66cd71cf38b1d0c752bf6c8e6b20a6b3c2cd38a51e1839bdc61c5f9caeccc1f43daf3682c8864a5af175acf328b100547de7ac57110b816e75c44beb106beb70a2154bd5218c355d105db37cec4be949ca60eadbebebb87bb33bade540c71add36db05bd0570b0122178c7d58623b2729269ca27a04ab6eebb20a7717901ff0134096d8878a0ddc374e0ef47aab6f5d9e587d928e566e4440a004ec3ad07521566da4f9f4301b418dfac1a28cce9c3b529d2bc8bbcf9ec370d134ca1f656dee83f8a4be6fc437efa442f3e06677d5b59b9955950ad977c079ce85f3e3ad331d96fbc3cb3ddd6e4f064b4087ebf95e83b40ec09a86d0a2d68494d0ccf6835cabef9bc1085312940ed7c29ff32927e41acccd973adeef622283e5c1cb60f79bd78528f6bf0b019d83b3c4c82929fca18c31098467f5501ea0e12717a93676c8149241b5cedeaa2bf6e1b99ae40d30924a3c8b1bca7e670810a4081c5d449c1b5c422507289937036776055613f5f04a985ed2cce86d669cce2cd11bc353ac67c8c2b890e3ed214ac6544cfa762b4312dc8dab22e6f3f31b9b881c8b9f5285921bd0e8d23d5486a0de07f46c33f1a95335d8bdf1b15e0cd00df2a533310282cf52636353cb8e4c511a7b03bfb1957e1d3e7b810abbbecbad8cfe4dd945253804a69ca902b90560efc3c7752921b8414a2cd662f9da7a58dcd367fad9ca0986c6a375990610716b82e4ff7a6c99eca1e5c77302eac4ac3c2c58b02e5ef4f1aa86f480991400d70790d318032ddc395ced821722cb559462b8d8e34244aa87793d0266b8f570d6df3282c0358254e3fb1dfdc38b761770bd701e315005fd0830a6d1c38a93e1fd04dc8ee1b6f23179779b4f99b1e857fe5eb4c276877b68e16c545226ac354a2fa18f188bd436705833f283a4256ca18d5b73403605c1761e711a0e4771b29c31ca46eac5dc448f1bf8341dd975342b30531fbb99206a8027dab1123d28e5ab2e670eb4de1e80a61ccd7eda2745f4274098003ee0861a10455b8d7b8b9ed4b5918b5ca8729bd8438e180c8f43b4e19d2bfb66facda8b34a50c19a1b8a664185123322c4166ee551fcade05240df50e76609f3c58b55ef2ec897e99db85826dce6945ffdeca936bdeee41be5939e8d4bc8b171cd1b4277eb0968832651cef5f6241f42591893c7044259c7dac996d5586bcd67da60e60777d05508e5f9840299e3938288f92c0f68cb3e54b3823c8b38c9b7c902a457115bc96e94044b2fe5ce14f3e2b7bd014e6f4fd922933ff763323e8ad5f8439f86812924ae2e5ac7032fb80ed0ee02d048be89c40faa32a32938a436335058a9eadc59c06a6af37fe32c3d398e0c5456a67295d7dee91827df02fe5fc3f526b8a1dc3db06323124e473118d12cc92d7db2e77c48031d443b83ed1e8483da6ec1537c714b3418391d55d28790ff63b7d7b350c26de6da7b45c5a1a0280fc35b9fa3b3ad338ddbad963189945f6019a62283bb5543a45d0c0f8482d47883572e9d350731f7b2829e6c66acf8c9ee3db7529410098cb93c0172bcdcba1a8fae3458c12260c8fc49dbad167cd0ab65592ad4f63394bca807846a7f2d193df10fc2299fedaeb31da1f30fc18e8f5b8e907be1aa0f9ff580d98101f28d12c7c97293d298f94e6fef869f27e43e40a5aaab4a372ec7f7cc3fdce9409d1b751000fa25c9768898e27b49f183928f7699b863441fd0f8088251db63bbf1e93ac11436ba632230a587a92d15d43727f09b69b80366344290740648d4441f0a34e9e464781cafee717005de2d22e6b221b5764d9c4f550130a312193e6f61bc7765017d1af741c04349638910164454cf41c8717029f068194e57380547cc409c752bed2531e8f9f792e5b55d84529fa8bc0ca86bc1f478db9a4dc60c087527864cfd7e4fdc1928240232f2d0edc585ca21a27f7de7fa96b1608f8abb2e9c6e267a455540fc393a767506fbea91b51768fe2f701923ab89cf14ea0e9fa9fc43dc0a6bd832ac86eff2cee5e197d36314630f72b3b86ce6ac12edbed7ae2062d1f57cf0bd3c9ae740994d86dca716d02a91ccbcab4cd0b5d8839811562ec415e95487ebc06690be796e3d3fa7c91a11aa19a55529516736fa0205e505f08481c96c78ee9f121449385b3f8ae03838a0d85d01836015b8169008c0486fcc1b953f8df1009a6ffd37a6e449009e0747d5dba56bd78b6cef321cce1c749e5e4d1571984202e8ccac699784f9ccf353e2350b9a57d7a877a3338c87c2c71e1e37411d4117ca5702a435aeeffbb2fb604fb3b04cb6a916e8850d88852d562fd53c35a3bf11c94f8030f2d57235f658a92c85432b3fc8ae9e04b4f2150c244293ff850ed8da8c1efa669378acfd5f2e993176df7096310e0d0c0ca6d78b6345e30b8acf1e0c5db8119ba60cdb2fed440037640926aab84daa96418446afea3de087dd123042f2df275157b20088fed0b88710014c0f5aa2679a239e5f01a287b5a54d3d09ee91e34f4aea94fc162fc2cb308545c7e7e0f5f03add978789d203942e70125e36bfe172a1ae3fa712502b3e91154785952a0418cdf4899f9cb6463c5a57a027fd43b2a23ff6fe1fdc4738c8902190daf7b14cc843ab46673e417c268944e7e64dd8518f1e673a3e85baefbb18a2c7d6665c14f07c4f9bab6aae718ed5d9caf5d794e4dbb25bea918622fe0a48d28242c3adfe6b2e9fc5684eed00930d3806ac1eb4a5d9c4c4154273f1e66429839685b78c00c8c470ca1cd22e372d70a03960f97171d913b708758a9ad038ebf72b5b63769a3974c073103e85803378f9506b5166099d2d65de636c3aafa0234359d0d8dc398828a61dda58429e95acffce3cbc6ee2fc711b0c33936cc536b510da5c0d8814cf4a76a54fcbac5fe19a3b6c30ce4d3e5122697b081b0f006ed261c0981be5fcaa6b9af6b654c72a12b4f6e0c9a84ddc8e8ab6b27a0c27dd48d940f218ed700ed6e7f250b73b2dd60bc810a03c5b4b4d2d2858276177c9ba281605d830dcbf8a0ddf54e9eec961feda832ae8bbea6bcf549e0eb543e1e88a87d465fa2a9adf87a3355c4c3b88ccef913d0bd37dfc267986e014904b7c2b33ae25f0fbc5cd6e0d347f618dbae7eabf93b85d54f45839af3e5c7b2472796c39d04b553b99ea964c241ca0a5e0b65a3765a79ef443e90ac91977bfa1d5a2f7e473f0344cbed978793678de1f0c1d67189a8c3e307ac118adb6d3b3707b41775f0936e1c95c0f07d02110b68fd29d8841a6161480c8371d5ddb8661b59d7aea1e797c339403208c8d7a5afdb7fcb374a704337c3800e969c49e182c8a7709dfa6a1cd9058e757123770c5f7f1b1ca39a992f73d21af2d5779f037f701f7c96e51cf16e40ca0e77bdff70d8d14b2db5575d12e734758cc384862576dd33212679ebce8b0fdd74e8b9895122861952e67fa4644bce864ecc71a22fc3005df97296227873284cd5d1a8c04759fe529a4a11c8935ee4b4c33df141dcd0c0b1414651b02deb6482b857cf9b9f49f9cbef8d4b88d03dad652848f6389131d4ebafb82d7e19ed05274cf5a93a174d9ba517033dbacfcd3d655ccd329d2bd9e09f44fd29dd03ea3903d082cc8595e628bfb90010e60ad4c23ef7863a60adb081aac8f5146f084e293d20257166301c6fd801e8d4f74b3f5164150b475c33cff07dcca4e9c5002a57b2af259361022eb2fd21a4265e6ffa9a37d1c7617cd493522b5bf190a7649640f98e7bca9793d66ddaf8e0d40d7fccc5a21d8cf8b64eda21cd221f928e9d496bb59bb330991186d4a8893b0ad846f37f15682a472572c4d3e5e0ce81bdf8294afa934cf2fe6d84bada20fc11d2f6f9da1e89da0e76254ac4b021cac92f8b6259404de3400522bc6039b1140303afea8dbc6344a829ca123501c7a573d9ba92044768bb97a226af4ffd98e575eb5aa9e6a84f093d413b0c33d2417dfc16848f10522ac4909541b42956d6fa82e9725d812c8783c9cd8a6635e3e710b8fca3855b7d528f8427af4bdecfe858e0e4581a8ca7436957192b19ede97fde7e000a9426af879f6ca5e3bc2256c5b4aa3d92c3a13ef7c1bcf110507b5436f7ba7000531f5660de63bb07a3a08c622161eda2e7efc19138610a364b2c0baded08cba735349c2bf6c6d7d9706a647277a1cbb946e0b1dc2beacc9008685ca1df14a79dd019fdd6d0b11b271c18687a698047a9260a70c3d8357a0d5d4a68d76864296d262f4c98b7ea2ca4f2c0b227677a973d44e24ba4f7457b8ec79621cf579fb183cc35b59e1704bfe02130eb96d82aa082808b2e1aea1dd162474931e488c0311a8f66334b605d0e660fb90cd79192306a83870cfbe166f99910eb8f4ba11c8789d830c9a90f92a8528331563b9e8d018eebeaadf7c59360c335c6d42685cb081091624d4055cf40784a593cdb43dda86ef8ca8cdc71b6c26d7127102b75646b2a9a6336e093665072e9afbb4f5a75d704b395f98e72f2e54ffa997ea4f4425be526419f5912e8fc0c84987b43d8bf55d48b1847e5c283d1dd3459664b4199f259cf72d4fc5c5c1002363f83d90f6ccdfa8c5a2b8f74fd74232996dc40606246228b5dca8d209b683bd1f2e0c3ed0f14a2ea93e558c736304b13b2ef66351863bcb0263a1434f08083353e4398e71484c11958bd7fa5e6d0c2caecb48469be812a5b544cbaf93714084c79de207b0b9590098c94f074d5ec6ffbd5efb036e3e2ae3210befcff8de440bacb4370cf1f434059d69ec604607a62835648f9e6ae5c2f6324bf284d39f80dfb1653609c38621a8bbeb55096ea0ebf60dc7fc3272e49288d1cf2e4644e355ded49a323d8a4ef43498fd7555e6fa711a1a4dc7becb7d8498c1699d441b5e2c98d34a7ec05d2e11a73b885761c3fc0c14debcdcb170f173f92562e179245b51033eec35a8b89549292e62670ae8bc6e217aee72c9ade28f2a10fc494a82799b737a8a881da98cc00b27000676e3fe6053c8be763d7372ef8c92fda2d6e8187f6839c4a7a85fbd01c97172859de38d5881281daf934761ea783713ed550582198fa5b1ec741a1bab622c45645eb2bf6b24f0d49a2f4633d691223b08d00e409baa61d000e4ff7fe6c3df8aae75dd555b7f6c8dadb3793038f0f48f5d151b8cf24ecab9b15b9dadefc86996065e3ca77c19971c219b9b136e2405e8d9903cb563ab62493f7f7116384b432e027591c4c26c196fd63a034ad5bd40dbb402507c6ef1f879e202b313f8c8a79127a76ef4a47d56a799719d4d781303dd3c64b2c7c97b4fe08276015a9d3849e5058f1004f33424d17ea8ec810aa633aab02f5829040ba099d3051e964aed04f8a899f09b8060b2becc914fad7f44344cb82218b1dd1586adfbaf69c82a07c9624bf089338d84f020971962ed927a01a07cd8a0998edf7bb73d982f0308140176bdf1631cd0e107115c2ca9fe491d748ffac5cd359510e25e46228374aa75f67f8f71597cb30b3541808d26ea5a32b3cf546924ee8b7d0cc8cbf9bd463ad9b56622329577972ed1257c54a76b408c4528dab4987be768fae2d6da4c0934b07a20c4a87db4b9a31bee77c61cc03ba9618d7445bf89ab643462b8a7651c724d5c9c2943eb90fd5734a4db3c91552b0dd70ebe7505ffa13e58899c9ce4ed4bf1aa531e6d620321dff1440a84939c7d3ff20d5ee6aaf2cd8c9afe509119d59451d97cc6ea29d9beff417ae0599286af56a5dc8fe3ac878988cf358021b5497a106cbaecc075c65e28a627f123239fed7e257d1cf5eb1369acd47e18325ac8a50be50de2003ea77c993b66bb2e66e4e9a4d18e63eb586e21a3a27217b2ac0242e724b4bbbff9b84ca57a25cf49a39d4e0c45a587e950b3701bf12b56c0cda79b0cce9168d4f2beba682d3808d38d164f9245ad1fee4b69897aa662de9adeaa1930fedd0746e9dcfb50c5a0c801e7a01e13f4fbbd33b72e5941ecc74845fb165e0ffb45c3c0a2a0f2de08037662305bc02e4312f44585b5d2db045f6868237a4813eb4e32644eea2ceb14f5b3504cc17cf063c7477de87233e74a5aac474fd0379da3838ac6faae5695fd787fe645e2ca98d485e1ebdb3db546b1f96498505bcf0c918196d3ce7a7ec84448df75e2450132a11bce16de521e565c8785fdd9146c1fff09ca318906caa8a3fca0103b1838113138f82da4661ce84bacb0425c69f44f5dad4475fded3e0152fa6058a0502a463715341894132e44b4c99d0adedc4669518722e6499f5fe77111ed22daeb3678e33e844f37fb97227836e6c953fb43869870dcbaed16b6b70da7fc7ecee4e7f4c4ab698f41764a1f4a3160760ce349cdad58942b7f3187b62cb76fae061b2b01e1e4815ee29804decb4927a6ef686fb882df677ec6e5fbb90599287a947713d47348c6fcc449b626ec87177e02cdad4c3d8731fe876b58ddc6b7419a3d45a35d038d2a394b14508e2d7f49c462829a1c8cc19c315061e0756b906e9a97cce1b854eadc6d47cb4e5811a119026ebedb8e98d1b0f0b294a1c9c4a92fa4c782836035a27d0b496471f4ba1d5017811ceed30dd7282986a50718dd4c75c79edc230fb0e4e281868d472c545d89cad7a0fd2ff2877070b2e24260fe3e3a9abfb288f140903cdc1d91c3899f4f2b05e291b09bfdd0a1dd58e7aff2a74dc0cda9991a16518987a6af26adb39a24867d11d38bef2e9fbaa45f3217d5da19543a5758d9afc52003d6d1a46c71f78ab7bedc962da6ce9c4bcebd936568ce75e7963bedcb83c2afb2e3a49cbbfe116b575a5662c0fe262be9407fd9e25b649d2913980d20f2f3eadbad7b3bb1d709f8f2496ee0e206cc51dedf9ceca6e196578a8d3250f38646bfbdc956fb4d1ef0d8855bca3206c8e4a0ac47c4928030d9f3496f4e07880f70cb92f84204191378a2630a76e06fc24ad8d768475ff1594f9ada7ca5ea96feb48ab3827768bfa006b7919ae3885adfe30934de1a226b8e395f00114efba3d2b0063c01eba1862903a6db66af3495500413262915fbf7d211e9a024a381f6e7f294fb7ae0e666f2e04354fb60536e60ecd3da50a49afc451e45598acbbf097fc0c535b7873e3d6caa632dde5527ed6a4827d9ed80abcc00df4e82ffc265adf663e1a268ff67b9254f25c4b2859cbe96437efa0f11833c504b95568d052946f3444f16642563362db0ebd57c45c012af486e98c5a632fb2fdc65197c32be6e5166d695ea376b5be3260c26bdca0185f655d438865d751f0c9a3be7a50190b69a00a9a47172064be3039198af9bf8027212f9013d4726221044ab5f82a61d987bf64b0e673e35c51fdcfbf40998b68e18b5b71daa32e392444eac94419fb876b4b5eec3a81ecd03fa38e8f77a54c7128ef4a94d045eec86af7240b6e7ba2e00baa1ab233c89c191b60d8e5a3521e3a10710eeef91abce376b37f43659a762ba97445d588c80a702af573771a582811af925a91cef85d92de2c6a85d4b5b82ea0655fa05ae42a15bb8345c741c6587620f0ac9c0bb46173825192b093ce901e79026f83d88b9759c509091d2080af47a5f9e46f2f96edd61ac958265aac9e458da02f9a38d4adb326c9655e0d957656ce2ba6f9d54176a9e426243436808cdeb980095dcb7caee87129b93df570bb8bb8192ac73746e7044e2810bbcf2c47850dd66686a59f805e91541c1d155d36d17fa3060676a33c4ccbbc37f3f8bbd4c0f47e4e1f926adf235511aeba903a58d1148fd784279af43a08a2de7869176aa3a86344a7a8297fc2f15405ebfe26a8768ca4f0682e194c10e2c81574b7faeabfa18a3eae146907844170c84768f5ba18396b45cbd68b372085c32d7aa7d2d870fe863099673972acf35ee8f4d01a1a2002048d976020a21f0e44e1f35203a1b232cb4e56302066a1119d1b8f3d76c86b4829246543ea5880c319bae5ac3becd740364a3b60595a9d8b20160169d7cbae4dbd3c5497dc44b68715ddbe9176333285e964b003dba09dcb51b186086cee1cc135d54ba59fcefc77dc0ead9e47cf34d714272542a550f1022dee12837cc4a1ea122d78814117c81a36793e203f7914e7ef0e9da442851575bb19114eda953175dfec7dc507f1b979668761b1c5f349266303c9836a5c5f5290bd44cbc3d5684fd0c4a1a5322f47565499a9af4e182534ce738dba9fa60b3e650459cea9fb06e2a873068dac8fbb7095792c24a260231533243789854ae0157340c69f787654252a0ee93b41919fd408ce4d69fefcb2eb9534c21e496fb131bfdb38771ba66725ccaeab1047dd8d672eabf02054a842058371e30b1aabe94ac7be96c85fbecf749b25069183cd5bf506d1f354c52b0040ec0316c9030b652db4436d490639985d7ea562ccbaa61ebc283e0a685e306fded666450dedd556004031fe8c00fd3b7ff6614e0f466af9abac27d7085c6612844743e80909ae71b968677611ecf2b98b4712158c6df74eff6b7aac3df2c635adac263ea05b6ac15bdd67e3ef87254a9dccb6736109148c9be19d99c37412688b9542a90a021f7cbb877dbfa30e86df8dd4728470b81fe311b5c83f6410e468b1696b2def3b87952d4db7f1aaaf4fb8dfbbce984effdc4d5cacbe483b981a54e1ea6f05e5868d80c49acc115d95a3de535e3ab5304a6eed8e669ecc05075a83abb0414511bab7d998bea06badae81278e728870a4d43b02fa887e76db8e71f628ae1f85b9db634c585364d0a51aad2ca516779a690751be25f38d46afa2dc29c8827122eec85a798142f15d83d6e68012fed479273c0d6d879dee4dad056134356f21f16d8c7bdc98b7dca316ba1550267641a215bfeeb570ea7573ab954dbd7352618f0f33ce89ec06bf026dcad4ad604729d2720d67eaffc323b62f97045f58c4738d5f5c546848a245ee8cc198352284b5a1707606f0a4f223af4db6b8c6251e56702597cbb0a274e928aca37c338ed90b3c49fe31c2ff97beb80b4ce3d2ba3152ec35f8b77814228fc5881510433bf8232274f540e37aaa23689cd29f362a89134ef4a0de7713c65965b7cfe171ccb497285beb6f2dc4b7ac3eb4632a94bb3a04fe3e1ebfeb131d95a36bcf4e5937e40dbda1c0a96ed8ec85aee4a9070aa91d3651157da36125298e46b0ab00f9bac97af9bbc031191fdfd8c5d0b8cc26bd23014443861151824d9588404caaa86f7a4aa6d6ca7a866262a6d66e99ec9523175b707ab5b789d5b8021569c0e775ac48372d391ab80df56c21c200406391944407cd81a84e11295419c1f6fd0767ee9ab6fc45bbece22c6ede7679d2c9c39847387e4acb704308d3dd98cec78d41d574d193d0ba9b07191f934cf04839bf4e3e6484afd0c36838e27512a6e3967c8e57e4042946408e09e7b28b8d9958a4880d48bcbfc7ad67ed45e859783a9b0d060ab13f842cc1c107ce9aa5d485f97d97e13ee7613c9f38ac01c825ff0036996c648ccdf4d684b49298eb93247c854e18e33c37c9a399c550f90fc6d85e3b26d26d51cda232ce957a14614639ccdc579df03f2c13b0fb5c5b3c744022aa95842e936995c5b161768dcecd870717f7eac93d12cf39a0f67b281840ace408a1c1f7af66451f65741b761c902cc230d70059b32321db9269562474d96fef079dcbaf2347706742433c66ab100dfc2c977334664b4d08740b56dcb7fba6db106c45cb532876e13b482fbb34a85cd719a6fe413c4b76c65ac887b053546b325cb842f98954849c52bdd219fab72bc57d8e0510e36357e4ba32877750437b31b198a93dc4b1ee8281d58df9793e26554507fbffaa5c342068476bbf3a54c5eb2e1486f1ffd4a2604d0dff1c7526420e93a9f1000b4687cbaa57d22e22b63f460752684d17ac6da9487a0541ef429fc8ab79de178345cfc2dbc22d5e92b78e37e414dff780c83e3c82b2401c829c6c6707325eaa832955de73667721334cb65186d5fb6e4e1c632b6a44fbf1116c5fb231af618f430ba4c9e33d6e1489c1a143f553005b59822d7e365b32991d10c2363a8d5abb905f295506b0abbd8a7c305bd9a8c1e7dae613cb5232b351ad9f6b2a278fc8df857316d0a8daa7584c692930f1336ce75639f2fffd8bef1a33d18c4a5e29e3986e0dfd719063d8ccd4f0e1aaf6ad35eaedd8439686282bdd12b72228e206a7b17847eaaa0cdf7b09e2503846c12f4e0561f2c213b348203bf53a4f0b89b07e5a346b7d628c038747807e470955e463de3e53e5e0256e289476a37198db5203651a1d3507fbdd4fe9a0d04c2b11c89932ef20d1e683a68ac9eb1b7e9d1c145bceaf32c7de5addb86f3b267cc11c633372faec1475960377c25fe85ed74327daea973a5f3c4789a2d0d378bf969f360a1a253e7cff3ee0e4dd4aac33d246109fd4a36fbd4c2751d703642d971ba4c69a0534fbe6cc3ea8e1fcf317a912d865b41fbe3729c39e7a7ad60ed355bbb5969f3b476eab55b9d930e7425f5f79b847f87c13074c4f02cfa37cc7bd892ba82f2da88b33d757dc21de3baf95254b25dc5745a32e7ef1a5dc2081d7decbbe8c3e4770e59ef534ac121d4b5f57d66f4c9001dadb34cafc4189bcdca4819c86353c3e4010d8138e97a4c431d3934e8278afc56ace15ff51e40e5d500c1057e4201c25d008983d816d8b01d01127762bc9fee528d543dbdcdc2fb44481b5730eb2f1cb0e2759882085663ce7c03fe1302558a8305d62c1ae52a3878e0961cc4811d296cdf1f63bc42fb081c5ef0864dcdd62ad3d6950736cc8105b3d0483cd299c790221e484ee0676ce1df10fcbe9fdbf1df80be16999e201c0224c706c6d0e801132b27ae4111771b06b9e7a83820f16458816975eaf6a593bf4f9e1e73e6d0529875b72312cfda4c856933238de55dbe4f082302a63cf8c32cb1ed45f0fbe58c440721d36885bfa6e1986180566442a81301b4b67f4a9796e3227a1388febd84276632c42a71a7f4f7eea0f71dd41f836bd06fe3f5e33537069710e46e219313313a64c4818abb6e6c2065621456d8e5a370596623221d652fbe4a88cd49e1568dd086d55b728b9fd95e58d1ca5b57bc6a88883275f49a00e06b0324debee38113c9da3d9a35747606b9e8ad7b29a8adec989d46a37ab779d10b6d8bd535b6e6ec1275e06bda2895527bb99e915f5f485b1de341e01d3200ef8088596b4d557df188dcc2598f8ac1c5911b594ee91c9d1c25f584d70d30c3d72fa0abac0f38ec25dcdcc5f4c742f3f92750db5c6c84f26d6d5a13a6d9168895b718fa0f516f9261d81dc9bf801991b4bbb897e1440851bf815a71ed73d21d11a3bfb230ae2efab868cdff9ea0faa586c724a6fb3a3628b0b363c40089ac0a43cdecc53c278f205634056fa174f8c7e41f268eaca4d41b57cfa0d4e8da44d8043c0967ca7040a86a9ab27146a3f775229fb6ea2f04235e295bf55731f752f91f14175742ded6c9d845ebf093aece93d43c736758bbfec34f521364c3250475189471e67d25aeda8fd7ac3c9177c7555b5d506b7bb68164ab90b2f60f8a3f63ad526b7452e1255d52e704111c7ec31134a06498f67bae8b84b9e5551fe3edd215beba7b15ee2b1e2e05c3825ee00de2f0b78e70a543e90e3eee10d453b8e633bf62729fe2077441fceaf6587927e5d8665c2054d84961abd69819e563acfdaaf2e60aa8287d67e55bd9c3d85c95cebd3c5d931fb7f399efa118a874526736aa85fb43f311d91d517ae3d661660bd5ebda284d05fb75af999327cc3058537d5727ff0b2c116e4b1a75d872227b2bc94477c7049ad977901f8200c1d4ada856f13cfcf1b63338450b84e727b44cae1d453c9b808e82c64a621f56531283c0e372a5ee6651b50037e1fa3de7f1c84b047356f40907db82cd631c861671ad5ffc6eb36f05690880eb1506955a42d4f6ba5df8722a7b030a4ddb0590e1a0f682a50d4ce55483e6806a31e00dad0976870d99242a8dd96146f77bc5d35ac5bf0bc808b97cd9411feabf458697476d6e716c1da543a9072d105f4a47040901afb74f853e939166f4916a647b5e43585883a06a502e2f2320c542a49ac15935a81d040df47f5a27a35aaa24f99d39ce0211752c608b9c50b961784b626c68211fcdac72c1e88a91ff3ecfca537f368909f061fc110699433019f0bc7a75d1717fd34bad5d89b11444179cb79548fc4f6e9b1b3a092c30358acbcd12f7a42d203c9ebe1767dd4776daa2d50ef2aa05693294076f33fff2646d285059bf2fcc21cecb6ae210860211133fad07761c039c6499141e7eb53b39af51f4300f5c3ec37db8e1b83a623b24948b21fca5359f0c34c0337f0357a1e3e99a283a5c1b24b6e2a89114c900d9137a2a9fd6775029fe2672dbe4c378008f0f9fb624aa8019b0177c3ad08c6f9e2744a0904cec492c5cf597cc74316bb9112eb4905e25630866f346fda63ab397fd1e93c904f13088ed2cea984cc2d9576a1fbe22965fdd8f30dd7d287a81b2ff42fdd1106339f2af6c20b4585918c2b7c581100b8d6d7dd81051b301344de2701af8c2ad7222f6db49825c616207348fa64a8cd9121399cf33fef5e0ec91cd11bb97ed7ed54b4d537143204aadc55d9214799e7ce377445b55d5da4db1abed0b5408452f8c23689b0b1ef19509483fb43d17e721a2170fd56272054df78324a6114477fa4a07790cdf7d8a098977e067898764038979e0a1d351573bb3c05a15291f91db01847ce534933bd959a2622985ece4fd1d5a34d022c21f3422bfa328cb8c55e085bc913f8747dd24c478f18e4f7b37242476ce1b6539f35e4222a83971c6510d5fc273e6be7b4599ff177926269fc851894b4f6376e8cae0ee5eb29038a524160576c9b7972bcc9c876a32919d9c75c86d8e8149d051c8e2ffda2993ac4cbf680263c83c7ca30678188be69f685dda1b3fdfd04a84ba7141bc08341749b8041f46de5887124ccc08ae34e74b8a10492b1ec4b5c0a326f27853314cdc822d971d47486f66c5ac514c4db7e0f7e7c6bacd6024bd723488e66fd646a0f2caee3f4c045963f9616dc5a8268dc24c6b72357fb0561cfdb665568019df6f82e54c9678dc45eb8b2a226a5c6addceff03721247128325b3e41576d54949a2c8543bd2d8c64070b15dfdeb4c55ee72e05abea8ab049f2377d158d573dda5f04cd62c5b66e59ba743abdeb7ba49bc4469891c3a751d310a0e503c1f74f60c0304db658cd7acbfaacc8f323de3d78071c36c6631b9149370d137138270b493de3376b79afec1327fb531da0e6120ecf74bba311a300d58d1afd2ff5b91ffa9d4ee8df37e58617f45484f1f9d8efc4e4cb45aac2a282b20196ea674b513da54543b58633f2f165fd579fc123f38df4e201e517179910811c490753acbce026fc2a67d734b8d45c4cf793f9dc0fd73fbb62005cbea47ed774f7b0599899d190a363f318c92dc487405edf80cd4665e5e2f14aa7126bc1a842bf305c7e4860c6f5619c59a91e5bdfa991c64de670b2fc1a0bd50744a2931b67939814613f96288dd2b31f7043c03691a26ec5d97d7c99954891e99f3c35f02f04d265b84145152c177fd80b974c0c7a7a2e073a7b5739fa521bbcb2f15d6da8d8bd1993a280b1dc02e52ca24927de535346985741e4b6835b9afd029f843c177894531b3485bdb00f57dbb3fdd399160c0ee4226cbad74a2884813c8a96bdf4716247e6dc847d73eab97fad3865adc27aec17566cec71386fd32253bb30c14e68e9365ed2373dc5fb0284f8ad61b6a188ef3fb8f1908c124f7cdb546cf0331ef8b0009328293d27101176ca57f3982ff0d9f0050a89fd27b92dac44efe3354f540925f351e1d9dca04485047058840d001b2cfe54de5a4070a289bdabbc8b140c70066d7ef3d8737f80fe66b0f7195260d8c9d2f61ce73e173bbedb97e49a0808252b7788a1c833015b8ad089dbe1cfbd223ca314ba1d9f0cce5fe89e884c8ac23c13888c62c3c27600035d2b38931614de339c585b59cd081086ec9e74484f88af3bdddccdc43333dd0e39b6452455a2055060ef2303fd72696badd4f545b148c3681e7e2a079f5c252950d24a1f13e8fe2a6e07b3778d658d90c095add6544d14049b2e4328ddbc2e100696d1b6a858e516e60b46d315c22c47453fab27e94444482e94daa0d173be0b9cab42b59a83e7c6da6eebd98f049380d614d62a03413533a4f19d52dea157ca3c1083719614e37ac64c0a6ef7866cd6e863510b81cfae6dd67d73ee4c6eee95b57083b106c3c0edd6c9536bd9459c0d8958ec78671f2c317eb93b3bf061cfcf5bcaf55948d1793d94690621d99ec88952a56d9941266c3214ff2ea7784d53c675de4a5ff22351483fe8e62cc666cce1353f4c79f26fa93e68cd0ee2b194166554f3e3e3557900eafcde69618447d119b1dfcfc1ce7d1114442abbcfa88745159eeff281bd801cd2c414027fa78d4a3d660e23f32a7f9b1f35c179bdc295deb0c7781d985c4583344f38623da8603dcba429634a52181c4883493149430bc4c20560b01443940ded2c70457b402854e54cdfcdfe3130cc09741528872aca20adfe7e0d9c43950b31dc3e7ebd4cea932f6d7b880aafba761bce1fd178215c3501477742fc72e6afb3c67e8feaa12fa65692b9d937d846b655d40be44041de4a8c2e4d1270b510fd695a783379255b519b95017bac605d2e035f46baf6ae09970696ebd9bfd5dbd7e4b855e18fa0f203e784a8ff6ca0c25341a87ff999e7e56109752c2d146980da487708c10a0ad848a6ab3e6205a832b6636b2dd533d8909a839c49dc35926f9536d56ff25c10375288356f181133f8b2254812266ac07791ee8808b105c91c6bd991d3df1ab3f7c28a97e7befd93116b45214b2c98fc540738df484c0aca316dab503f94f4bed18b9367d1566dca65ba69f06a4ebffde5d299f7c146562b12853b9f6a663d93c4811eec8df041afaa9d3054b1fd043c432045d879bcc5a42e1906bf4cf24cf76b3b1ca1121dd998e8ce0d5d34cfaeb94154aa52b5c01d788d3858231ea541f3dbfb050dd57f1ad2aa6c15b1ac4348c7d726e760a5a0bd709607a4b689d11fb888c6b538e586e977413a59f5521c817668600abbc23c16b6950f42577783d456af512c88429dbca546b84b96c41fa5d19608e8a17cb3a720efb374ccd391c3a792082bc17ae5d5c714e24e8df6a440a73e0613eeff3221afc780c1931c10578ebc468e0d4d8818b98eb86c35d16d33767993024d29f98aea32b990e9c298186c8e0b3b3984ff7f6ae7f204995a70413e9cdc4c4bf0abd96bc099e93855e7fbb1d431a736772bc92012e53f893ef21d455f6e6efb5b3ad274684ed11fad203b7cd04c0736032342b2095c3360ee3497f0f5f0bc669ffe42616e6cf5fd05fa1616eb5af20b9a193882e3ca46e1a275de526429c370bf11481529cd8891b96ed6501d0a284f6a9d7e02cfe21848b9c4bbcce7d518b3a45e9291d87970074ac3281eb1d4d0d6b669f7286c2f5b00be7423b66170d1f89a4cf2916dc5829a5865411e496b29c7222bab7a316d58c1fbaa50917c747efd3bf24b6b069d9647295c40527b0be71b6494f7cf871b062c504177d09795a1cd571996236f3f9adfa78dd4fe231bebf0e7a9eb0675312e9e78b8bd307ad4812d0f5a57b328e0c5f5df1fede394b72b5284d4069a037c6c4fba707c06224a115fa1103da9ba415f62391ba4502313b689d119d4104eff5a666b80a1fda5d8d5aa3dd0701d133cf6191df5f82119f53bf598cf09299c4f55f22e424589cf7be92084e25b408b90d9688d0c3c06a8537fffb5d7cf3e714e21b6bdaa63b6bb4ffde76880e9195d51fe8df9d1a1868890d6849920e1b922200c111c28ba07090b1b61806291d178d800f96e36edd4e3bb05fe701780888da265c0162d7bf03df93844fc8b4083c789160dab696fdd8d8093cb5a0023bd62835637d77c31348d7dafd7be7965a048813cb6fa5fcc4847dab47acfb8efd43fc87250bea7f40a3726b4e368fc5477e3a0d5c2244f1b667f815117c0dea10544a9132050627c3aed9f4c29ae541701b046d557f477dae6f4c549292d8b9e61813b3abc84929a810cd02ded061fbd34516e5cb164c50ba777b59166f7c3a3dd6e9a36af61817aa283d496b0570c1a3c30d87b69cd1b9f618b2d540756c6124a6444296e65100543b28df511a62b846b7ba4788f061a0abb7b49338f2688d2a243e8681483709d6b07c2a512b6f0372d7839548c5d4903d3eca5e3cce69701408434733b46e6099d7f1446db5b95ef7220adc2ae749926797aaa197357c6d99c70dd38ac1ddddee9c54d5d278f4a6be6abd09eadaf46bba4b974747ebff960289fe452bd9d4523ccfed559f714f6e88c149071f3bced69d2709e65880b60a8b2bc2c4a2949125be5c285209395a7b3fcf0c54ebb487acc8511a7570761b43b43eed5054097b368d07a7290914b7d2e99fcdc76766158ea56bb2ecda3174eaf85531036ae8f29524c70840ef641562aa3fcb9ae8585a001792383202de146a57e827d03063bca43df24434451655662a75d22c75e33bcaf30b22299392089d262035d0b91f19c0ec53f80c4516227ea62453e4d61b470eafb5ac94d7f8a6fa600c0dc6bb6cbc8544614228b991352ce11ab9adcadc7f3175e3d8c54a46464f3aadd4e728462f140b0890978497862edb702b96ccf603eded2ad79a8317ddad54f17315a31f4c0764c8630b052655cb5a5cdf904b5c311a3ee2347ac07cad58047aaad24a6ed566cc37e75dce3eca53987a1dc164b09fb7b17e54798a8b0e9e7505465330a53973c718c20edabc38cd6eb30378e2f78585200a6950593be5f8a27f3404855ebe41ab7e33f5d4e1f7eb6f91f22d368aff4c27ed5c1aff5514b33d71b8c6c9d0712b33cab3969fe8f3eb7bff91d102bb1c53b0ec0c77859bbd7352811011db67c7707fef967c4c886302becdab04bc1374dcb82377025d1d4a08afb111a5317e613f78e1445535984d66f2f403c737a657976a7478a9aa380e1f434f5711594862499e248c18ba06715fbe849eb84809b5d881c169a0f4d8680d8089ab15181da95ed90fa2dc308a5cc7e116f66d2b6e21f8d790b6bd42485916c9ac2d64b26ac4453beda5f3c591edf5b36d1ec44a102ba94f5200d769b1d6d42f5111c2318172a5c8da1b424b79698d72eeadd69f1e144ae090ccb8fc0f01797f6723b35c9cc7f49fa92a079f4899c8a63db64f886394ef849829b51a5eb5956b89f696edad3e2b07d1b15481c4a75ac470d7f4853962e61dfe7caa94bc276e340eabe41a97c235777879c8c2d5730cc64e4f4c1190d9df813d45ce7947e5304a897d1126c983aaa7cee9b1c5bc0a6e5f982dab896c13c4da26ca37202da011ea12a095c0a7c76d109530085163f11ec4db7f7af8893ed77d8c6006dc040bbb8426da919e9d7e6d55fa89949f3913e75720343e7caa8b8eee2f6c4406b35fca34bc0323b2c411e6f49e373422fc296661b290395881353a29a76a9dbe45c649c0a77cd317ace69d644d36b654d7f7a9fd942b9fce21aae7a835a1b780cedda0528885c30fd1df639ef425f6111f25b890a7ac06b21e015686ce1faa9c8d4a1d7710b6439240c387d5e8d7ec55086d8c534b1896ee722312849f341c3d30228836318265c319c8e502269eae3a1f31a4320c38e78c27077d879ab2f5a5269d39b200cac48ce493548e5a0a6eded7fe235b20fb49519874151d35837a05fd93adf0cdbc97089f34b6216aae486f6d660c62d314a01a7646582b1a346ac5e39b745e4e1735b920f6bd1e3d5012a99375b84b66000119dfe2daced5663f1e618714e6bbbcf70e130f680e8d8e8dd30ffb8e4b493b0d1b6694003771c2a48b4b7b75969a7be18b0c0ed228e3b8df7432b98e2f79cb33c04ff5eb04f4e4ef3fb3cddc97265fd635c4762288703b89367bc5d7fb26904ffa841f9a84dd6e3f70cfd67d6385be63c9e01c9f92776bb8e9ae85e580a07708bffe28fb8b6a4c94508a21b33498b5e2db9e3095e901f17055b527b148d4b4ebea65c8b59730584a4af36c4f031aa48290ba2656fed0269da4179df4bfd9ace7e2c0798d78f65af6cbc0af50b81020e197edcc8a7823ea07c4d8735a7da10e562925c7b56b3b114f22df061bbe4387c10aaa12391a425e543f8a43d8c70204dd510f9c4b6767753935870b88766f33b13a1831626b7884be666f08e3aadd82e8ff63a4c38a7a9e616bb186b5a26e2730681a7966a39b6a772e20900d765b993e3df7be42ea2e7b961326c6a1ff869f75683b1ef063fa964c4a74aff3300babcfc6c392dd07647bd5d8d5324012b7c8c39322e7f16d0e6294623874688f50140161dfca6b5ccd8f5665907b384d5b8191e09bf16337921a4299d47d9e5888ed504dfa2b8cb69db846e9e21c10dadc1d15f493c40863b3e4aecfe92c8b14008bad5da8a46a23c049cf5208dba0648161a1f005bca2f79df7f4fba205e935e85cb99020bf64aace429aa51aafefd3fb065311c1e2212a21f869914bab0b29000d897743599ee83e8bffc0e6e38f54507295460ded14430464c385d8cb9690001d2508012f64a1d1ec4a7f0582b32abe2f01534cbf05da1481ab79e9fc4cd3be5a2d6b5b9e4830d11471a7908ca75adc20f0cb1c2c5f3429894c773d4ea2b1927980bc4698c2480cb9fa7b0869d0085554e6b3e974d269327b4bb8246fb6e793e612557d3bda48499d853fee15b543e1bb44d41c7e423363df3c604bf614765a86844e8872401447d1a61147dbc78a1d59319e2e1f9f8ff38e9f5178ebf2dbc36bf4c640affca0d85045ed7e07356a1ea3326e6e26935402f5a25ebdb825c5c806e470dcc67504177833dce0fc0a42f70ff05e08ae87f6b9dd7a77923496743653f60a6784e90d6afeaad27881cf5bb27e072f89f462f3d4bde08411bd8a05ad582eb01845e31c1a003c611e6fa114f4d196274766a0158211c00f0d2d45507b6b8b0245277626c9988bd8d74434a8ff2574caf38a4259cc35795cffaa726ffce207fae29b3d5d7834274487e765f6fd868166ae9eca7f2c11a8c4ab07e770deb3f94eee2d69869da4acdc3b774aee312804dbd6d3c8299a079a4685927e857b67139c35eb96864cb3a1e4a4dd0b3e402685e9b2413a3ea8a0a1c71138a1ec6599b13c13d4a0ef64c4786c3d77d2bf1c767457c3cd80081136050dea15c6cf6fd40d8246c5474eb34a28af416ad0e259ecf62091cb308df72839108e246ebbc992630d225aa86ced3568aea0380cb6abd3c21c119f1f6884e3e98cbea5a25bef4fe0a106f9a6e5aab79960e93aa764f65834113309ec96be11b00f7939ea4f4db7f7774cb3fe1b3b5eaaa522848cb58e4286019dd90f6851d344a276ed85f985db1a157337c6dfccfd75eed3a53f73003045368a0917316939e39c1ce4a3b22d7464d8c142c07dd413e31b8feeea9ab381a4f67e4223647fa7e4642b789155ed8d58ed6e35ea6f0cb3837ccf116837c94dc0071975a568a8f4debb0eaafb74df43c00f85a4888cc2ed759fa13f06d7acadc3c2a4697f0d9d5eb0d5fc47a100097bb81671d99efb3aeb70a0907cd08ed1fb167c16175000cd1e9e69526976c255514c4f160adaa24018bc84bac751982ce8159e8b6578ea8f3bd6d2d55d0c3426942d84b99b267afa2970e88b53a241362702354a4703f0b6e48cd87328ab682a212f07964aa1cf9a20018a7babd758b8e260dbbfe9b2e335219dae8c15fb693ed38c759728ad22b7b2c60cabd491f33577226be0c1ff2a9b616dbe5908212027de3abd2d9666bbbdaf8fc3b2256074967454029bb24b2f59f3ec276909ee41ea5211fa05ecacf12769305e20d22c3cd3b855d6b571ad1925b06e5a74cc13b18d1f12ef58c3fbf2d8126d066b75ecca14c5d5f6c9feaa231a1507f8895e6e8cc6f9a61c98803af7e57c76283360c8f3d60a470588f9aae8c907ecd495c42fccfcd6233a1e735d55d61f69051a23385865c6e4c5e631b65642b1a2e2d8ee37d808d5e686a23df50de5db9f51616992dd86f08408d798e43beb6154078efffc9524d335fa46f4085155526cfc3f49d576d3dd153f774459ea8e12630a9a95cb077b48499bb2a7218f14a26eec49dc81dce9b796bdecd0f28f0520da5a87b90f63c55e9d236868656ee58071d9264e9a3cb5b405dd2bb18e56255b1b49f62a5bd42998ae52620f00b5b9fd754fb1966df3a5e25577c3355a3849572495ba4e42f62935fd5cc938e31625d42c99b7edb648b7a690b7e512f32e9cb977dc6ff2d0b97f11a9f17053384ec504a891670367a2cc9a403cf718a47b893d62bd6bcf9bfde5b71aef40fee751ffbd6c9278d4694ccc98300a232d4037bb9db62b93a02fc17983e3f25b09b18de23a565cd3ed9eac527483c6bf81cc9233119d193f5dc62dd03448a1d4ff91e91472eaed5427f631ad9cf5c4b09de475f6160e5c1d327057ce79b4ef8b1b497380c3608a36667d20d9eb668a3b5a09df65cea5c8cdf971619d844cabdfaf3ef06f15c0d47529cdc8d165f1537df28e3a06302e5c7960a45a7c0b313a72951528e4642185a7e9fe045c18b483878364e6bf648b734e922f91dbcd701c4c4f8c5971a5a9ff7a4f45e3e9cf52557e7ba5a8ff2327e1927e5470d851723e05a6c089c155ba94d47ce1c14cd4b430756b9ff98ba8bb8f010b0522661b7b503486b2a4299c9f77d5d228a609ed736bf9cedd3444bf250fe16290c3d5a9b2e60adbb8405efbf7919bf2dbcee59c060b957b53ad8fa969656723f52623e6ef2aa21f137ab68a5f3f899059bd27bd66ef9b1a5a09821db4b1483d591efbfa5e034f349db98fa4e788d36c3c3ac1a0e5c66d4b7b34d2040a4aa5877b190b16d11556f7f3edbac28e12375d818b47f905a1ecf795c64994a631db6a4e8241de9d2c526215a2f540fb012dd4d04bfdf1e1928b3cb98759f396ddebda8efde7521fd6a9bbf3853b77caf3d568bc6772b441370afda7db5e57b7d0270acc4f4a08e7e1157a29d573e51bdde91b061eb8aba4b1efdba7d8b37ce25bf45481f759f882700d63cc6b47cbac90e10c1345fd18c2e536b21e90451e5e9adff572aa4ee3557bc60ca71ebba47332f00c2087f951926aa4161cf3f8c72fcbdbf98670e1b06ecb94653ca6394250510d1346c22354f27573571ff633bb20a17c4fd5b294c8cb08f6052c3b7ddff39b517dfc3b359621b1ca2d14ddfeb0ed597536574089d09d0d5af0ac3e42992cf56ccb8c96fea9920ab47dff773988d1c2f6ba524d31ed57b93c928cbd81b6aa6793d9b0e4b074d626a61ff31c0693226ac2fffdbfec3cafed23c891e033b861203c8bf2ee048436859ff51196aa3791fbfcda8776340785fa943a8ad78643709d1b974ab49cd4d29e703a4a38e244b71f71e4c40611e5a5e58811f248e3ba1e2449d01018af3b73ba16a763e225e6984bd5291797c96682be775df4310dae60efd909a215edb9103a677daa909b6e807da1cdfacbc1a803ca97269432bf13ccdb67fc87f710c48618602ed607eaa82c796e024ea27327a6b360ce891d23a760e40d262934aa5186c09fa880a1997bf6f0c584e7836462315a4c5a5138c83038845fd4104e4e7765c5bc6230e9b49c054167a1aed632c03f7245f0b1c684ba71822c1299976fd47276b3e1efb1479bd2ee546c353d1126ea0847d18bf433ed182d263f88b98ac245daa3d4285fa8b2e526af7b84f9e22a738a22cfe55a93341b7ac78ed93488e2b5b49b559074aa9f704a437ebbf0d42a1defbaf74408142648ddfa8011258887213f395d74fc9da65b894ed8ea6eb458b9a9404edf19427df165b400e3c23b6f03b4f0907162dd0f8d9e75e3feac9265ab65725a927e1c7e3c0cd25d10abe355a6f759ec04cd4cddc40a8df49bc460d21e62b97aeb0eee9bde3671c551885369f0cbb3f703e4d356780da79269d78e615e6784d361dbc6df90102ea7fd336f5822ee92cb0ccb95f735c0479e18aeb0a0f61303438cbe1fdff2db8324fc44c6b112a429bcb79c2d512c6865bb21e0613fecf964c4f94abd97995c4178d3e0a49b34ded039b797f5fade346463fef5b60812cf161ccf7447592db9ce3008f9745a40599eb811f881c547e849323794b03c53f28485d5bee86aa1d41255ae392481b62290f0c7cebde3fcc69558f24365718b705334f457fc86db42ff2834b306586267f01e74a1d7336d1d52b12f34c8cd93419b92b4e7933922178d45f267d445a30aac342bc23dea0b5de57bcb5fa547b994c9ebfdaa6e72d53156c3e8ec828717285589a929c15389aadd5dce5287852cd74b29d778ca0ff3f6c3adce2a7cfc305c499c6d356d027c9397b691b3db70ad887fbb4b15479a24e98b85cf2f44dc60e40f9c1b891f639d6c81eca46a82a7c495cf5a2a55bacb289ae4274c8b2c9996dd6826257835b27aa61197c65d4e71b6ade7d95dea7b20b82ff46b7fc27bf9d488fc70712ced2d8dc506cddbfbf50c8818cd3233d8a225a3072550049f0536f4901bc3f053090d1ac9bd7a759fcb8787b3415c8f552db816ca2417a379fecf91e544908cbc6e286e39e35f9b2e717918361b2cdfdccdf16dce81aa01d33fabe35969b711b0e4d537d9ba1fb31fc3fb0f77c0182d2d52fedb6d8fc08318334e30784e84598c0e209d89f396075669ec25d9e885d7076869223eb7220a07bb0fd569672c9945a2ab35d035819c1338b12ba263b10ce5647dbe18a4eeb3c490d631725fc345fabab06ab0f7994b01728c14d64fd3a476513c9a87f73369f85eb61721d0ca4056d237c1d3f4dc289b6f97ea5c0d4f286e06a7e9c9d3733a9488174054f14cf001745f9e26ac709faaba90ab3f377864b9e65243d201501c2a1b4295434ec50cb8185614ae74a43358acbc1a7ec511d4223d70d596092cf06835cfd655101e5e07efc79c56d91f6f456779c509779854eccf6fa5cfc2d56ce2b78a66b47b160af0265e02ac5dcce2f7f2002ba152f146ef79cd7d97ab2117af3f5d64966b1f84eb5d6a1d1d8b561d98b73e3cc64ae78f5ad42dc7f131cbd39c254411856ff3288000c531d63850bc9944d19deb884cdd6331a7423f581753678d1504b0313b6d1d30671cff1d9062affa9c59ab099e973b19cf8a91be2bcf8bfaf0f4f90bebd8e79dbc24e701d36e99d1acb852db8e45f0b761db9243018e8f6c9953ce45e49bcc84039b3ee0d858e79952229bb2f88f54794288576a2fd926219db6dc5808db733f3eb2181816536d3163fb38ffec2c1d0476e11f76f9b8ea347ba0bc21e626da938191b10e1e4e52834d50e0ceae03e38efd552ea4976699769534db71783cab2311d86ab162e9bd2dc6ee259c78ff2e5176e8ff4ea8d9d7a18af56149ce3336a10657e96c9f7f06042085647928004e62c3a40a550bb47820945063ec0296725f2f7c8188e1d83bde6228f020f295453688d39a7cceda413427c4a0317b476876271a3e3d43bec0d12c3904c8850f09b1bb6a71559dcec9d981d2d0b9b64b76aefcf1c4e6e7a3db5a91695fbe10bde9ff276d464216575535d5d576d9ab7e2ec50d20c4b73cf4fde851f6bb8b80ee5898174f48107a7ba932bc768820f0a91ad1b3c3431b1df4a9f8d8981e281053d3357c58dfee2b48b537183516eb68f2031dcee9cc56d97993fb5f458214b554d7461c0dd8aa4bd47c6ec4e40bc79b80c6ad082ed169c2a2fc0363fdb2a720a4db50fbb16e21bd988d9e710003cf32bb0ac337b162a0537ca8944ed770e67d86fa2c4d6e5a9156cea78402c52f53090fd0be7dc7cfbbbed41d7273c3bc7ccde41fb0268372e450a0da5dc8d720a247d2d3b72eddfde0db257b5af037dae057e706aeb4609d04bee4604f9660da5055960715c869622620d6f91836c3258314197694b2a92d3b8d0ecd5b54621941612fd5734a572ac35e01c8804e4c1ab457dae58e28885a5d474a0a896c131d0ee206813e52253208b44d17ee7558cb20dd045228b958ddbabcb9bf04bfa230bb94b1899b88072a8500f1d504103bcb5ed28710033e03ea50cffe286691c4bbc38d367dec38f713392a681bd23955e057017acc85d5f4829efeb4bd8016f4bfcbb8c02bfb8bb32ee46d5bafb9b5bf35446c109fc1cf96320df04d358181729f60cf06d9dc4ea06f115b7801069cb9949dcef4c1236cddbd2cc67e7c78c0173e368d8ff340860225a5e6d4eae55e318240bdad5f140d7d8759344f3188c0c133a0e808970478f9ee2a4ea7ecc5bb0837c1d91f280d7748ca7b2587be587e160d4a0f0aa4dcc09f20bc48b46b922ad7dfb4b3e8b64107c36857607830be6886c7fa74494b8d0c094973824611b95ba6a44369bac6706efee135a15dc38946e2aa0ddafe93506d809670a1cb9cbdbe02af61e679eb556ad59e069c0989ecc7b683a31fbfdd5f9e7adc74064798f60f65ca01eecba67ce6b22fec8d852d5bb9d20be7d3611327943f3664c9080fd13acbd95b161488425311afb1813d9117b7b06828fc3136b264f692be5f9328099e5c27ee565aa73e7cacd8bdc58b2cf7ad537147a1a69cd37f5c058f91d87bd2fd222c2bf28a2d3ebd50633654126a60b1c69bd337154c17b25dae5ba98236ebeafc16b2ad2c910b18148abc5a6bda009db0b6de4497ca8b24b66faf53efeb99b1114c521c6b684e95b55fff60b8127073606b427dcac0da75e4ef4085fef0e7ccb136e47101333de78aef55e03c7cb30ab3c60400133cb61ba18b1b8224218bc1569b2e9492fae2ea45f0a0994065049c6658caeda3e840fc57fa4d8f312041488bedfd21090646c87e95069bcd6b4bbeb1f9de002d3269c7c85603f16eaff6670b67bafc2abbe2ceb51cfde408b382db9723e7a628757cddb271ad9b3db2faec65ba0e2809a69893e9f331beacbd8c94d4b1db030df7a8482ac121c060877332515aac9023636db5713fa8ff0cf6a6903019b773d8c4856c312b2e957939fb4cb1dec0728e95baed400c309efe231e0d755262864af0693430f7c9ce25a1f9da7016a03f752612690efed07dede3e53aa5a3b2a9443dc9c0138a276d34d78a22452c8f33300c1eec6a683ad2d34666bf21d5a884d42293d68dbe45d174cd33172c13e39d13bc5444460b708cde9f0848c02e7104e25e0bc66ae9f46ad0589097790f5d41746b97a54d7cd9b44f48031381f1947bcbf705c9119294768491e939a8cca4efe36e90ab66ea20d6ffe9ef41d6c0c46b3ddd73e8237759e6aebc1ced6a126f5491c89e4dd4bc0c070f4b5adc0309bd85e43e317e2e39624edb1709957f3abc3b498e1a914752e83b9cc7d4d75139791d40cc055f9f9d1d26fbf4cbb63cbd0f59fa82257ecae223234b13f7e9a29a12bbe1801abd62186e94b8f058a8c1cdf13c1e900a690afe2447f9ec6d3e7383eec57d1a01f999ea759ca7b6e145ba62e1d565123be927e6d9e3e225e39722529f050e3fa4b4b42fece038e82462f1fda3f046b645fdd65c2edf8a1c259e9261c6298c0838ac2dbe0ef8eeb0e2ae09faf20a70459b30ec95b7d41feb98313678cdb9766c9c6c85efab917ee2ca8ef185187afa45b78b218af6cc373e421f55c5d419c997aea6363d4e2bfc968dedfca6efc4d947a5c4d3667b7cc0c7c464a4913d3521c5e27631eaf74b0ef1bc49200a2c645d2059d43a4828a784f6e65ae3a7d7ba76c189870ccebb11c7205f08f81738da2a3c19a2881d62bb3becc336515571790337d13a065cf8a9e1cd3581fed96ea3d359cca0ea6d46fe6f085b2c969231f4910168db0b4efd675517131649c6df0d83ed168baa44a0faf22bc66532418eeb65e6fa0be7ec8f267420ccad57884f4570c53e1fae0556411b04ddc833cb2082231ea9f5c81edcf3ae0cb83826337665abdcec0ee9bad3c335a99bbde04aa2cad6e39f0251f05c1fc9cbff1f0619943f49bfe0799217799816ae7c9390aa9cc8234a9fa1da7d6b75e28e9bdd7c35ea17f23c7ffbaafea3d59371165225bac011b5ad3f86e6bb83808b484480eafb5352d4ea52f6a24cb0cde4f8500be5c14062616d289576b5ee1f4e57f0b5ea4c92035dcc231cf47061c956da17f0e5946aa328d993b28fd64639becb2bfbdc410d6ef80ce763553c1139603be135f2e63fc5c39562667ed4505783005221b65cc4cdf5515ff638c4222992d91512110b914324c8a53ace2e29004f50a0db723651fb88dd68648c2c58c8bea4150eb447c3df82094d1b95bd490bb0e8713d0501a99e1f74def84edcad70fa3877537147ed73ee0fd8dba98f0763d0d5e08d4169bcc06ece0e5b3a3a9a7229f4d26c8701986c7abb991caa68cafd760da881326f6733b213a4af28ca6fa72adc54a9748a8a89c05c714371fc670c4653ba8777e02cc750d663d64eb055b868c665d3a6fe03774fcddbf7c59b57d424c9a487cdb0a1dc5ac36efc0d869566caf3efc940a385900f262573de30902fad2499f75aab919617563137aa700ccc7d300aae26ff90094cc0e8f2a53a5a5cffdef473f1b74707a2d3fb5781e0cf8990ee207d069b37b6cab72d825012e21511ce94dfce312ad4c5da7ffb3e578e828a6fcab370494e4f43e4a420bdb3af8dc88ed86c6d05bfa11730cd736877bae11d7f9933d511b1125a401cb33d7f5eede8059c996b4f699936a22f4771177b7bc2d76dcb901329a6537751c71c6c3ec9a8daf41a3fdf8bfedf6c236e353b55d82600254030715f0fdf41925c0d006571b6e6174736571d3d1e9cf29389b814f4213c9111164905e3406121c6fb10d9af531c2d48d1467f2eae1af5fc751eb65a85848094c72994c35f5fc6d1d9187458800cf014cc44af7a69e07a9b7a73a39fc0ffc905c23c945f8d2b9f7baef1677f1b96a24e7e3c35de7bb0c5f06d524fe1c594f4d962811de384764ada00f46597a52a9c39d9c06a683a84a899164cc866b70e0e62a2b9e5bc04afe6d45610e9c4fb4f3b8a5a7b62d001ff0d156d23747d554db9cfd39bf54a03f1945a1e1a0a2d8701ef5e887ba61279ff2a09b6d4fca13f9e8cef0121347b29103fd9f092b4d1fd6f1490d5398c8e41227158954f4716cb42c87af0ce75154c83f6ba4e30c6a7ba451cc169a42a313be5458876d873adf37e84268188087da1602067ac8887a47780ebc218184eac07e1ff1265ffdc9fe43b3d8e4154e542c735c59db735592e342fa911c5977379dbdd92b3bffac31fe3d870ff623401f762cb36109f5ea26e1a478d8999095ac18a6873316a860ec1313ccc6bc4a975907f9baeec7c4c83cd53670d0a6f42e78f8c253821e1185538c845e7aa624d8b235aeef1466c1e428239a58d18a6d38b63e319e429b83f4f90bd91cc5385782b49efb2b82be57f8cee3c288f210ec4fbcfd6e6ca1440e11b035d4c50b2938828ed7acd871d7e0528926df2bb445ab9bf561503b0aebfad8ebe1143ea6260e27e955fcd4e31ab7d754727804439f6083c04a37875d502c714933d723c2c055d4eeef9ada77dffd52a93f6bec5e409b8a2dc14987a94e4837166aa1939f076f37053e784a1864a103233ea89ff45bf1a1829b4f32ffe064b0d8fc7fc8af8e363b631fe37ff968161fe3a417f230249323118e816fea259d42da1bdcc090ac5d1bd013a049e745028916ecb5b17ee1e5de071eb85a0c13fe3bd6bef90b24a95828e65e705a484f170a6605dfaeb05bb3d8fa1826e309837f75c2a57b0e52d74e24e0220ce8fa92446fe096549be42d020b6e862f3a843e9aa1edea4d17a5ee424566dd31a22c28f7cf0ca0c79e09ad77e5edde0659e0ece2bcadf97bd5d77f296e05b05c1c587484a474a80621455304b78b2bd127a84bc115c6269683bd435cec65d0d7d542153af4806cb290d48a79afb33806c9c79ea7d47ac29aca927f1f691d38b64ca74309a245e5f2c7890615a6d191c6757e9d6f07a959e90ec80243c2968b62a1d2054b3ad7b1d46887c86e9a1b3eae14c7d000ace5385436dd6445b02b57765d9094df5b5d3f54a29307cd1ab172e847aee8b26f013314f469e8185c1f410c7bb0fe9dbfa3f6007264674620d174d5bee6bbdd124a8dc9dd503956ad088deb88c393aa81ee1ee097ff4d6fffc7ddda0eeb6369b5736061a7f69c3bdb289dd9b615014d490a911b62ee5b3387423209b475b0b87a80cb0f074fd8107946d7f7e94bf044d319deb8b54b29dcd289122d48f8b1c4d8b932e01dd19b19b3d34ec33ace5c8aa6d8b14ecea75cef7b28121b31b251517410f7aa0af1e248feee6dc807be7fd57ef1e17bbc53aacf8bd967dbacc24d9a61f9cd380cf9ce521ee07e8b1a2a90810db666d193781df9027999f8db1776ccff709795bc28ad308012d237d93fcf5bea1442dec95526941f664dbdd784b84f6409dc34d4360b3407fd48932ff759f712660e19bccb8670ba40866ffa4524d281681a80772d156d22f8d713dcba32360e59ce388dc09dd1bb1f1dab310e95d787cc0a73aea1e114c601b5cc1b05d448685aee4eef4c9652ae95c0a02b1ab5f8768690b3e3b72207ca006901d496658a844641c563d0fc20672ed93223637bdc81913aa1b1ff3422a92f8088902f9c0370cb22faaf5bc25870d278765b8aeb53b7c92533e927ac00434b5ecebd5c789c441968b0eab116233a4c5293c7bd5c5167e322e5e543e748474a0dc896c61d240c92ba2e787f3c928dfe5e777f8212207f0b397c2f26f0622708d0b64bd7d4a4a809fdc514b9e9d1ad409903fbbb97c3bb8d8dec394a9a1bf951e842ebe1b04d3f9ce5b7987a9e3a5c502879980e72e7591bc26ce3383f049c56a4ef8693f3d3c07e2b3cfa75543d5a2e4664e2b590c199262a63ecd03d3d1d89068b0138c20dbd23adf614910ab23673e1189180febd91470edfdb9cef4cd60532d5c4fffdbbbcb080c2a1f42f5d520abfbf92b398993ea58e4b0b729552b998c5001e8e69a8210befcb3b07366418be002f64e2030e8c9f3daa1ee4ff2162ca943312a0176e941701787df61a6c7a08de4c9d46cf3d064d0015fb59746689c1c8f29c7d7e6bdc5022d270b3b77146cdee4825917530a5353bb2ceb63339565034dc39a79932a283d79835b76fa86d13198929f5796623bef3cc2e0e07353b0bd1784c048a1c3997ebd37c6ad08172226a01b2930177988ed79f897d042ecbc511a96dbfa319b3a93384ec7995e918f19e5d50e0ee4fde8c649c0083cc33a5b184843bb9b39474ed88d9ae39c040a72f671b390325a64f1dc2af6381e3535b6ea3ecd3cc2201e0d4343b65ae533fa8eea93eabd2d880df51bb6ce2d58166bc0e8f337e71fd7ad50978f3e30a7a13af327aebf6189519f834461dee8b27400161d287886b8cf85b7ec07c46645f7a790d5b06683f1a962bd8893eff32ce0758140a468141d32809ee8c02440753be78f49fbdca4379bc166df51c7e614955ed7e8ff8f38401468621cda6b7cfe247e7a2d524737b5cae3a8eea6b4f402d17209c56f0dfdb3c944faead3a50a52b688a69c47691ee600d48dacb0ecd80171bfd38a73c2635d612be872c60a86c3e22cc9d3456ccb7d2e1f00ee277c3de3455160a5d068cf97ea1d3c7aa76b8ba7085ab4e60eb02132da297a7e53a356e3b665c1dba5c8206301ced41d16bf214b4f7f344b1ca5c77026e0ec448401f931913310ebb66ae4038d96d4d98622d42ad7e1d62e2c6074c6fde038acf6a7cbbe419445b5ad2b35a161f2b4e7c4b395a1776da75a25afbe407bfd9eb80ff9b6a4447944356803abc01bdf2eb8454c4c50be175abb35caabe304e1ee001513366b312859da8d98f32f34260ded7beaf19f6e22b77a3b3466b3c0c9a060f1f4212cc58cde89096273ca4bc26e9e911b7838cf6f4a8915ac060733054590be46b7411113fd3e5970b43efb6f33622b0aa3a5e095a4dc93c7014073f1a7bdac7e89bea6500ffc0c0a5a9673d32d26047d7e3e9052b9702394708dc448ff5ba283b9d441befe95eae248a37fc058545d034fde82f0cbbc311efcb4661ecfb5c7761b291146470ffe22f738c33b635b2ac38c338b53bab220c9588ea6becb41d1bad78580ab144984f78afbdb99cebd29623f2951f259c09feaec2aa2f653fc59987725838e46c5f4e7c0e761a29ec748f89d2a5e54943b1f196ddb755849de9ee584ac220ae3a744e2e2f00909a6b90a1b17e521a475c5a6370f4e0cdb817ce024fcff0622aaa99f0f69c075f3ff715b6621664c0c83b0f1693ad9e9e1463aa3152fa6e51a734100829c5490622b40d73d46b7f5d9821f31da73d61f47f58ddbc7f85591777b971d750a6aa9c50985589f1e135bdf4e4eaf078e5ceac9459724fe3cfd72e49773ff010ce1d49302040ebf5a08f543c275e51f4b50593a97b9109820ef8375aaebadcafb967edd2251ba6bc72907f1699ed7c8fa44fe2321584cf62c3dd184f9e075863e624b1d2ff9ea1b68d92d8f28ed622415d919f026979dd5f270a12e8144060bff8dfd4d9ee1c2c7808b7237c3a1eb97556651315c8656d1570fd6962407db348f5020adabcb9adc4a57e82d6964383c223fdef65d8e86e9aa8ad5b0349f49fb141483c26a45a5a3987f9fd8ebdbdb0b10c894122cc90916e6aa4648cdb134d901a3efaf3e4a7bb114954ebb86a218d049dad9efa8235e4abfbc0707d1f78c4f58d3d8779a3d2bb348bb38c0505a680b5c73b27f2baade67916154f3a866fdecc3289aba2799e9a0dbca274064c97aebc78cd3206dd9084bec360193f284991f46fb23f42f37b27d2642cf6c93f81a698e1371b473038ccd2307d22d07051ba26e2515a538bc93342b78e1f826bab1272e99347b805470a0f10cd16aeb27c15bd4c56efc79629827cbdf0b0bd0cac5845bd80761ca272f66dfcf239915ed1afe14ad98c2a0974abebededf74b1b0a590bd64aee75eff38109838550717db19365638ff73a5166cbf6af91c58610af4a450f420ac67560445443733bb4f70dd96eb83d9b3c44dc027d7b6c4aa0e466487b2f1cbf0d7c1a9fdbb2a31f8e8382e606aa28c9d0750998ee90b4893b5999a48470cc9072fecca982986d3edcfe3ea4464fdbeed0055fa7f6492a2ec7b9042c168c301a82306e2ac238ef46abe6aafc60997c3fd46760e3c5003176600b07872f968d32cf3c9eaa9924f1122153d5695aed78ba504b950a46c998fde1364b66ee6136668949067dd969270c476124bd4efc332ccdb16a1d3e1610069386f60de324c78acdd8db02b5e2d1046c5bbca87df541d69ed3ab74807348b3703c098897cc6f323798e52c969972162d5d8490d6a9c37f548360c34aef330fcce32601e4fef68da6d982b31ab7ec55c80dab5e7f3fdb0c8066c3d1a43505b710c393386d0a9070023a79e8a0808d4c50df1de8fac2562d469ae47836b3d043bdc710328ef0b622af6803a1e5e97606c8294334d57e93c5c09d9302cb7a50d93fd3aaaefb3eeb42be92423140d18c41af5a2d75d4c89dcec1428ae443dcacd96aadce9437ff46bbdbbae5e7398e8e41cfe634f51a181141485f503d5247c00af7e05cc2a0c879e96b2bb20038049a1ff4f964cdc993449bab846d76f81038bb1c61b7a7f1791be8c08101bf02dd6570928d4b1ad227026e783e199da66d29d14c19180bae28714c583838ad80785a5cdd817b2522dcf04041f12fabe027159c82abdbdb7af4c856ad053d743b096b30366441eafa84c1e13c1860ba1039ad0a31bf38789fcf7e0512d1042f63d36104a31cbcbc8197f92eda51af82d9c85b8b468b5a5ef10a12553c9dc3ca024d8c4e8c48b040b88661320b896c12f9470dc2f714f908291257eadd73f060602e30196ba1f363d9e62456ed1459f0d61b6f956efac72cbbcb553b2e72ad65d48c64343b76c8f2bc6f7115c19b4da4df9e8e5375c4186ae9844bd4398b79fc125de010bbb4ad4b4d3e4c37530e68137e957aff7904a31813be30296bfca9302f53016c6fac4ba3f9771a96e3500df6eea14527a19963c74218bffae4a6cd744dc5795f49ca534ea7642cc3a8c82efd68c76494c8ce5e42ef1efed21b2a43598b1bff1f6351efdd15d2106c528df5850e3cb9a79270edf84592e5ee868f51667785e611e7758ed9c3cdb56a2100a347fc4913368d94681468ef357b386c4e7254c7b03b0854661ed7a2750539e97f8d303355f70c0360611ef584365974eb4d791bad482143854551dcf14dfe0007600941baf07763261d4e80c44790c44d3729ed932cef2c42d040d3cfe567487f912fcee506804c93a5e784950e505aff72b7955029d450aeb0d58fe17a5c71b0cb60638e280950af65e35aec3f0d2d1b85e0106d1e478a76c1f6f8c2a3d972f594a5d5e0ea7f61f53ebcaa03ac95bdd8ddd6c1c804fcd37ddbfe90c5e9e9baceb7111fab5a84f177d9358505e235e3a2acc2ffee029f69e8446e409ce51df07e73f813b59638f508d70688de53d628596b08cfd9579772e3f68c98a444f380bfcc41bc236481973408d840b9f0d8a5e7901ada3626c60e09549e62ca581d9dc0dd3d8698c2c8bbf1a111a9789785f68336c952d1274f5911a371bcf97947c4d251798d7a0e0c8c90c38f9b1c68c97d44c4f6f249017cb55021661a908d213f0c4ee413a1b54aaaff48cf287da61de0d05e59cd7222f893a01eb151e5150417353dd9aaff94fa4a86a3cea99d9eeab89fd1d89cd9441c724b2b6d0b1c60340d3740f721814448c9daa030e39ea5c4847da7f7c0c76959f375ded52fd3e6fdfe8ca9c070e3e2319e5581b2d4966708d5b1794b86f7347b46b4e2bec8c005f77f1c1a039d533350c4b6db3e0a6d1faa623208c8cfdd41e5e671fe5a3edf9e1e718ac47ae6e758fb371710d0adfc40cf81e26911628f284ca6bd4d61f48a826dc1bbc6d0a5017e7c0e33a650336d55db277c05c645bc6bb1f7dfdfc1b99e0e7c079d8921a7ed3e79dd7d2e1264388b8220973d298f31365b137962917455a96066726c9b92add19641c2fc3ea43beec41fc4048eb16823a70f7be4adc5b095664d06d5e544192585e085119d6c948f40844b0627cb6c52cf9a6b19db60e602ee110198ae9243618ff0be5767810831cf6feea77dfd891b280ea8bb99afdb4521505a0c13a6a7a0bbcb51821831d431fa62c706149c20645e992afeb1a1ebda268b48ad4523d23dc223a5e253fab1e2dad4daf97499736d8eb76a65ee252ee2185d9e93ae936c0c3601db225db94ae3b726c716d27fce0046b1f88b646d140bf30cbd79365e9d64dd84333530296d5c2d38114c0c79254726c808c8159a22172c04c5019868a445ef00b5fb6ea48c10e365ba64a8706a02048c91e49360c41cf27048a1094e57df6381bc9fc3b541766cc383a6f354750f5f19bacf79adbb086cfc24709b708ad3ae3f21b1325271d48c4bd2d84a972756c5fc56786803ab5f0fcdd09af7f16586ad3099f8f589e8234f6eae9f11120234610665610d49c4af279adb7113a71af6ba2bb516f9e20be66a09128b9914672b7a9437ec49314ab1792427a73ea414c3576bac9dca6635af357ead952b695c328afb7765e056062326d033f6f84289b256faab32758640e8466256f67a48735c29d5bf8c933f2ae5b706b568869ac8bec4938f09cb99272efb43f84e403ca52408f7cb934b8747ca31cba9ffa9dd41b8bdcddb683d3c4fa0605bcd44c739cf24b7990af9b875e7513471455a133552248f9b8283f45fc3c78a844e396013ad396b8135bf132c2096941fbe4d28e7949a4021fdc6e2ebd612b1d2e5a4c0461753cf97dd6dd953b8af971c8f83e5ef6bc7626002bb0e55eabdebab79f712d1ac1fd65df2d4c074b5930425b435961acc912e7ad410dc3464aab5753ee3a9a5cc6d91d7b4df0b879daad5155e7f4113c2630f845e9f95c4fcd13e47ff279c776a5a4a6a61154e55a2024b3b099019b1c699ef143891971ee5f8ef0e8816128d99524686644a0ef3a2ea9bc0e20fb1b33f10e25126fb5541ca996fd6fb10439df7100bffb7f41b0cd75e3dcb08b34a2aefeada8cd4ea0b4e8259bcb283914c65050a54230ffcc3a59d4f97ca3918ff4e319c34d39a7e0fec5dba8866b2192011ab5fb72e530783c840283e4227c9d8983d452411d4f4c088e30850033cc7c656ed53b63f4a0490d99653912f1480480dc4a4e02618c31ac1eae8da190f292a71fe4cbbebdfd4376acb19975d4031cf5f86f2e3e3b3f23d55a97d8b665f80a12f3c4d2c97c4e9d037b515454d34970ce48b46dd83b30d670544cd069d737b9f4b7f16c8896cf538ed79d9a1131afe8b41c183f9c8965d637160b93ba4f7740b04b0201653fe4c1507b82368175b6c024446b53a325ffa3a52c5e86fdc08d2ed3b476ab83a287e579e757a788726b55a4e45f880b9de905700b77223a97f6bd9f93913d59e1ad20b5c0cfdcc5596cc521c14ae7224b79d12eb3f1a0c4ea19e2933d231088ab3fd0f629c922a30a43ed250b3f4860e84160c1649cb4e39a7038a984522dc3ada8add9ff63918577b7131a87e88c0634086a2b991efbaf8076081875270a76a2b6537f79c966555793f225f7a1c382f0d96dd6bb7df17684f33b8790b357711f244826187b90968a0b78350a79ac5c3e6ddd67827b0a513fcf607594c55f435be57e652198f2d9e7c447efded6e6166b4f805d58b6626c9593df812506db06370c3e0e97d4a41d770dc80690dd973f6d76846f3c4b3aa0fb3b54b0bf108ecd113fc72bc25af2102aa06cf277b82bb9584adf479ce0c179ab022b4c0ae6347dd8efb47b822888d3f7aecc08b3e40b4674fb1be1a36c38e973c6afb148f3ba4b394decb8531973c0890cd0d68de74b677215b91784694adddd88bb68548524e3449665d75444de1595976972efc6f6d521899ca93d2b03b032d4b6d6a4661ec2b570e9b3bd039b59cd4e6b264d09ea9939a7192019db98a7c76888e82d450a96fd3837f3a08909eab273be28c7094887f98495091e67277f0033f1bc464015a0459f4c299ed682b2345cfd76c555d7f91f014a53f57aba1557b700d6bd066e84c3188f075d44928033b0052d32ae759b9faafdca70491b1edc2eab1d2874530be4608af72765c2146e747b65f7caf017a5053daf9eb84507347b3a4a56860405dee1bef5ccfdec305239edf6c96d4f00edcf5d97ff5ae7136d6da37b0ed1fc0bc20c6dcbf92eabe48edef69dca74446a1c9cfee39192d36eb5c1b2a4da50c766729b2f51af4bf6237a46c0c11eccdfd2776250bbf1e35f801d04a8c88c102db201c4d3c3bff5ffa5ee8bdbd25434fa89ac3b53ff0b96fd915b64f699eac252e7d0eb4415f424809fcec25d1d6f5599d575a5ddf6720aec0f9a7716836f55ebf792d19c28a518aaa4eed9a91529034d115c92109772ff3a39932f6cfae315dfd1b2d79d1ce0b941d2d3c55f55cbeb4b7ffb7ffc59d12a9fc1bb5ce7ece6406489d55ec9ac35897abdf7ee42480b81776aeaf8e548573426986190432e00548f084e2d0dcd862a41bfc8c96c113f8be9aa634bf64af2b0db5e379cff7a68747316728af293efb8c702b76ae6d7769ac62a24c40330b83474a9e17f6e9be855a0d2e44deb961687c57a02750aba06d1405f0f66adb19e3ea19ae10e33f139f0a2c509cc70a0ae4bd0fc182bd1e87aed731e533f1ae1962a691523f1955d6ea177f28a84363c274af0d02639ca2d6e0d18527752fdea62e5ff5fd5dc49c5b5442311e3dca20e757af5977b38021f38d47da09fdfb5ba26c9678268a4fdd4c04220e51a187bbf0a7838675ed12e086877c35ce43fb89ea383c6000f46088b582420ed67d8b727a882589766f99ae786dc711895b1af69fa1cca5c6a7f50b774126b013ba5f1d71dd42189405b4d2b8a32dd4b1ad7a6ff190c505a92ff7e470a82bada02f426e44c6d3c3dbe12774419c2603d0b676b846405750da624bd57cdc3b9ca76d0d0245cb451935e116142a0b67b65d6a41f69422d67f2b1ec3e6cbaf59ade3fd3cb34864e4dafe47716c1dec7a816d9aee0f58d8bec120511c2bfb1a4e222a7d04a58b73a332353c22547a5145fc3b9dd0d92d9e18ffe5df56a1781b353ab112e191ee418d29fc3e528959e18f37080b7c0b1be51f38e53848a23abb03626fd9a6dd273b5087084928c60986bfd4b4f582473c66194ff97c2bc9631564b847f939ab4f4e620be5d306cdfa7fa7706e762769e25b83fcd90ce8edb96b47c7f18b9f3360711a8f72a62939e2a24bca65cdfa7d3c45b062f339ccf6cacc5fac7367d85551499276aa85b6379c178ef52ca9dcc2671e30008e73b830750c0380a03ab7797dc2335c2f4dd4b8cadc26a2fc19ef66c0fe679e23f4f7fe9bd00d143d1304287540e013c42636464b73b61a2dd77466f022e962c3906c54081251b0cb2dea6973883627036a058d18e318f93d843eef8e1ff23dc4107edc6700724a57b489575b0c7cb39768a0104e706aee24776c43bebc46ee1cfcd63eb0521c2674ce3ebe18e0b68fd7479807790657b3478a9ddaa2d1da52407d61b2a0e5260a5c49a3495b036aac46236f4975878fc5365dfa19d0ec09dc7a9e1cd3c7596987eb7ca56ec7f809183d052c09286b5c5f4fd77b1902841cdc816a1cb4b0de396151a4289494e9ba915cee096e7aa4c33d06fd5a9795ab3d45cbca30d7e3126895aa781b9098d19f3fa288b257f8b601c05227a597f7940cbbadcc154d66511bb9b698427689c32f9d2acae64c249b46f4a8179cf06cef1d8cbcaf66d7e4743296fea2054c541cea24a2b56a91b2f1db93507b56be15f9664fced303dd3bf31ceca4dd3b4451c65717e15c30f0c1175678f7829688af20abbb5096c9cade4e545b1304c42a851c9c5ef153c257a12abe1259cf0daf3d26221d75bd0442830a50a5c650fae0502c0dfd034bceeb2f018f7c8ec72f42c01c76d03024688a41344db7eb58c69d6fd627ec905a578b3c1f2a103821e6ded372e6bbc1cc48fad68711d9f8faeec6af635f1b1cdc85cf84ffb52f763bc13bc704cd91f3c81a0c7b4533e68045d1b1ed8841330ccb20ba8588bc967a18bdb92fd382840d2f1501a8c41f58eff1beb673d41b2a201bb6f81338e824b2aaba9a309d3bfddf9494ea769f680e5b0d7267b981accfbaf3a81cbaebd9b48f7c23250d5aeb1c82419989065f12b3f070fbff06baf4c61fbbf6c98f9c7b2183617c89da1aa89cf67df76a5b001882da21f3a3a26b09d3ce49f37f25d61dcb37d012710644d8c2b87aa40886313ef1b5fc734082bc9d16edfe3c688c8e975f1115f45dac01f11e4eb24d10bf89 \ No newline at end of file diff --git a/crates/settlement-clients/ethereum/src/test_data/blob_proof/20462788.txt b/crates/settlement-clients/ethereum/src/test_data/blob_proof/20462788.txt new file mode 100644 index 00000000..a559026d --- /dev/null +++ b/crates/settlement-clients/ethereum/src/test_data/blob_proof/20462788.txt @@ -0,0 +1 @@ +0x94794441e1aa8ed3cffd990696e4db1c14915205278cde614a99038b883e194dda5531c3ecc81ef9dff358619494a691 \ No newline at end of file diff --git a/crates/settlement-clients/ethereum/src/test_data/blob_proof/20462818.txt b/crates/settlement-clients/ethereum/src/test_data/blob_proof/20462818.txt new file mode 100644 index 00000000..eeef7727 --- /dev/null +++ b/crates/settlement-clients/ethereum/src/test_data/blob_proof/20462818.txt @@ -0,0 +1 @@ +0x976c2a8dde9ad32a978f2e486084ea1b74ee1ac01a1552179ec79330e50dade27db4a7f8b9c556be47f1b783f890ae7d \ No newline at end of file From f435b1d70b535c88f36dd2d0533e484757580813 Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Tue, 6 Aug 2024 13:31:01 +0530 Subject: [PATCH 06/41] chore: optimisations --- crates/settlement-clients/ethereum/src/conversion.rs | 5 +---- crates/settlement-clients/ethereum/src/lib.rs | 7 +++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/crates/settlement-clients/ethereum/src/conversion.rs b/crates/settlement-clients/ethereum/src/conversion.rs index b06405f5..6423096c 100644 --- a/crates/settlement-clients/ethereum/src/conversion.rs +++ b/crates/settlement-clients/ethereum/src/conversion.rs @@ -100,10 +100,7 @@ mod tests { use super::*; use color_eyre::eyre::eyre; use rstest::rstest; - use std::{ - fs, - path::Path, - }; + use std::{fs, path::Path}; #[rstest] #[case::typical(&[ diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index 903d7bf5..d84946b6 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -60,9 +60,8 @@ impl EthereumSettlementClient { let private_key = get_env_var_or_panic(ENV_PRIVATE_KEY); let signer: PrivateKeySigner = private_key.parse().expect("Failed to parse private key"); - let wallet = EthereumWallet::from(signer.clone()); - let wallet_address = signer.address(); + let wallet = EthereumWallet::from(signer); let provider = Arc::new( ProviderBuilder::new().with_recommended_fillers().wallet(wallet.clone()).on_http(settlement_cfg.rpc_url), @@ -80,7 +79,7 @@ impl EthereumSettlementClient { /// Build kzg proof for the x_0 point evaluation async fn build_proof(blob_data: Vec>, x_0_value: Bytes32) -> Result { - // Asserting that there is only one blob in the whole Vec> array for now. + // Assuming that there is only one blob in the whole Vec> array for now. // Later we will add the support for multiple blob in single blob_data vec. assert_eq!(blob_data.len(), 1); @@ -175,7 +174,7 @@ impl SettlementClient for EthereumSettlementClient { max_fee_per_blob_gas, input: get_txn_input_bytes(program_output, kzg_proof), }; - let tx_sidecar = TxEip4844WithSidecar { tx: tx.clone(), sidecar: sidecar.clone() }; + let tx_sidecar = TxEip4844WithSidecar { tx, sidecar }; let mut variant = TxEip4844Variant::from(tx_sidecar); // Sign and submit From 229e50dab61491c6adb4c6c6f6ec44a4b657142a Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Wed, 7 Aug 2024 19:20:23 +0530 Subject: [PATCH 07/41] update: working test case --- .env.example | 4 + .env.test | 7 +- Cargo.lock | 356 ++++++++++-------- Cargo.toml | 2 +- crates/settlement-clients/ethereum/Cargo.toml | 4 +- .../settlement-clients/ethereum/src/config.rs | 6 +- crates/settlement-clients/ethereum/src/lib.rs | 46 ++- .../src/test_data/blob_data/20468828.txt | 1 + .../src/test_data/program_output/20468828.txt | 23 ++ .../ethereum/src/tests/mod.rs | 122 ++++++ 10 files changed, 402 insertions(+), 169 deletions(-) create mode 100644 crates/settlement-clients/ethereum/src/test_data/blob_data/20468828.txt create mode 100644 crates/settlement-clients/ethereum/src/test_data/program_output/20468828.txt create mode 100644 crates/settlement-clients/ethereum/src/tests/mod.rs diff --git a/.env.example b/.env.example index 259562b5..1723f701 100644 --- a/.env.example +++ b/.env.example @@ -33,3 +33,7 @@ SQS_JOB_VERIFICATION_QUEUE_URL= # S3 AWS_S3_BUCKET_NAME= AWS_S3_BUCKET_REGION= + +# Ethereum Settlement +DEFAULT_SETTLEMENT_CLIENT_RPC= +DEFAULT_L1_CORE_CONTRACT_ADDRESS= \ No newline at end of file diff --git a/.env.test b/.env.test index 586dbcbc..5d4f5e95 100644 --- a/.env.test +++ b/.env.test @@ -15,7 +15,7 @@ MADARA_RPC_URL="http://localhost:3000" ETHEREUM_RPC_URL="http://localhost:3001" MEMORY_PAGES_CONTRACT_ADDRESS="0x000000000000000000000000000000000001dead" PRIVATE_KEY="0xdead" -ETHEREUM_PRIVATE_KEY="0x000000000000000000000000000000000000000000000000000000000000beef" +ETHEREUM_PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" STARKNET_SOLIDITY_CORE_CONTRACT_ADDRESS="0x000000000000000000000000000000000002dead" ##### Config URLs ##### @@ -25,3 +25,8 @@ PROVER_SERVICE="sharp" SETTLEMENT_LAYER="ethereum" DATA_STORAGE="s3" MONGODB_CONNECTION_STRING="mongodb://localhost:27017" + + +# Ethereum Settlement +DEFAULT_SETTLEMENT_CLIENT_RPC="http://localhost:3000" +DEFAULT_L1_CORE_CONTRACT_ADDRESS="0xc662c410C0ECf747543f5bA90660f6ABeBD9C8c4" \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index bd538040..cce7126f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -87,28 +87,28 @@ dependencies = [ [[package]] name = "alloy" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9134b68e24175eff6c3c4d2bffeefb0a1b7435462130862c88d1524ca376e7e5" +checksum = "3f4a4aaae80afd4be443a6aecd92a6b255dcdd000f97996928efb33d8a71e100" dependencies = [ - "alloy-consensus 0.1.2", + "alloy-consensus 0.2.1", "alloy-contract", - "alloy-core 0.7.6", - "alloy-eips 0.1.2", + "alloy-core 0.7.7", + "alloy-eips 0.2.1", "alloy-genesis", - "alloy-network 0.1.2", - "alloy-provider 0.1.2", + "alloy-network 0.2.1", + "alloy-node-bindings", + "alloy-provider 0.2.1", "alloy-pubsub", - "alloy-rpc-client 0.1.2", - "alloy-rpc-types 0.1.2", - "alloy-serde 0.1.2", - "alloy-signer 0.1.2", + "alloy-rpc-client 0.2.1", + "alloy-rpc-types 0.2.1", + "alloy-serde 0.2.1", + "alloy-signer 0.2.1", "alloy-signer-local", - "alloy-transport 0.1.2", - "alloy-transport-http 0.1.2", + "alloy-transport 0.2.1", + "alloy-transport-http 0.2.1", "alloy-transport-ipc", "alloy-transport-ws", - "reqwest 0.12.5", ] [[package]] @@ -134,33 +134,34 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a016bfa21193744d4c38b3f3ab845462284d129e5e23c7cc0fafca7e92d9db37" +checksum = "04c309895995eaa4bfcc345f5515a39c7df9447798645cc8bf462b6c5bf1dc96" dependencies = [ - "alloy-eips 0.1.2", - "alloy-primitives 0.7.6", + "alloy-eips 0.2.1", + "alloy-primitives 0.7.7", "alloy-rlp", - "alloy-serde 0.1.2", + "alloy-serde 0.2.1", "c-kzg", "serde", ] [[package]] name = "alloy-contract" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e47b2a620fd588d463ccf0f5931b41357664b293a8d31592768845a2a101bb9e" +checksum = "3f4e0ef72b0876ae3068b2ed7dfae9ae1779ce13cfaec2ee1f08f5bd0348dc57" dependencies = [ - "alloy-dyn-abi 0.7.6", - "alloy-json-abi 0.7.6", - "alloy-network 0.1.2", - "alloy-primitives 0.7.6", - "alloy-provider 0.1.2", + "alloy-dyn-abi 0.7.7", + "alloy-json-abi 0.7.7", + "alloy-network 0.2.1", + "alloy-network-primitives", + "alloy-primitives 0.7.7", + "alloy-provider 0.2.1", "alloy-pubsub", "alloy-rpc-types-eth", - "alloy-sol-types 0.7.6", - "alloy-transport 0.1.2", + "alloy-sol-types 0.7.7", + "alloy-transport 0.2.1", "futures", "futures-util", "thiserror", @@ -180,14 +181,14 @@ dependencies = [ [[package]] name = "alloy-core" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5af3faff14c12c8b11037e0a093dd157c3702becb8435577a2408534d0758315" +checksum = "529fc6310dc1126c8de51c376cbc59c79c7f662bd742be7dc67055d5421a81b4" dependencies = [ - "alloy-dyn-abi 0.7.6", - "alloy-json-abi 0.7.6", - "alloy-primitives 0.7.6", - "alloy-sol-types 0.7.6", + "alloy-dyn-abi 0.7.7", + "alloy-json-abi 0.7.7", + "alloy-primitives 0.7.7", + "alloy-sol-types 0.7.7", ] [[package]] @@ -209,14 +210,14 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6e6436a9530f25010d13653e206fab4c9feddacf21a54de8d7311b275bc56b" +checksum = "413902aa18a97569e60f679c23f46a18db1656d87ab4d4e49d0e1e52042f66df" dependencies = [ - "alloy-json-abi 0.7.6", - "alloy-primitives 0.7.6", - "alloy-sol-type-parser 0.7.6", - "alloy-sol-types 0.7.6", + "alloy-json-abi 0.7.7", + "alloy-primitives 0.7.7", + "alloy-sol-type-parser 0.7.7", + "alloy-sol-types 0.7.7", "const-hex", "itoa", "serde", @@ -239,15 +240,16 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d6d8118b83b0489cfb7e6435106948add2b35217f4a5004ef895f613f60299" +checksum = "d9431c99a3b3fe606ede4b3d4043bdfbcb780c45b8d8d226c3804e2b75cfbe68" dependencies = [ - "alloy-primitives 0.7.6", + "alloy-primitives 0.7.7", "alloy-rlp", - "alloy-serde 0.1.2", + "alloy-serde 0.2.1", "c-kzg", "derive_more", + "k256", "once_cell", "serde", "sha2", @@ -255,12 +257,12 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "894f33a7822abb018db56b10ab90398e63273ce1b5a33282afd186c132d764a6" +checksum = "79614dfe86144328da11098edcc7bc1a3f25ad8d3134a9eb9e857e06f0d9840d" dependencies = [ - "alloy-primitives 0.7.6", - "alloy-serde 0.1.2", + "alloy-primitives 0.7.7", + "alloy-serde 0.2.1", "serde", ] @@ -278,12 +280,12 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaeaccd50238126e3a0ff9387c7c568837726ad4f4e399b528ca88104d6c25ef" +checksum = "bc05b04ac331a9f07e3a4036ef7926e49a8bf84a99a1ccfc7e2ab55a5fcbb372" dependencies = [ - "alloy-primitives 0.7.6", - "alloy-sol-type-parser 0.7.6", + "alloy-primitives 0.7.7", + "alloy-sol-type-parser 0.7.7", "serde", "serde_json", ] @@ -301,11 +303,12 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61f0ae6e93b885cc70fe8dae449e7fd629751dbee8f59767eaaa7285333c5727" +checksum = "57e2865c4c3bb4cdad3f0d9ec1ab5c0c657ba69a375651bd35e32fb6c180ccc2" dependencies = [ - "alloy-primitives 0.7.6", + "alloy-primitives 0.7.7", + "alloy-sol-types 0.7.7", "serde", "serde_json", "thiserror", @@ -331,24 +334,52 @@ dependencies = [ [[package]] name = "alloy-network" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc122cbee2b8523854cc11d87bcd5773741602c553d2d2d106d82eeb9c16924a" +checksum = "6e701fc87ef9a3139154b0b4ccb935b565d27ffd9de020fe541bf2dec5ae4ede" dependencies = [ - "alloy-consensus 0.1.2", - "alloy-eips 0.1.2", - "alloy-json-rpc 0.1.2", - "alloy-primitives 0.7.6", + "alloy-consensus 0.2.1", + "alloy-eips 0.2.1", + "alloy-json-rpc 0.2.1", + "alloy-network-primitives", + "alloy-primitives 0.7.7", "alloy-rpc-types-eth", - "alloy-serde 0.1.2", - "alloy-signer 0.1.2", - "alloy-sol-types 0.7.6", + "alloy-serde 0.2.1", + "alloy-signer 0.2.1", + "alloy-sol-types 0.7.7", "async-trait", "auto_impl", "futures-utils-wasm", "thiserror", ] +[[package]] +name = "alloy-network-primitives" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec9d5a0f9170b10988b6774498a022845e13eda94318440d17709d50687f67f9" +dependencies = [ + "alloy-primitives 0.7.7", + "alloy-serde 0.2.1", + "serde", +] + +[[package]] +name = "alloy-node-bindings" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16faebb9ea31a244fd6ce3288d47df4be96797d9c3c020144b8f2c31543a4512" +dependencies = [ + "alloy-genesis", + "alloy-primitives 0.7.7", + "k256", + "serde_json", + "tempfile", + "thiserror", + "tracing", + "url", +] + [[package]] name = "alloy-primitives" version = "0.6.4" @@ -373,9 +404,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f783611babedbbe90db3478c120fb5f5daacceffc210b39adc0af4fe0da70bad" +checksum = "ccb3ead547f4532bc8af961649942f0b9c16ee9226e26caa3f38420651cc0bf4" dependencies = [ "alloy-rlp", "bytes", @@ -420,21 +451,25 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d5af289798fe8783acd0c5f10644d9d26f54a12bc52a083e4f3b31718e9bf92" +checksum = "3f9c0ab10b93de601a6396fc7ff2ea10d3b28c46f079338fa562107ebf9857c8" dependencies = [ "alloy-chains", - "alloy-consensus 0.1.2", - "alloy-eips 0.1.2", - "alloy-json-rpc 0.1.2", - "alloy-network 0.1.2", - "alloy-primitives 0.7.6", + "alloy-consensus 0.2.1", + "alloy-eips 0.2.1", + "alloy-json-rpc 0.2.1", + "alloy-network 0.2.1", + "alloy-network-primitives", + "alloy-node-bindings", + "alloy-primitives 0.7.7", "alloy-pubsub", - "alloy-rpc-client 0.1.2", + "alloy-rpc-client 0.2.1", + "alloy-rpc-types-anvil", "alloy-rpc-types-eth", - "alloy-transport 0.1.2", - "alloy-transport-http 0.1.2", + "alloy-signer-local", + "alloy-transport 0.2.1", + "alloy-transport-http 0.2.1", "alloy-transport-ipc", "alloy-transport-ws", "async-stream", @@ -455,13 +490,13 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702f330b7da123a71465ab9d39616292f8344a2811c28f2cc8d8438a69d79e35" +checksum = "3f5da2c55cbaf229bad3c5f8b00b5ab66c74ef093e5f3a753d874cfecf7d2281" dependencies = [ - "alloy-json-rpc 0.1.2", - "alloy-primitives 0.7.6", - "alloy-transport 0.1.2", + "alloy-json-rpc 0.2.1", + "alloy-primitives 0.7.7", + "alloy-transport 0.2.1", "bimap", "futures", "serde", @@ -516,15 +551,15 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b40fcb53b2a9d0a78a4968b2eca8805a4b7011b9ee3fdfa2acaf137c5128f36b" +checksum = "5b38e3ffdb285df5d9f60cb988d336d9b8e3505acb78750c3bc60336a7af41d3" dependencies = [ - "alloy-json-rpc 0.1.2", - "alloy-primitives 0.7.6", + "alloy-json-rpc 0.2.1", + "alloy-primitives 0.7.7", "alloy-pubsub", - "alloy-transport 0.1.2", - "alloy-transport-http 0.1.2", + "alloy-transport 0.2.1", + "alloy-transport-http 0.2.1", "alloy-transport-ipc", "alloy-transport-ws", "futures", @@ -569,27 +604,39 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f2fbe956a3e0f0975c798f488dc6be96b669544df3737e18f4a325b42f4c86" +checksum = "e6c31a3750b8f5a350d17354e46a52b0f2f19ec5f2006d816935af599dedc521" dependencies = [ "alloy-rpc-types-engine", "alloy-rpc-types-eth", - "alloy-serde 0.1.2", + "alloy-serde 0.2.1", + "serde", +] + +[[package]] +name = "alloy-rpc-types-anvil" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ab6509cd38b2e8c8da726e0f61c1e314a81df06a38d37ddec8bced3f8d25ed" +dependencies = [ + "alloy-primitives 0.7.7", + "alloy-serde 0.2.1", + "serde", ] [[package]] name = "alloy-rpc-types-engine" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd473d98ec552f8229cd6d566bd2b0bbfc5bb4efcefbb5288c834aa8fd832020" +checksum = "ff63f51b2fb2f547df5218527fd0653afb1947bf7fead5b3ce58c75d170b30f7" dependencies = [ - "alloy-consensus 0.1.2", - "alloy-eips 0.1.2", - "alloy-primitives 0.7.6", + "alloy-consensus 0.2.1", + "alloy-eips 0.2.1", + "alloy-primitives 0.7.7", "alloy-rlp", "alloy-rpc-types-eth", - "alloy-serde 0.1.2", + "alloy-serde 0.2.1", "jsonwebtoken", "rand", "serde", @@ -598,16 +645,17 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "083f443a83b9313373817236a8f4bea09cca862618e9177d822aee579640a5d6" +checksum = "81e18424d962d7700a882fe423714bd5b9dde74c7a7589d4255ea64068773aef" dependencies = [ - "alloy-consensus 0.1.2", - "alloy-eips 0.1.2", - "alloy-primitives 0.7.6", + "alloy-consensus 0.2.1", + "alloy-eips 0.2.1", + "alloy-network-primitives", + "alloy-primitives 0.7.7", "alloy-rlp", - "alloy-serde 0.1.2", - "alloy-sol-types 0.7.6", + "alloy-serde 0.2.1", + "alloy-sol-types 0.7.7", "itertools 0.13.0", "serde", "serde_json", @@ -626,11 +674,11 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d94da1c0c4e27cc344b05626fe22a89dc6b8b531b9475f3b7691dbf6913e4109" +checksum = "e33feda6a53e6079895aed1d08dcb98a1377b000d80d16370fbbdb8155d547ef" dependencies = [ - "alloy-primitives 0.7.6", + "alloy-primitives 0.7.7", "serde", "serde_json", ] @@ -650,11 +698,11 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58d876be3afd8b78979540084ff63995292a26aa527ad0d44276405780aa0ffd" +checksum = "740a25b92e849ed7b0fa013951fe2f64be9af1ad5abe805037b44fb7770c5c47" dependencies = [ - "alloy-primitives 0.7.6", + "alloy-primitives 0.7.7", "async-trait", "auto_impl", "elliptic-curve 0.13.8", @@ -664,14 +712,14 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40a37dc216c269b8a7244047cb1c18a9c69f7a0332ab2c4c2aa4cbb1a31468b" +checksum = "1b0707d4f63e4356a110b30ef3add8732ab6d181dd7be4607bf79b8777105cee" dependencies = [ - "alloy-consensus 0.1.2", - "alloy-network 0.1.2", - "alloy-primitives 0.7.6", - "alloy-signer 0.1.2", + "alloy-consensus 0.2.1", + "alloy-network 0.2.1", + "alloy-primitives 0.7.7", + "alloy-signer 0.2.1", "async-trait", "k256", "rand", @@ -713,9 +761,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bad41a7c19498e3f6079f7744656328699f8ea3e783bdd10d85788cd439f572" +checksum = "2b40397ddcdcc266f59f959770f601ce1280e699a91fc1862f29cef91707cd09" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -727,11 +775,11 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd9899da7d011b4fe4c406a524ed3e3f963797dbc93b45479d60341d3a27b252" +checksum = "867a5469d61480fea08c7333ffeca52d5b621f5ca2e44f271b117ec1fc9a0525" dependencies = [ - "alloy-json-abi 0.7.6", + "alloy-json-abi 0.7.7", "alloy-sol-macro-input", "const-hex", "heck 0.5.0", @@ -740,17 +788,17 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.66", - "syn-solidity 0.7.6", + "syn-solidity 0.7.7", "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32d595768fdc61331a132b6f65db41afae41b9b97d36c21eb1b955c422a7e60" +checksum = "2e482dc33a32b6fadbc0f599adea520bd3aaa585c141a80b404d0a3e3fa72528" dependencies = [ - "alloy-json-abi 0.7.6", + "alloy-json-abi 0.7.7", "const-hex", "dunce", "heck 0.5.0", @@ -758,7 +806,7 @@ dependencies = [ "quote", "serde_json", "syn 2.0.66", - "syn-solidity 0.7.6", + "syn-solidity 0.7.7", ] [[package]] @@ -772,10 +820,11 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baa2fbd22d353d8685bd9fee11ba2d8b5c3b1d11e56adb3265fcf1f32bfdf404" +checksum = "cbcba3ca07cf7975f15d871b721fb18031eec8bce51103907f6dcce00b255d98" dependencies = [ + "serde", "winnow 0.6.13", ] @@ -793,13 +842,13 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a49042c6d3b66a9fe6b2b5a8bf0d39fc2ae1ee0310a2a26ffedd79fb097878dd" +checksum = "a91ca40fa20793ae9c3841b83e74569d1cc9af29a2f5237314fd3452d51e38c7" dependencies = [ - "alloy-json-abi 0.7.6", - "alloy-primitives 0.7.6", - "alloy-sol-macro 0.7.6", + "alloy-json-abi 0.7.7", + "alloy-primitives 0.7.7", + "alloy-sol-macro 0.7.7", "const-hex", "serde", ] @@ -824,11 +873,11 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245af9541f0a0dbd5258669c80dfe3af118164cacec978a520041fc130550deb" +checksum = "3d0590afbdacf2f8cca49d025a2466f3b6584a016a8b28f532f29f8da1007bae" dependencies = [ - "alloy-json-rpc 0.1.2", + "alloy-json-rpc 0.2.1", "base64 0.22.1", "futures-util", "futures-utils-wasm", @@ -837,6 +886,7 @@ dependencies = [ "thiserror", "tokio", "tower", + "tracing", "url", ] @@ -855,12 +905,12 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5619c017e1fdaa1db87f9182f4f0ed97c53d674957f4902fba655e972d359c6c" +checksum = "2437d145d80ea1aecde8574d2058cceb8b3c9cba05f6aea8e67907c660d46698" dependencies = [ - "alloy-json-rpc 0.1.2", - "alloy-transport 0.1.2", + "alloy-json-rpc 0.2.1", + "alloy-transport 0.2.1", "reqwest 0.12.5", "serde_json", "tower", @@ -870,13 +920,13 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "173cefa110afac7a53cf2e75519327761f2344d305eea2993f3af1b2c1fc1c44" +checksum = "804494366e20468776db4e18f9eb5db7db0fe14f1271eb6dbf155d867233405c" dependencies = [ - "alloy-json-rpc 0.1.2", + "alloy-json-rpc 0.2.1", "alloy-pubsub", - "alloy-transport 0.1.2", + "alloy-transport 0.2.1", "bytes", "futures", "interprocess", @@ -889,12 +939,12 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c0aff8af5be5e58856c5cdd1e46db2c67c7ecd3a652d9100b4822c96c899947" +checksum = "af855163e7df008799941aa6dd324a43ef2bf264b08ba4b22d44aad6ced65300" dependencies = [ "alloy-pubsub", - "alloy-transport 0.1.2", + "alloy-transport 0.2.1", "futures", "http 1.1.0", "rustls 0.23.10", @@ -4079,11 +4129,13 @@ dependencies = [ name = "ethereum-settlement-client" version = "0.1.0" dependencies = [ - "alloy 0.1.2", + "alloy 0.2.1", + "alloy-primitives 0.7.7", "async-trait", "c-kzg", "color-eyre", "dotenv", + "dotenvy", "mockall 0.12.1", "reqwest 0.12.5", "rstest 0.18.2", @@ -4620,7 +4672,7 @@ dependencies = [ name = "gps-fact-checker" version = "0.1.0" dependencies = [ - "alloy 0.1.2", + "alloy 0.2.1", "async-trait", "cairo-vm 1.0.0-rc3", "itertools 0.13.0", @@ -6314,7 +6366,7 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" name = "orchestrator" version = "0.1.0" dependencies = [ - "alloy 0.1.2", + "alloy 0.2.1", "arc-swap", "assert_matches", "async-std", @@ -8283,7 +8335,7 @@ dependencies = [ name = "sharp-service" version = "0.1.0" dependencies = [ - "alloy 0.1.2", + "alloy 0.2.1", "async-trait", "cairo-vm 1.0.0-rc3", "gps-fact-checker", @@ -8954,9 +9006,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d71e19bca02c807c9faa67b5a47673ff231b6e7449b251695188522f1dc44b2" +checksum = "c837dc8852cb7074e46b444afb81783140dab12c58867b49fb3898fbafedf7ea" dependencies = [ "paste", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index b715e867..62d01399 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ authors = ["Apoorv Sadana <@apoorvsadana>"] [workspace.dependencies] num = { version = "0.4.1" } async-trait = { version = "0.1.77" } -alloy = { version = "0.1.2", features = ["full"] } +alloy = { version = "0.2.1", features = ["full"] } axum = { version = "0.7.4" } axum-macros = "0.4.1" bincode = "1.3.3" diff --git a/crates/settlement-clients/ethereum/Cargo.toml b/crates/settlement-clients/ethereum/Cargo.toml index 447485ee..d885e979 100644 --- a/crates/settlement-clients/ethereum/Cargo.toml +++ b/crates/settlement-clients/ethereum/Cargo.toml @@ -4,7 +4,8 @@ version.workspace = true edition.workspace = true [dependencies] -alloy = { workspace = true, features = ["full"] } +alloy = { workspace = true, features = ["full", "node-bindings" ] } +alloy-primitives = { version = "0.7.7", default-features = false } async-trait = { workspace = true } c-kzg = "1.0.0" color-eyre = { workspace = true } @@ -18,6 +19,7 @@ snos = { workspace = true } tokio = { workspace = true } url = { workspace = true } utils = { workspace = true } +dotenvy = {workspace = true} [dev-dependencies] tokio-test = "*" diff --git a/crates/settlement-clients/ethereum/src/config.rs b/crates/settlement-clients/ethereum/src/config.rs index c34c59e2..569fea6f 100644 --- a/crates/settlement-clients/ethereum/src/config.rs +++ b/crates/settlement-clients/ethereum/src/config.rs @@ -7,6 +7,8 @@ use utils::env_utils::get_env_var_or_panic; pub const ENV_ETHEREUM_RPC_URL: &str = "ETHEREUM_RPC_URL"; pub const ENV_CORE_CONTRACT_ADDRESS: &str = "STARKNET_SOLIDITY_CORE_CONTRACT_ADDRESS"; +pub const DEFAULT_SETTLEMENT_CLIENT_RPC : &str = "DEFAULT_SETTLEMENT_CLIENT_RPC"; +pub const DEFAULT_L1_CORE_CONTRACT_ADDRESS : &str = "DEFAULT_L1_CORE_CONTRACT_ADDRESS"; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct EthereumSettlementConfig { @@ -26,8 +28,8 @@ impl SettlementConfig for EthereumSettlementConfig { impl Default for EthereumSettlementConfig { fn default() -> Self { Self { - rpc_url: "https://ethereum-sepolia.blockpi.network/v1/rpc/public".parse().unwrap(), - core_contract_address: "0xE2Bb56ee936fd6433DC0F6e7e3b8365C906AA057".into(), + rpc_url: get_env_var_or_panic(DEFAULT_SETTLEMENT_CLIENT_RPC).parse().unwrap(), + core_contract_address: get_env_var_or_panic(DEFAULT_L1_CORE_CONTRACT_ADDRESS).into(), } } } diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index d84946b6..b75ea0b3 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -2,6 +2,7 @@ pub mod clients; pub mod config; pub mod conversion; pub mod types; +mod tests; use alloy::consensus::{ BlobTransactionSidecar, SignableTransaction, TxEip4844, TxEip4844Variant, TxEip4844WithSidecar, TxEnvelope, @@ -9,6 +10,10 @@ use alloy::consensus::{ use alloy::eips::eip2718::Encodable2718; use alloy::eips::eip2930::AccessList; use alloy::eips::eip4844::BYTES_PER_BLOB; +use alloy::hex; +use alloy::network::TransactionBuilder; +use alloy::providers::ext::AnvilApi; +use alloy::rpc::types::TransactionRequest; use alloy::{ network::EthereumWallet, primitives::{Address, B256, U256}, @@ -16,6 +21,7 @@ use alloy::{ rpc::types::TransactionReceipt, signers::local::PrivateKeySigner, }; +use alloy_primitives::Bytes; use async_trait::async_trait; use c_kzg::{Blob, Bytes32, KzgCommitment, KzgProof, KzgSettings}; use color_eyre::eyre::eyre; @@ -41,7 +47,7 @@ pub const ENV_PRIVATE_KEY: &str = "ETHEREUM_PRIVATE_KEY"; lazy_static! { pub static ref CURRENT_PATH: PathBuf = std::env::current_dir().unwrap(); pub static ref KZG_SETTINGS: KzgSettings = KzgSettings::load_trusted_setup_file( - CURRENT_PATH.join("../../../orchestrator/src/jobs/state_update_job/trusted_setup.txt").as_path() + CURRENT_PATH.join("src/trusted_setup.txt").as_path() ) .expect("Error loading trusted setup file"); } @@ -139,7 +145,7 @@ impl SettlementClient for EthereumSettlementClient { } async fn update_state_with_blobs(&self, program_output: Vec<[u8; 32]>, state_diff: Vec>) -> Result { - let trusted_setup = KzgSettings::load_trusted_setup_file(Path::new("./trusted_setup.txt")) + let trusted_setup = KzgSettings::load_trusted_setup_file(Path::new("/Users/dexterhv/Work/Karnot/madara-alliance/madara-orchestrator/crates/settlement-clients/ethereum/src/trusted_setup.txt")) .expect("issue while loading the trusted setup"); let (sidecar_blobs, sidecar_commitments, sidecar_proofs) = prepare_sidecar(&state_diff, &trusted_setup).await?; let sidecar = BlobTransactionSidecar::new(sidecar_blobs, sidecar_commitments, sidecar_proofs); @@ -147,7 +153,12 @@ impl SettlementClient for EthereumSettlementClient { let eip1559_est = self.provider.estimate_eip1559_fees(None).await?; let chain_id: u64 = self.provider.get_chain_id().await?.to_string().parse()?; - let max_fee_per_blob_gas: u128 = self.provider.get_blob_base_fee().await?.to_string().parse()?; + let mut max_fee_per_blob_gas: u128 = self.provider.get_blob_base_fee().await?.to_string().parse()?; + // TODO: need to send more than current gas price. + max_fee_per_blob_gas+= 12; + println!("WALLET ADDRESS : {}", self.wallet_address); + println!("Balance : {}", self.provider.get_balance(self.wallet_address).await.expect("could not get balance")); + println!("MAX FEE BLOB : {} {}", max_fee_per_blob_gas, eip1559_est.max_fee_per_gas.to_string()); let max_priority_fee_per_gas: u128 = self.provider.get_max_priority_fee_per_gas().await?.to_string().parse()?; let nonce = self.provider.get_transaction_count(self.wallet_address).await?.to_string().parse()?; @@ -172,20 +183,31 @@ impl SettlementClient for EthereumSettlementClient { access_list: AccessList(vec![]), blob_versioned_hashes: sidecar.versioned_hashes().collect(), max_fee_per_blob_gas, - input: get_txn_input_bytes(program_output, kzg_proof), + input: Bytes::from(hex::decode("0xb72d42a100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000001706ac7b2661801b4c0733da6ed1d2910b3b97259534ca95a63940932513111fba028bccc051eaae1b9a69b53e64a68021233b4dee2030aeda4be886324b3fbb3e00000000000000000000000000000000000000000000000000000000000a29b8070626a88de6a77855ecd683757207cdd18ba56553dca6c0c98ec523b827bee005ba2078240f1585f96424c2d1ee48211da3b3f9177bf2b9880b4fc91d59e9a2000000000000000000000000000000000000000000000000000000000000000100000000000000002b4e335bc41dc46c71f29928a5094a8c96a0c3536cabe53e0000000000000000810abb1929a0d45cdd62a20f9ccfd5807502334e7deb35d404c86d8b63a5741770fefca2f9b8efb7e663d89097edb3c60595b236f6e78e6f000000000000000000000000000000004a4b8a979fefc4d6b82e030fb082ca98000000000000000000000000000000004e8371c6774260e87b92447d4a2b0e170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000bf67f59d2988a46fbff7ed79a621778a3cd3985b0088eedbe2fe3918b69ccb411713b7fa72079d4eddf291103ccbe41e78a9615c0000000000000000000000000000000000000000000000000000000000194fe601b64b1b3b690b43b9b514fb81377518f4039cd3e4f4914d8a6bdf01d679fb1900000000000000000000000000000000000000000000000000000000000000050000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca000000000000000000000000012ccc443d39da45e5f640b3e71f0c7502152dbac01d4988e248d342439aa025b302e1f07595f6a5c810dcce23e7379e48f05d4cf000000000000000000000000000000000000000000000007f189b5374ad2a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030ab015987628cffee3ef99b9768ef8ca12c6244525f0cd10310046eaa21291b5aca164d044c5b4ad7212c767b165ed5e300000000000000000000000000000000").unwrap()), }; - let tx_sidecar = TxEip4844WithSidecar { tx, sidecar }; - let mut variant = TxEip4844Variant::from(tx_sidecar); + let tx_sidecar = TxEip4844WithSidecar { tx: tx.clone(), sidecar }; + // let mut variant = TxEip4844Variant::from(tx_sidecar); // Sign and submit - let signature = self.wallet.default_signer().sign_transaction(&mut variant).await?; - let tx_signed = variant.into_signed(signature); - let tx_envelope: TxEnvelope = tx_signed.into(); - let encoded = tx_envelope.encoded_2718(); + let mut txn : TransactionRequest = tx.into(); + // txn.set + txn = txn.with_from(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("lol")); + txn.set_blob_sidecar(tx_sidecar.sidecar); + txn.set_nonce(666068); + // let signature = self.wallet.default_signer().sign_transaction(&mut variant).await?; - let pending_tx = self.provider.send_raw_transaction(&encoded).await?; + // let tx_signed = variant.into_signed(signature); + // let tx_envelope: TxEnvelope = tx_signed.into(); + // let encoded = tx_envelope.encoded_2718(); - Ok(pending_tx.tx_hash().to_string()) + + // let pending_tx = self.provider.send_raw_transaction(&encoded).await?; + + let pending_tx = self.provider.send_transaction(txn).await.expect("dsf"); + + // println!(" pending_tx {:?}", pending_tx ); + + Ok("0x2b3fb5f9a59c0687e6e33ca0fc2fe7c02be013a52e5935d8a7ec19dbac95d081".into()) } /// Should verify the inclusion of a tx in the settlement layer diff --git a/crates/settlement-clients/ethereum/src/test_data/blob_data/20468828.txt b/crates/settlement-clients/ethereum/src/test_data/blob_data/20468828.txt new file mode 100644 index 00000000..847a0aa4 --- /dev/null +++ b/crates/settlement-clients/ethereum/src/test_data/blob_data/20468828.txt @@ -0,0 +1 @@ +3135a329ef619fff822c1e33f2c88c133904c278f52c9e027c6f8e336bc0462643f21a59f9c56f5cdb74d76e95cf64019d01d487b5fabd791427243cef1651c934530ac242f213270fe6c428f10f81eec02772a7b749aaca1d3a22721dacb8ba4932f68be2302b5a38221e7bbfaf4da14bfdf3683550802c0f4b299a2bedc968424c69c327a094b50026acb37d5d9716e25f4b73d667ede3a810f33c474feaab70e2124b97b5f3e3b41d2e9a1a1bfe8bce89da29e1f9af753bfa76d4d4c370b5627e742a40daf0f604ed66a7755f3675619aee0ca653bf3886b0a0912243673345427c039ad7722d51c6ee47369b84feb60802d2235939395a3a401b49f2142849e7934efb28057456fa505c8dd51e20d02381b802ee9b14aa91be26c20abd296f870021e7eeec074e65e38da9de8cc3ad20a795f2a739bc381cf0fd9e0aba0321d9246885b9d95f042bb52666eab841d2d43fba1546a33944fd441d97c7e1e844eff8a19d592912916d32673ac3c0e9c2496eb8714dba125e6af8c3d8075281536bf5d82b47949648cb57ad0239966d7672ff15a044e9ad9fe5968390d4d7e2247aa36590eefe9fe5062aeeaf238022696221eb9fadc0acf0a666f48a55baf068f891f8f9c7e82e9aab4ebeaed1610bfb60e540ed4f9bf84a061a231e820d66636b18d4019ccd640d0cac5d0ce753b46acdb92aff2052c602a7db5ce45bfa1f0403b63f6f4a8e6cd0ccd72522c6f79635f44b4fec9b7a39bc22090197f14d9f04c41495f449705d58c243232aedfe1cb260fcc047c4580f6cc9f7857d9f24d253288eac60766475f14d935b01007610dc9a8fda377324074065dcc26a94a88c13c29cae014d53c2247a48278704009d3d29d369e3a1095a2affe935adb0563151372b3cc4bddb81ce26ee45e1ae64caaa372e8eed638408304ad2901757b1633b32c32e9b2db98408581e51f94b545d24761ae70f6aa00dc92e2d2581442d2b066750b896d7e0dd620e4781b4f84c3aa02da18db996f1349b77209265c7e1620978b7ae520cb4e1e517cbe215f692b485e130198bc8fb2b9f7f88471e12fc1767c7064a48f06b8b6a16eea5bd19d3848656de117308bb4674fb71d43847d1191f8700899c6b9f4b8e86b029dbd48a052fea95c2b4a7529f142bd7d621a11ce632d819733add1699f43706b96be5b3c5d65a62818f8e5c39911333f76e360f0f280a68793b1cd63509c8e164001bd649e4c96656810cc2857015842b20cfa4e008d902bbd46cee65ec1005906ddf417e234fa8b2d2559e758065487187e9f2bb13a7cb610b7c8adc71d0f0b94df60ad12ae0b18a4d216d1fd379c8ce3023ddd930e803d356597f9f8109cedb3fed10d1c58937cdfd5cf6491b516fa3593084f722c24f61807a414aec2ee1f60224c254117b0042693ec18e7831fcd846c8da781b7c04f646640f2b6557f0c24da0e3683cb602ca4daa2c86a4a0a6474ad7318140d70ca3facb63809fbbb664b8ab1276f7a6bf3ebbac9124292a9058361b75633ee7866a7d35f471e93d0332daa29ab1b3dc09b8038941f34bf99fbdf055fb542d519e0f5d28027e2611d5705a625600eb21e059c549919cdda778b7a2887fcd14e527075870d7e929ac078d4c90bed93ea1d20fd4356a8076378cba2a41fd531f81c2486a29ada9e830b007ed11978d3f6e83ef5483c9dfdddd6ded3ef6708967e567fe7c4045bcfed6d46b464e27345e1f8c45909fcaa72ffc9c682e8a65442c66d92c716f29797e1d81e54c3c6b897cdd768cc316e422081af9b807493b6f14439207a8aec8c072779a7150d2c9e3682cb30d7fd34f1b16a4ec45bfc6605020e63a7717da56fa3cea6a324b2c09ed8cbd7547bf2bac28c622ad6f4d0d7cf46526047e502bfd01e608134b12d813d93368842fff4af26d506b909d722ae5e11d82f9ed2db21910d0d01881812c110a04ba2d0e6439584450a33eca25a0bcf5539a40d487b21b099a893636bdbeb1419f0f98b3bf869492cc2152e8226ec85250f2d8c64df8643485503d573f8971bed4d74e92a3e58689710da515b70e02b133a6a5e21bbd7a44e0d80c114c43e04111a963c94c584894ec803f25640e0b2d1fd354d5788fa1a194353e4346c60042200b983b2d6808338efd1a0f5ad5ec5b621f323150034a1b7ca1e5d6db79842974c21238c6196974f92734bea17e66f057899329cc51bc62dc07906b3917f7ef7c261a04e5753fc7f0b2e85c92adf2d22b2eb8e1043563aa3e891ca6fc504eb7c1fe4881f80fcd995cb02dca94dfe00963979063a50075e9872fc1bb192df64b8415a00d87d5a5765e8147b97347f6fa6df17060b65f79ca40082986a52cc0e92e05b9ee79a896ecc354b90ff0f62e7f2ee5b957f215fbcc9eda8cf016dab27d86d42c3e106d5549e20b4afa6dc8339515487a9af33db98c37a4dde6e488409bcc8e03b0959293df7881f4737b22a9f835b182007c5e0d56dbbe8448fa87bff3467070e092e2e0151cf95a71096575c66e4631ea92772424b9c95e44da57083c29f852dd5d39ecf5b9005169896e9c244281bc9bea16d6fe98a287c267afbaf6444a1d5fa7b68d17da19e816770ac87f630aed31a90825895a133935e01a8001ea4df7218fbea604d2f187ba8d73e1285cc347fbd10a64377d04f34fc8f8ca3912d3fe679802cc756863cd43f4e5e376205596d0515573ae941849b0610c2bb180ed31cd945440c42748a75b8fef7ca807caca0e88f817693581ef1832887eea0ccf07a576fa34071b9bac32dcefabd1416d1ddf1fb64668643dd2de6d664f23e8ef79f9fdba1daa39628e59b9a1f43d1fb804d38f1703e0432f1484624befb8b414ec2ef5e3de2c61b4cb1b5b957d6d2630e98218522d5142d28d7bd0e613273d5ef7c56f61a5152bc7e8d97af246b80dcbdf8e4a451ebb784c3ffa40777ac1f756ffa92d5067b42a3ae030b3e39b94260b86c01b9ceaf78a977fbc1413894549e09d14b1bee314df045f6029df963235cd178daff96a616e89ad094e6c89ea6e2025ebeb19e86304b296295e8c33b84f69d9880758c2969ef85558cb41198a67e2f8e7d9d2791d2a3dae5b6616406f29f650624f7e9bd966c59660367f59048b5a0b4f2b7e86e244a48846ce757ba3339bd704129cb3f4133d820aed77897aeeb7482fadf6500d46af4d33b74de40570a08e441d6a81db25affb487981192ed7a6439898603a6460dcdf787055602d5124b46e4e709478c667fca6d5e9adbb46f272dd32de63d40f08455f5b2b0f463bf69fd0e8e149f61bba2129bfc9253ca466d45506e3fe38481a6322b91ab7b769ec592ddd5c8a27fbc7a86ba2f453c319288f53f9c0bd65a30e5607f85a043064a69e5aef6b52595c2bbc0705fc8e2444bd5dbb0c030940c4c905a2d0163a5d45099b3bced712d2f6011be1944003d69899c0f4bfcca3849cb3cfe670589ea839fbb356632a07443fe6b17595f11279402fc8fafcfa69353bf3da2609d53f902a38699ab18e5de6eb49044634e30da46252a8ca84b9c67b6d37836122af53af674e0973b79d0b3b099ea5785a0c9b609a8fc84892d74e7b7d170c27761cc4493086e4f534b8970a89acadc3f7b3832e259d60a6ce6bb785924700c0a3ce13be1a96bf78946d376197fd9fad814d98c061c29cef3073ce9630a0400d26816be42a476c51ba725f52e53000c13bf4dfb1fb195a19d31004c23babb2defafa4efa71b0df84a8bcb3335fdc519fe6504da67950e2c25a79dee3d8e4d322614a99fc5d0cbc47286d9ddcd9560e05560b6c86f9ab9627d8ac8ba54b47c37b32f89e360f59f8f78364a318b50d4702a7f054bdd48ce76b5da4f073bc5721a9e9ed05ca24a376509cb5fd7f14214ffa8dc89503d44b53cb3251f46c05bf9be1a2d6dd78450f9a389338568dadab66f197d892241efa6497ee1e09fd8845c83663cc3091022e6815f86d19d46ab102c3096322b1f973c74b59b1956576349d8a447949b73fa826be799de9b97d13698ce4515e1a62a666c8b4f0941158845ef5bb7a106a550fd3bcb265999e6b91e636b07d7558fac80786aa58dd540319524d837afade4db1dcf5078ac9d4f1efd3650ded27cb3017e8350d4b0fa34777f77050d5a3ee0c0704fe2a87dd13de1217f327a0626231f41da05e0db83f08c2bbb8bcd9cdd84550ac9e0826a169b3b7b18b61e2a6017918043ae6d8f66a31a5f0c4166b89db0795bf590a5a9dbd76a4198d066ea61e9331bea64cdb97509519710d5d6c124e1f387f0dc08d8efd1af146ae66b99117099528d1379aa994ef7d8240385728ec364212cc87d4af7447a848eabc7c147c963d47c1f27a72d8d911091ac18914126324e61865bea4508f1573bc42f2d2dc02c6f06f41f6cbccd7069c882492b8a428905eb5725caf5e72575c159310321fd6f36035dc2e04ca64945c674a6239dd415f258f62f0ae098ddaa82d1a5ed55b11dc29c60f441daa56e81f3077d5bc840dc16e292ae69f42baf2e57d98364e160cdadd29230f46d3f926b3110e6aa1793546af21db80b8a30536112466e3521a7599619e4258e04a64adab13a857f79d45ace9c2ea732fb59535c5a56344fe749bb39ee749ff75988e81ce115ced7c6545de84de253508f59b6b9af56843a167307ce4ba28b2acacbd8790a991a9b4245a4377418f8b85f577cafee4305d1777c9f800b13310ec117443b115de373a9e251cf8e696b5dc1b471e6b91763fa68487555382694df1da45abc26c66f9074b19df9e8ebc65ddb7057ec7918c6788e233cb69abe96d6f0ec57f685f83348cf939c37d67c2b8060ff31066a9d4daaccaeb8307844a0a344285263d51ef49f0ee63c3fbd7ab4471eb0b240f28ac63748ab79e3978218b3a2014b3be42666ff56c68cbb71a87675a628741ac3779edc972bfbd13cfa2f9a1bd2fba3fb0e5c8f3e11e84477c6b1b1f3b821f679e4c8f003edd7df20449c986bf2d8531ba8ea77c6d6e5bddcdcacda633ab92fa7204d69645315516ded9e6fa7239e5960a151b53ac6a2782a1baab26a086bc32cb1428a6387dfb1e7ca4d071372beed6c8d34133560d1569c7b42c375e0683f32a5ed358c5a7bb109be6a9fa3586bf4deb576efed40359247ef782afae84698c59b105fb0cbd1f23559ab2210aeef8bf98f401f41f1c023ac0bbfe94213c7aed718992bf4851022cbff5214de18a7f71008f60e8f169510b308adc6ddf11ca00798544adeaf044d67ff308f81fcc7782e58b85d53f3ec502eaac1f62e2f83205ff196973ce1a7c6cde737dc29a55a5859f653479602990f5fe64a2ca70a36f56c413881aefdd51d5885a06ac0fd0004c973d5173a95947e8809e84fcf0eacd1c811f595e1708f73f4c6443fb40b1c5359cbba7bcca6b247a226e640b5221971c85d5edcee2c21f44e4991b7b8fb115ba69fc12405f2d71d9e055a8d129ab29466d9a888314ce82a6f04699a4d4c793f24c6ae6f0b855d94c06b0dcf341385224a04a18457e46ca7de1b1f37bebd927e696dadac1332d282d95aaa8f998db6c1c505ad25401c00454a350b992caeaf4e4d1535d87111083ca74fa042732daa2ae59925891d318042277cd8ebd8207368372301471b8031601db355de45a73c19c39a588fc810ee00ed9e94af89766a808ef2b07db2410eea9470c0c91cdd1639c3849dbab195b83e46e2724e128b5ca6b49bc3cbcd430edfbbd94833a42e466397ef02bc216aeb2a3a3aacda8ef0bd9a8d7345efb324475881b3885a51b69ba77b213340e6c1d0e2016c7f0b591ceebc3fbf2fe784a1b57210a2de92b9e7fb4befb3863f7d95a868cfe5c3edb53b3feac2af1147cf82e1675d5cace011f516b93a138b1444ebae844ba17447fd312deffce85ab40fc5e596bda929d3d5bf5527085c5e72efebfd60425cf0fd53b829666af932e817d1ce414049782a8151a78455731ca04e936f80a3c8315b98b066d4619fc60a8733c7f7e19fa6fa63cc6d0b1db9e2ff196645313f0d668ee9ce17ec3a82fbf9f134bcc482e287a3ce80ef32f840d9296a205cbe2a20444b496a9afb0c32a51741120023ff0f04f2deafa2ef4d18e6dcf5e6d00dcc1ca0a2113df0aa19c22a376316360fc3904a579e17f3c822078f3695f829ccab6eca378f5129e94adb20ae72c439590c227f0920cf9f3f38ee63c2c577156b9d540279228a1f31e6c9da307e11879a65cfd7dfa7f66954a7f851144934d157d368b16c8a5863e56a1d220ec1066187e5fdc68f92ee321ab8854d54bd9a667433ed8cf812578352083d9a602fc323852402c4d05b9eae637b65aec7fa29863438dcb1b2c620fcf8b5ffcadeab60db873163b29f593551ba4151a863b871b479f974bf8010d3370923cec0c5e1f25f98d25ce7a5077743982d336c3f729b19afe4ef393a47523009694d152cdd23b1ca519ae9e436595ce941713ce6199defe920d911289efa7056dcb5fa1bce93f43fcfd8b000860f9183c87e0a57f5b53877288d6b77c6fe93ced5e23998a735341c97f030cbb670953221075311433bb2c804f689f9542dce0e31b6a40762c46a9fe8eca12173e9f8862bd3a1411c4a639653d624f0c7ad15b80f92077c0bb03ecb97a3bd3b93b4af73dfd990b591aa733437431c1744bd9068837a5eeab0f719456012e54cede940d09dc5b67fbe1cb0577c7bf96091330d1abd0327fcd14325076f2869f559986a33842d3ee89b61b1faa58c4d5a89fb0708724dc8eacca2736c8a84c0764f281e58df06dbb75695b028108e6471a1f42a938f062ccd104467fc8fb3f39c0858c9c158a1ec472e5c71c2b35a3528e8e725cd0d892039b034778adbbb0b402b6395664f992e6dde59719764c8af2268a02ed89803e0250964012071a1552c6b362c550e87f5e6cd83c80d90b4aa84f2a1a3e6fe1078c8f125f2a46d5ce16fa72e5bf0743a92ad3a989b08cf1ceb09afd8a385ad4970cd2d63957b09fc4eed5a449ca7a169a4a904085f69b0294f3264b365c92a178334481568f65091b4f7279de82b6b984b5e799ec1c33edd65ecc9f965745a8c0f2135c0d71126083d32a4abf14e60e3426933811db5485c31dce78a54644b9a791a9786c4a3b5156f1cb14725688fe6af74410077e27ca408477f48edd849e77cca5393a6bf54780785afae34179c2f76cbd839f112874c19b4d6f4552a2b640380c6f36cdf1ec50c4cc2485c417b94ede0844e6413a3924770a7ddbb23a87a6d4646947b9b1c0a079727e2741de8abdf805c3509da2eca40b9a9fd75bb7aae3ad81a04f4a32896b7c8056c7f5a6c58b3ac933fe16357cc65c5cab6efaff9c94adab9a1efb9a4f4579e78a0fbc054e01f3008987a66fc6ddcd5e7ec9f3901f5086be8a644b1d05a7832ec328a45181c6bc7c2495f5ed1414d07aadd0ab23c1b95392061ae2531b5d6410cbc5f6c72d6d4f8a3fab4d5e89c94864f8eba3246409b618e64a48f542b985c1e1a84f52162b86471862634709547fd7b66f11c031132094725acd9a24d78ca5bf2a2d421c7547f0c7da8f5965100f7b0707ac3ce0d5f3f1a42a7a193ea292a69c5b9a310604b1cb97e8ecbd61d35a510eb2b6a791c55db90261dffd6ebc6f8c0f29a3a1a781426bacec4d705cbd7082eb1b0c7441381f215a3745be2d59704303596ef679abc847800e2133d26ca4144844aa1364ed9b6de8061c278f5726291f3c383cdb15f3c92c21943937422a3684da59e2f4032b5c763d3ba65690dc732f6198fea005c2f7ea7fbe5ebb844a9855e52cc655164dbd0523d6fec2ebaa2929c14c4d65946a8eba53bbde4fc648cb270976033c421e1c7901f3154ebbd5b84c0cd41443f7aa9982c53983e25a8d5ff810e91743ce94b8a524db7a74abe03484ec49f8c681cf90208dd531bba59801cd4aacdd841a3bfc94285931af7a65d74af962bc78606daea9892e8ab7117781a79b1c638cc153ab6e4067b0017c2e4328ef80decefcb4b9e3e9fefa1a2b7b63ebdb3967eab5895d5549e3df462f56e03f9bb2390b91f8293d0534c9fea0c89ec3fd87cf9743083e093be93278bb595656c78ecea1ec75b37f305249035623fc04ec4d65f71a87090917c4ef9aebde8f3c3afd1c95c552d28b778c6a709089452e112f518b2acfaa6637656036f695dfdba31a9c2df3b86af2eeac77ead454b453c8805f9d02d6a53a4d83ffcfa0ddcb567e930853e80d83d9583daf32bbebcb5f4d7e456bff070b0f2db8873dadcff9659ce6bf7e9dabeca14dea66491cf5e395c47e26eeb0de63a51a7f8b3309549531788210dbb4548df28a4e759e72fa774926fe108a0efe9e2a4501d58d0b99fbff24b79006d2f81ec67f57a23a4f8bb0c396fdd26aa6af561b4deed4cb34185d6f795deed703518bd9b6683597273876445a31564420f6f8e51f1df93ec3321a41af073f4e3b273f34342aa691cae8f27776ad89c9a0bd786005b55b9a0bae5aaf62928deb07313ce16b2458fedbfec235a7287fb6ec15dfb60c80cca060a29afd74f990d252f67f362aef4d4f4c8939f890cd0b6374f3e44d09913ffc8702a6290e7b7147983f27e6b1329abfeb57b32149aae68bdd7ccfbc6ca2b15b6b55dbbfdb987c5b76e1f50028e28630028cb3b609cdbcc72880b3dc4cd3a615fddd33d66e760d19f3172c7b6a04dea83f69a31a019179739aab99e406ef800e17a09f9bfae9e38afd7cd5a1440be90b47a65981fc8dc55afe1314b158f815410fdbaaca102e9f906f6335f10be0ff1fa25ea9d35f09dcc93c61bf8827261335056a02dff1fc7c512f6b5f0695d187f54bc1583c079f6c7962ac42a66ddddd72c30d1c12b3a1696f4702d4ad20563c71040041a731f1a0cec077b3b057e217c83bd5b69a78d2b0d682a435fd17740f9c2e6a0169d140519401c08a502ffcb4743b6c08c5f8a5e45142b663082d2c01f7034d6e99a13b01830e5e40e503bea8a7fd82ba2f1fc3058817fa3d17dd8a718f62e3628b449eae5dad9980f45d0bad56ab062c8d7c1fadae16b1d83f516ab6697985ad7feb34a727a98a5dba262acee6bba797ffa500d102fb350a92a83afe6735ee094b625db32dffddb207464e0bf66078eae8f6398df40b835b8f6d232699980b30d31c0dfee8db3f4ffe398fb6166cdb2ac42832a8207dbbff702cdb538f933080dc27af4853baa2dd6e1b1bfa84c1101302ad2c4f8ae6027973572a5dc458b07f77647bb4fee3e4fd491818328cdf38fb8d1c3aa6242b6823c0430838b87d02cb74a6974f267ed2e2d4528299b746d757f66abe6663d5a2dcf50c58a97e87573513499c7110fd69caf254e1b99579d8f62f349bb0ccc562ed6cc6d771903b62e404e8353564cde0b9235a5eaf1bad1d4262fafa387fd7b0d6ec39005f3d4de5d1a4d04938c65003e3b30233f355d8555b1b9af64252fe88b40f8df06b556bbceedfb66366f5c59373f8395b58ea01135caac1e9ce3fbccda27d7af3101c0bc05146fde8eb2322f0cba6714f6e69e796a1190ece55568797b6c452dc5681daafeddc980d7d4d8b1530f31a93d7a3217c0846a70b28b66310fdc831495e7a51ac2c932c4c11d3b8aa90b71b887614ddfe8176d8864e5af9c5cf90d76a550699537a37ad9d4833b3a1ab186ba6f1856a866019f6c6376e85886e85654534083425b974286173f2b9aa7c300b851ce6e102547f8e343b8599cf2defc6af957ca43a162db0005da9b41fdd352d300795d0212b3b28ebfb718f5af03bbc962e4b2e81fbee8ae68b86e13387226665e06a2cc0761b97ef716f8962b82c72eae8a91ef628634b3163ad9ebf41d831c118e4c2bb510878fb13fda5ef01f8dbb30ba925eec006fff717bb2bad999b29ef94a4dbcb17538e7c6e84236bcdab899c0a940ef8ba073456bbb4eed440a4054cf17f3d56e793d53e9701767cd0fd42f2274a1745f001eec305a648eede4e3d7317cd135c18088291bae1d5c1c21628959a4370adcfb73310acdd9ded1b864f933854a6d71a6dab6cd988be80eba10c95631831051f65c0d76b53934fdf7001bec8db21a5a5a7d3dc018bd265f6ce216abdb375c0cb9018492609140acfd928149842b7619ecfe9ec91d34d631de2e4846d20bc2b7380661bb7834f81c91d2e9e679f5ab8d5bd38b56e73731fae6aa1a3ac131e766ad3ac4e8da334141d1b3ebb13bde4390fb35c7278b7dfc75c029b9c54dfc7153ae892d481a21d47f69963fbaed90132205c8661b4129b7ffbda035fc0e97ac2ff9bbc853c5d1ea0e7671b31a7eeaaed26fb99a44dc5f4958235f22e4deaad8797068982662279df10a55e4f6fe0bfff3405c9b8ae58bfc3ec689189e9a60989c15cc7cafffd86dcc5b96a1338075b0cd60a4c2d7f9fac821a7bbec37083f1d3beab1555402ab68328aa30cac5fb2b0cf11f5792851d46bb210dbfcec31e0e5b6003d73a46fd22ce5ac11ef19a0fda66f13183efc8cb705e0629fb5f1654884c212469e69263a0e5adb40d10ba02e082e7775f9299a1b2037aa26bb876087573ad6f69ff1a17921004d169ebe5529648a8dd5c246463078fd0e269b2ae476e9063d2b6a790821b05081d37815dd68f5e3811c059ec52fd6e918eece493abcdde4e4288879351dc8b4e873d54a12605a631cf5a138aee193916516efa429c478fa12757eb6388f41bd72d0370d88c1833a82828de3e2a8e2987f3d2b15772b42cada510ef2ac1259c1dcd6050e01ffede6912cb05f4d8cb7d0c00e9c14218d6cd9f94cf867466262188fc0db3bbb49b04ec43bfb7cef89fcb805f4a84dc677491f88d50ec9f76a785e8da6c7c1bd2b128487b35b924e216d65f9baa3e5570316ad89aaa444c73789285526a257a0452e07ed4ef7af7cbdd595a09e6ea1d044fba88a6fc3db3be8e3ecc1d39d33ecc7d81bbdbc81f2f57f6cd8811617093cadfa49a63fcb61ecf61ae581d322248a299b0249aacedb119031e518d5d000ec4cc63691c3eb2538c3686dd261b3853e5f741f23e3aedf03d2a62de5067f2e47523e306585946a0c88de9c48d67de4930b77aba26504c4acb88f613d1ce7a1ea08b255cb77adb34ccf408a99a71a38531b1153b662edcad387ec6ca1d2d268b948203c051e690e733f2d2ce0f4aaf1b8f41079b067b23e287c5beb7d7aa6f93067158bf92366ab7e8b04899d31eada7031211db66372db6f51f6958110b8811641187543abfba4a6fb8ef4e6225e2eb1190854eb989d3e8215d9e267760778221e5c93ef77a606ee4f5037c860c87acaa970fb640a69fe2b431bb108e9d57f78a772b7cd17b095dcb89cf492e2812ab41d6614e5a763122137491df58f3af387c0cd8f20cd6675163711169086e2dd3a3daa1fa4bc3ee7b1b5965d7698d5124b3a2bd5be6e6e9aaf0973ee4781e54ef17beb58e9b2e11f7b91324f8ce2a8d29bb7b8a33ae7e15818f80d7f3db6d4315554ed8d5a493c0aab837d136a687bd61b2391b7d0f59fbde3284a376890a1343d26728c440d7fde7afca092331d0ad2e8e76c68ce5432c52ee2d584fe129a91791f06168cfcaa9f41f5b9ffc65d2b55d69728fee9ca3780e290da612710688d6d5d8e412036861f92ed960dda8b96e62692b72344ed7173e553ae82c5e470d00ba7ea3132a0966d898fb54a30bcec72eada7573308815022d732de50ed6462b8701e82af7856fea69c597fb00428588a56c829de38f090110a274d0c8f6c788a08784a26d4434b062d200f966fcf429e2ced17303fd844b9639284f8a639b6ec912e36d6aa18702457aad370636aa1d8de473d12e0f3b2fc0452836ea65dd0c18c4747e22796fe32e7053742cecb416339e4ea5b0fa2aa6b9e03e02b9234fcbeccc7304b423ccb8cbcf62b6aa4ee0f1b0f6fa63bcc9b18ba651574f41e391249f1b41593e53f26747de481d054436d344cb2ba18af001ca04ba72e2f865f9a1f93fd2b7e0c170df64a458e78486a80cacf033d572612f32267c60770315d74d1f9469a06dc2f6304da850bf693084ab035a264f4514c7e402e23e3bcf21a54ee733fea0f22a1ea65345edf330ddd36897e37830d97e67591cefcee51e26a2f9a6cfbdefe80172886471d2c8b3822c8c032c715af888df9d8872d72f6130b50f7f32b28fce0423234d21c2f9e69d367ffbf416bccd9902f76568fe6eb9650ae9593b4eb50be79e6e9434fe8c524da5866d5dc5c54912d4bc2f979ea3da71541352231ee0dd24cbe03f1de52378c4623ced5b2c0f648a10b811d3af5a11058f9e98ef3eebd8598173c67e92cd7ec8b6a8b7a825cfc844ee19216076b3bba1a489cff5cbcf2771ec5bb3fb753e1b0d26b8b48304b8224f0f3a1502f90a8f31a5d44fa7da12be232adac9074bb379a8f0afcb6507e58c14d82907cb6154303224faeec4866f8e2e7768cb329829febc20d93126039f5ea7721ab910ff024bb1b6f37fe2287ed1a5567af0b330a1861680b1de585a9060d44269f2f484a13b53cdd3647a2948be2325fc5efc7381bef29b26998d441a6aaa2a7668d3e6173cf3ab8fc68650500898075c14aea8dedb060ebf9b985874ac45ceca62e220c8712216df62e342549640d35d5c47fce45dd144d99d628c8f1bb8e8b7d8eea07e82846ffb6d1be3eb56bcdacbafd089738f2dac7fc0ed01363408d99eec49d30cd3f07911bfa4a2c307f868ea9aa299383dff32ac65f99fedf8f0c87ae31af13384114eadfd753498cbf3cfc1a9d294eed1bbb7dced4686d165eb0d3a7b8633c879a49a31c752b4da425db7a6a75cb674e6435371e3136ceb320c86933a9a58cae66327cd590e9ed31e3ff76fd370a0345ad4a1d3a96d4060474487b04cf354742c954b77d06d94dbe8cc49a4c74efedba580fb8eebba0431b124bff770aed929eaa2f046837f30bceae8e8ee57a5b946033c3236ac315fff2e4f41db6d7f9cd80a11332b24ebe32f5a7b79371a0a19ee8a5a73d81861df3a1a3f015955730b5da76110295b8eb89e3d6e9bcf0eaf7482ba99096cb07a02a5f16d80421bf20f93a261d0f64caa80b5b11dc70e8ff1776b8e57bc8d44e78ee0cf1f0d478ceacc8c2613c3b22c542c6847a28a29bef27a4d9560b08c3f4936f65c0601870b2d8929ca31f7ea1cc2f2769f85e275be72e30f7742088da03d877cdf80851e09c19bf74913c7448c19a52995da3d731455ac5485c15ba89e766f447b4b8a686a0f72f61d769501ecef217ddde3653017019ef0153fbf84c6bccf1618b3d10193811d223e15b379b38992f061e400e1782631c3d620c4ffb919c5181a194dc8eb2d9cb47e10cc2029e0d8b6acee553a61a2936d774604e480b610d087db33134403f2647b43a5f772a4a5f7fc9dc0d372e1c8f693b7f5f97811bb2a7695cb895b1e1df4de8589e51f88ac448a769ac240eb9a6ade630c3afbe488c6d061088be60b1cc4ef03663265aeeb718fc24bb1c60569d6b761711d3cebe83b873bf6dac02565363c431367f7ccab4e5ffe7c085d265a7f44fe4bb78e5ac5d92981343ae5253d709d6718b9352c8b621fa5dd00200f25219c2b1d53cb337a825d4da4e2dbba12cfe8742ebc76931f7e79278759ad5cec812e6f80bd801a2bdcb9bd2cf8147faef0cc02a3b814e56c951577a188181c6d86be28b260e19bb5fb84853a7d1027545cea2511b995679fa451da3678cdde30005f1bb40fb02a1bf627c5a2ed94f4b0782845c12533c2678e3678a2875011e8e303dc95f3feec5c010163f06bced9b5737df4a14e656b2ec3e81939399172fb35624279f8d7490fd02398340800e667d69e746d9740a678eee72c0d7071042ea6f103c1da555530fd2bb3caff68121e0fbd7382913e3b8e70d4d7e24bdae96ac98a0f52d3d548cee0e6d5e82c9a17f6061401b88e9ecdf803dcd70b8b9cf8e4976713e70bbc950741755491bfd1c602eff932b01136638ead0e4c8f7094f3b35dab08dd826cf76f61331a4bf665fb5c568c11e5c2c2c600ebfeade72359c001bd4f44b166f38722a60ca598cc9da1f755ee971fc0fbf49c4e80e4b0d24712306afb8e54b23701a5fb1623b79aada824bee9c3462d7921f015927324e43da7f566242072af2fa257cfc2534212889472192526844ce8e56bf08e5821b9f25a5ad47f8e8c5d4c70832aa6005ca5215953a7f5b047786849a2d223e49c0c0a0b79e5d32becf0edf1fdc31cb25dbfc8cdaf6c5cc16dd53c509bb834e54a0d64425abcf5e00aab70ad7d8913aae89393c365903910cf61cfcd0a05a76f36a310ff2df8f13ac49982d8b74d4ae4aeee357a0f487124f948701c7bcb4eff90204431c58edf46dbd1069077e97e76997631549aa16393f34136d6506262988fe657667dd2040f829b1686f7fd7c321b65e5898088ccf46d55566374749aa904039bc9d66b915f77d49fcf1252995c9274f43fe0af11b11b0aee4fabec69280fcf923656a1c9e9e42777e22bf49e51a6addbfe0e577dc6e6a1b6e0a6c2974a8c45338209fd85311ea98e334b98d086686903bde7ec93f6209202eb426c2e345848068a418c13b2e1b3f3c7586cdcd0dd7be4d635eb52820b8ca15a9f6429547b022c4ec2b146aa74ddb52a9619fe1036f06be998e5cc7496ae62cc92af31dd02b789c8abd2a4016165aa24c6a7d450abd81c126a5f2245d710f67f6c1639c36914b1c8325cd7317b92fda08b5566f1d5208ea7f341a86394cc41f9ac74bec85daddfb5bb70d6398a82111f004351ceee27cd02c655a9a19d06f7b3a713e4f5e86bcf1d18182ef43250f015d8ec75e69d82839927309182d4242e159556ef8201fa404809bf46193fb049defe4e2f4261d9a6dc9e77d2a4953ea11839e1930cdbb5247e02ee10eea8a443a3563e20f94b053f361f7de831cd69ca813acb95c1ed83dd4c636ab259c64cd1c2d81a398ecc2b20021e4eb804bd7f5e507a28484f3f3e9a37b7ea3c3e8454dbc59e207443b9f9effb78c0a9930356b7a928cbcc93998777c6281a632f982dabe485e92dd65199c4f316ede6c6f4c5eb52f05f5b25abdb726d0b14d42d927b6eac1e60ffc2e24b0931b0e87d65c9c820f3a3bb59103cd4d2d8d2b146ddb4d6a8b4ae42ee1b1273ec57e3be48633769524d156e69dd01aca1b534b94b9e5752ff90e9e3dc9edcd1664d09ed2f45bee5707fbf3e4da27628bedda82715070e599299626a56765abb493009e5cfe64d6b4c8d408e3d11bea911cd5e7cbc68893e9d227110f99f1848d11e4dfae022fbff075f84b07736690e18675463d5e7ce5d1f7c3905cf816f2885a67035b405c3ba06649ad78e7dc2fa14627cb5adef4c047f11a3e435312886286facaa3f41b38bed5bff5ce74b9f6f181ec1d5b444f86cba3e8916c2241c0ff1041985dc2529f4de9c4d4cf232892ade08bd0647455c74273a5b47ea5b3e385cd990fde8e1ad2b3db506d8ef143fabcb9ec494dbc827f6ecd0cd572fddf48066a1f6f829710660f779ff8264222006975f67957dfda22e4a08226a3f89f8f27d33ffacdd4448492644d053cb0a2709b8124d89f5f3963efff0abb047bc339b47a59b60c570fee2e78b1d8573f0b4d2e5eff97eedc3755498ad8d07fd5ec524f3431f255b14d4de65d02b855f9e04a73dc362900f574dc26b737f9109b251facea9e872bca45c97f97613dfd05907b1a4967cb0d1f957690f38bdbb937d5920df05a8d9e071bdb13a143333e2521a87441c47d0e19c8d99c5c927bf0e39257456bb59f996b438814cd681650487bb796a8f75cd65c3c5231caa94e06f3b2bdc2febe9c4a6f0e55d9882a6da804d88d6191a52dbf0ba8f415fea7e97d45967732bf286b677655b6fb10198cd98f9b40b00ffaf474c7a46d1ccf64c2932da2b977e5dd0a42dd054cf4c4d4416de592b5308a9e6e99859dbb7e0402917903064e11215dd706453dda9a5cb5060fe696bdbeeffb9e5adf295b26e482c8aec0b50849774ebfe2720ecb2a046820b57f778ab81efcbcc45d441abde029a2a2dd3dd22e625f5d35684bca2a9b5bed339ab0e34b94cdae5cea144b8809d287143daaaf01714fd8a70a6a03322bcde77b284f5f2b507de8f298a8b13e67780aefbb3c893f4fb6f4ac2055ae110dd3fb9f604c9428811aeff035f4709df91de98bca4e77a62337770be35b51522e480360ca6e854ab404a937ff1c91d7abc5fde9e01d8a9345316351e317520104e6d38b8948beafc9bf7344acb81fd4ff0d0d9f86be56b1a298b0536e286ff8adf7f329cf1c4cc08dc0beb718f3383858a884d7d7a9b66cd5e74dac7d2e7c780554acee6663948f54e8eeaf23c8a38198038be265c33b1f416b2e481701b6b200a23d21f689136f599ab2daa3e60f42dbbf51007bfd9aed5b44138c283233524f88fe7058ee5467ef6198684733140135d1768348cfdc2257429381df19d06d73e409e4b9561ca2ba5a9663a9a164991b50930bd21ea9aabbf48a0a4445d4ad464afa50fe947ee7996401cb2fc7b2913d3828db0b835c801906181fbf1546f8605f9477307e751923d9a8560823706906473ff08712e137efc2f95db7421aac96f74d0810d66389df12b409be2abdbc7e932907ec3f58deccd05ed1fa65a945dfa2afc9857e821cf1a33afc050ab7ffb1d8fefdd81b3832e830affd5e542852651bb29d2745cc2ba58728a9ce9768ca5de4071193745c519ded6e75f54e64f68f94b98c6f32959b58a7ada3474f4e73fe570a88166ae2f0c2df33c69d5c870ad006198a3f058e67926790767eaa05695c305e08c1d80e3a893e3ab95f53f57799111586a9bb931fd5656f6f26f09db15385880a0bf2e6deec63ea654f237ade2faea8e8a88bac9fb24b501df9703aa9cbcf611e5b468d0ad70bcfdd446ef2bcce6fc12a794fef31d27f50564bcdf90fc706aa81cdc0bae92755506e96717e0646f5c36a55e2f56a9f80e353f6f6f6344fd2c100273dfd34bdb373c88b1601f69212239367b9e8bae3e1c50fc70a4d78e4487003c49defe76748de56a05a8d8465afa668b29336b0a5ab3ec803daf4d1388954f1b0b10017fde3c235746f9ddb8636f52e75fbe9107adb8b0d79f71118d47b3be844cb60ded99d214b9b5e4f55cb463d7cc11a2d4a77c0f0e06ad3fbc661ef6e3824712ff5befdbcc6e53c0750fc0576c7cb65197f4e49f1dd5ebfc4ab56b988bfe238c33abea4416c306f5849f80619acec4b4f9cd3e63798c146a5306ef8a268e864256a99a5e6319939d11d23676d505efebbe62be52208becdde889a0d1fe8653ab88ec17de9b919581c9b0e723e0beae14b2c8734562ec30ceb2f6151c56330f56fec1dcaca88670830b858cdf4229ea53d37b5ebc3e06ce05bef7342cf27636a01b6588f96aa1f5ede41fe423863fe11375a614f1d29542c06edf52ce7741b6e9e6d044ccf234932e6047c5492902d0e326a470dd2d9a8b3dfbcf5f5ebdd4bac957b381d9666ac4ed6e5638c94c5ed8eb6d480d17101c10847adf84708b767150b0d10fa6f84af00b27909799ca98da9d5ead10c000c6e0b9af5cf8aee1b6f43b4eb46d4f1e00753249dff58788d1646f97bf6402baa5edb9e0f928d499cd0242105c1ccc2c7b4261d44b5b024a9446946eb80860cbde5c08f50d4071f02d2a8e7016e2f251a287101d71c2f41d23de778979b8195fc60bceea9c5da91604ee14dd4cea3089a6959da469903cf26915c57b50b46d9c0fbcddb78abda23e08f33050711331bd5065861ae5abdee6e5123dce9ce774dcd60f7594ae084189481d069fc58ea79f91f1e652c84f7642896ffe0182701261ee3c59f11f56a88077c12777d104d5b219157e4c2da6ac34b51c3ef4d9333ca922f4000b51646a961f85574dd4c307e9be7027c1326ccf0dcc9ea0664c1920b223338a06b5e7bad8c548c1ad3225531cf803159183cb4c857a078df096b35ed33f105050fe0b3ad9405839968d97782f09211fbb361b293a8ac5bfb7c16913d0605f57d4daabe9c40901c46620e39b461903c4f9154f8402dd36d6277979eeca2c6e45de9565e2603e66bda7227dec28adf0e546129aa41eeb4345be8d951bc20f0c69649f43d333c0ff4ae260a4c1f70f9424fbff5b86e51e607e48cf5527be573e407ba6a4aba7c42890770b6a6712387200b064d5a26a8785aa101ac6f5d1b69046667480c6df0ab79068461e658e34a461e2ee161c7d8118e41c83da939c910a09573efecfeb23111a7ce4dd25bf1b723c168127880b9515a7d25d5e518873a330b93ffaeb69837d0b5f325f935e14a3b47a74d7ab37e86f197d60b580d0b3742cac268011ad49c6e2537690501215732984fa6fc7faf8d6ca832aae49fca3a5dcb967959ab388456a28797d922a3d6469eb1cbe87c6181b1fcdc87c7d6ef09a4ac94648055bfd3c84a85324995c273367632cc83bfe44ea5d2e5933fefcb4b43d62ed7118d71647456cbe2986e2fbd6d085efd5af7e8ebb6ea095bbb90d514a507c74d1dfe1e41b864cae007b013e21e85b1903a8a91d9522a6a70fa7367d092e98c0805fbca433c26e92d67ba214860e5fe8e30d0c54e6631cca375cbe4aa44b54e7b1db892b466f60453f23bcafe207e41b18c7f60fc0306542a54b30e3f0fa4e8ef3b14bc2ae31dfa7c9325d7365dc42adad603516cd1c90df1b660cf15b5ccc54121bff008972db4bb24d6a4b8294870145b5a71f04f3ae2943a137db8a1f5e059f77b664cca1536acf7b144cc524fd59615289fc49dd4c2ec008948c7c4875c707898ee4a224f0ee54d0ab6bf587a933822345bac07dde50fa63dd0c203a6bc51a5ef6f31957a9abb273f70212d444f985deccc4c3d3ee1cd0aa57ff49f14502559a208fba6fd3a540c0b04db10d3837933aaf00ce9c7f03fdd0b60f6e3343f3f58d9262eeab6aa94a07f189c638957bc41fceb5a730e7cce286670f229f49c7b560962ead683da897489590d1c8a101ac20dbc1a10e9d4723ade8ea5decd4e8ab0b7b7080a1e0ec202f7da0f36c37a7691c35bedf38508f8214a70a6a19ba9cd3cfd06c97836bed4a45923ef04446218f13d5f2e1e6a82e569ff6e27f71deda65612d14b586b3db1ef00ae3c1f288a0093f575aa69505e374adb50a609bdb9ea693679a26833957f4c3886061f299fe6fe0d29309193576e7fe4bf708bcec86c42f24b2cc3054a3029119723474d16be2a5f1edb8c71c2c06159a122c9027bb1f0c07bd1c3fed9611f2cb5470792305c8434b93b01ab611221bdfcf014343ddc1d5dd089444de4d37809d1896a81085e748a24ef61dd859a25f529313af590968f663da2d609c4ec89b41eea0e7020b0a38e5bf7087280031b048997b367b83347fe0385e4a6b32e57a317591e0345151770927a3977b7ddf4ef31e1f903fd1cf4eec1eff7931084922dea41483021083ca5d85aa46b6ad141b7f3db52c4bd5a85698dc8b4a403de555c0b6a6ae7fe6c9d09eac5d65deec2359877d715f5a51cf6fbf448f78ca0d4c6fbb7aa3df6672734bc0b649eeca41bbad8d56cbe79001934765e88444d1a79a9a6b6bb6052ffbbea111bb5fcdd30d39429a84b6c8c4dbfadfbc724142d7075aa99196559d67985b917b06f4c50cfa0c12d8aab49aedafbcd4149f4847a1d67a6b8a9e32ee0b4efadf9a5f956917f2f430274bce2f71190a889e1dfbfbe8c0e7e78d1354321ddb2833c34e73fcd19f4858411d210729f4ac1fa84a72c2f06d55badffa66e9c8853fc8e981875e32e94ed66de4a30c21a8cf84506b590d8eab8bf8df6fc01297c3a9c2d6efd09ca4cdba84dfe074846a0f132e098dba7688d6d00bd27e54800f585dc8d97b8ae3ab79e88251f7c9ca659711962f694015c4d218a7748384aba3a5ac148be7683949a5ceab806e9cc015307dbd214f0b056e7cffc00f3b037b5016be64a26c6b8150b74d2d2dd59ae56c9fca58ac0f78d5e373c07f1e12c4f7abda962b6aa9d510f21748e2a7f0e05aeddb7d8b187c928cea55fcd487a8e4b91c8cc8ba2a73a5ba68950aa2e6416d5770aed8fb91b9936d616308249d51264d33b38dc03996599fd4d0b1636ba8ea2d724d12d9d94a5c3375da4834cd1f11c4842b06991cd64bfae2d2e1784ee3d4a5170dcb1764688d97866c1f4ee48592892ef0e44b872db52f817c5370e38bd4a8bdd73c96bc8b0ef9cd50df0b43e6a2cc71efbd3425b990675d463a15ad9e61bcae0f48f67f72f6ffb6cbce3f5251e4dc0080f378a3ea2d8d0b0e9294f3b784b352d8d1a5b6dafc2f9e5e810d3e4db46909d649ff8e9d5c89768fea9b4f93126427bc8d5fc6d64943ad22672a44cb67287c8105e6db9f7f96b0d3d08d5f2041e03bd3a9502bdcf4acb4fea298c828106e29ec0d1c55b90b9e78fefec5efc33660a17b75f85d3eb292055c5bcd9ad841335f2f165f4b514cbf2632fcb27ee4bc822e13933a9547f5f37cc8ee6d32fb62ed6bb60963d09060784de669997276f8ebf371ce163e4a13fc57fe4e1a5d829490cd0cc1891322e5c74d3086299e1edb1ca696d56a2a8c2eeebec6fe8f61ca0193ff9d779a83d4bae5474af32e2fd94369567f77c0d912929d22b2874c4939d65d0a9aaf6111cdf3a577e4078704b6dbd29e144d2d9a2b201a51be2f6ac45963a4c14d13cd65b56645e8ae508d50b849abee91892e3081b450676e4d3e229a212a098559b72c0268e3eee84534173211c06d2203c7763272e89f431f0ed9b74273db30283c51a4a4b1d6ac2d3dea6f58b7455c609cafb23d011eada8f620e9f2d8d0acb38603ec457be9a54d56e1475db7f5a49021c03b58f33a239f222fc8c07e3b95e644529bcbc1062d8e8c49b018efc7bb20f5d8ef6496fbdd91e7afa5e5ecbfd8f4bd00f37c445569e334a81e45a0e7cdff481d83a92ad22f884aa87e23e64fa2a703d885008a8e5e4d4d439aa48f2092b9026cef57e501e63f84987fb365bd1d5a051278e6dadb6a7500f935faebd3bc8e7f69da0e00af68ebe808322123c4aba9708696fab0faa9f7323cf9fd12730e424f3345328ba1416272cd5395579a1c009e7b1f155f1c67d5b43ae8913c203da08dbcb6d0b55abf173e58d2a2c3b1466adb0d045350c79e29a47ccc8fbe9f2b409b5a553f1bf2f7de7ea56d2686eaa191825fcc330019febdf715f9ce72dea73e404b3d7252c334d77e1e1f710d120ea850bce08dd7add6924f6acb68048da2ad5c99a06f41769fb51cba44f5e45f8a75f77954df6b3984346ded17b5b37a5873b0fde20567e05459b0c60604f2bc91bbfdc0a937802a7c47d3cc120bde0613b38ea9c7b1c51cd540b414b981199764e5b6cc24f64760122f960ac645afd7b35d8ac9e1bafc0f13400252fb64d168203a81ced27bc4b2662552ebb3ad7186ea790b6b0d649a539159e422ecb142d50c33dc09c684fb30d1f7c6d6254bfae39a012418a690db6c1336ecdc52b27ee522cccf68daf9be3137cc318e1231e1ccfedd9f84334fee3f112c9e61f28000db3138bbec99ba661bd8704369b0e2597397e7af91b42d503890e487bc29563b8a4ab1003e8fc3c85984bfb351deeb532b823ae751c67c7db719d2d73db8f21e43d0f501681b901e20b77feefc14cec52665f1eb3b793af8929241447f7b360f957b29d5a20e1a56dbd54a3e441fa629805a3e47d7e9bc44d9f08e703027b2ef537f45a54a1efe4cabbc4490703db544eff34dd8f5da482211d6b5721209a10277f5f68a54b2e2bf12bac63356495a7952d22823ccd0683c0ff1e3ecd6b4325a45f91d20ca49cd2ac0f5aebc4e358b44e06007dfd94480733fb360081dc442e40ddc8cd424f47c6253d63e7233f22f8a42f646aaeab1ca05820914aa1d0221a94ca1a20edf2c02119701233c182a1e646d839d6d7594151a71a19a20d487e2adf1cd84f665f6d8fbcb69ce7d676c56633a4eae482ccd42221bd129860def01a5664b7dca29ef85de954a15d5c8339ff89ce502950bf797bef20b742b978111089ac255c8728a2ead4ed74705d47aae5c495a3a6e279864d2279b08f90df3031c2a8e0ce2109ccf01161a2006f7c3e293496fa57e215309dd8627b61110e1631363db7cd21eccb2e180748d7ceb1d16d9f929bc9f54bdbc423611db3d10a9d313476861b4c448249b8ff02a1c705a265fbf02af7f85beddc3ea61db1c51fcc012a0ff71d0d6f4cd80d77ebc8c24c1e5584c7d38084e36e7cc3ba11c6c0fb075ddb3afac94fade02e3e294b3699cb67a4de0b5d93f2341e7f284369d2210fde0fe8ceb018b3c57f7bc83fe7c45be796318789b5e7fb45302f93a1dc6498286c490e95100a5f7608d2c82fb0ce5c60cbe6d70b0379a6887675b0889619e62fb757c161868d55ce7ff5ced8854fbf2c67ba90cd62b94c8115b69a831e7e5d6f903a2c99636e4c56712826169eba353e26e76e739990675cd88373dd779bb60bb9480d89d07962fda181a2605cc69782924645e8714686cfb06ab91dd85a4045f731d1c4965442b90b7648a296f8b9971ecc058959fe55a7da6d820788908f943421d362be4220882bb5a10038abfaeb8931e5a515417a42993066e80d32997ff11f1e6200bd27113a869878ab3dd50caac43ab28b71375e9aa4c23956c5cce71e08ecc9de5030e5aa905cf3ac43e0ac68b97f9060444f98bf707afd3f99664d3914ccb87e7a4fb0e1ffb9ca3d03887d16109fea4730bde16b5902d40f9bb638c251402525b9b45da0170e4be7f0d2baca862be8f7729dfa50e00b3683a08ed8931f212783487c14ed0b62d2aff61d20877fbdc4bc832a68559b54cc036b80aacd1140de7fb0973fe3d26039d856ad52c89958324d3602163370eee0d6a25cd9982e9d44762bed3d75645c34a0c230017b228c3cd6ebd8bf116246d7a8b0504012455da802624e6b1ab34e8b8605b6146961cb037ea90ed0b80afa1b220b109faf2e0959e9f07289c9b5ba4995c4ae9af4ebed2c914899af0822fcb03e09aca9aa53c60b7176d569402bfd60102257332a7ecb639114fcd7cdb75070c0b7cb7e783961baaf43354b88d7426e8749060da36e22dad9465cd5d4b6bff540983bb97e57a99c22b894410f13885c36d469810c89b6db68e0347ea6e9c6e1ddf388133a66f1a3a0748dc0b99c4c5f668e3589da98208899fa1219b4c8b5cced5f9b57c7478e6205358d33d205f98a52b50ec3e16ce23cd5a65d78b8de8348ee63f5f8702fb9de97c4c06c354d8e7a92ec3bd0e78ba797ebfcf1c088842990613d920cdc11e71929a8990cf6ca0f7b7a23a700fed83a5dfdd3ac31f693f25f1331592e2c6a761949f40a793980080d555872a9117069669d465fba1af75de90eee501d7004c663c980a60f5648fd0e786cbbc14db1390235f71d817e3f5fcf4cce45fd294903318f63707909bae33baeee29bcc38c81d2e2a183bdfb03d84e759d409ce04ef3141bc4c2096e593fc4d4a00b29f933b10f48a822a7231448f587caaa05016270778d295e3c9093eea565614ddac4919eb2359eecacb815c7d62f8c020986639263d880d2b7a2ae9f843f1e48eb4a08b626fa58b7dd1a4e899f472c1279cb45e803517f43b26b0b0c2b2a92fbf64e090f962c43135665035a532db83471d20ac39ef3cb77a227424f18a1a13512c24f66827e16a5427d9e984816d82d33236a9c9be479d8715bb20bd848dbe6fadad2067d35cb7f0e23e56b70060369ca6e5b1340e280f1a489940ef93c67320ac7f4ab87f2800fbc3f497c0428307d9f392b3188b288502e7df5207bfd7216fd8dbe2e16bb0032ec4f5d7b7d277253d9fc5dd3eec3285c01548e68776dc7d2a46fcd024a94dc6f2213adbda98de099c69721f0eca99e35591727047b7b5f540fc2b9e5e21dbacbd4035fea1b08a25a944566c241e9ec7ccd9b635ae7a7e7b0e11ab3027cf9cbc436927ffb9301fba5ad8e2a97ae091fac96dd63d897b2112df92b1ab056ee5baace31c231a3eabf9b788e4a5ae1f035ae7549c838772dd1f9994685f65267abed52bbcfce2bb3d36752a3292724dee7add74c56cc2a66d0a08b14b4d92fceb5358a99cb615c9688d2ff5d6d8fc08731a6bdb6ae98c2abf910aa0ddae9d6b1a2fb620b574256cb294751da54d95888a16a179ce253980f3ef7091cb46f5b107412c1573b670c4e64b3f4de15bb8b7d75cc63b33b0752694fb8dc0c725414f206d932fd9aea3cf2263118fe52b970c2c397dceef709c2a15574f3da5a9b68bc0adec8f223f4be82490557aa516739e98dd9ae458ed799192354bd30a5ef3a482c141f6969d60c9293b0f01663f3e84e69c0e2db2a33422739e4f3db5d20c5382707c6d37c63313fa1e4235d4d5fa9b5a2e7931d9537de2388a2ff5ca2ad1732b7a3f896b89b0644517d3671370bba40e4985fe68e01f6114e7d830115ae6074054efeb4137139dbaa59089735d678b27f1759c71678ec3f932022577a51c2ae162ba401e62580859be1cf2c43f6c15b0c1dd3b9b39f4420f665c736d847af65543a747c6dae2f91b09e131a1a7c80d2cd10ea1491b2ca93866e855c901ec8d6863d1cbba7d39d596103118e38906b4d83a302a975c63f5367749c318c4d245d23caa6fc02c3a6babca8fe5e52bb6e7823a35c09ecb8454ee62fd79883d2410a7c03bc33283d71e1cc31b4c7691d69929de4b58d27ae09a576e456db83be8315cb8acab525d0b60887ad870b6df45ddac71eda53d7a81d677ba124154bdf9e9c0cc5f541ebe906ede61a8b5555a076a446bc3e4516f09439c9e8be033cbf78a0052d7ee339a8c021e5bf405669851cbc623f15eee0c270943c690663ac001528d04cf282e011569ef8726c160f341ad00b013994152ec08a4db4f11bee925b02de7cc7bda70567d3886bc56752825aa92161521550795771de2e3cf2e3bc192094ba06c093f127aee6aba5b60b9fa29374a1348270858e4b2f6b4e4c4ddcacccf053c42e44eba147a0ba5ed366d885d37864452b76ab656adcfc28075ea811c98ba80cc50d90294810f47cac4c76a652a6fd361ce3e5566346742d8b64bbba1b934885816109e2faa8691ff94923417742245cfcdf19a11e6626d390f84d059ad57c6706c79433ba670d8ab111931fe1fdd2b12df705771e3724950736cd12aecdfe33a9be28848ee649a069661d66912fbee3c6d4f11b5ba72d93c26f468320319114d5b9f82f4d31f1b8ae334af7cc211e84cb59e938c5d4f2d754abc9ffb774d24905f35022bb7cb2f2ff0f65222634a9cafc190f8d9613e09afcb48c9f814273cb1e15242766d74741036fd37a0219e12409dd461fa2a0a32351790b622c25acd0fdb8256a1caa0fee633e08416adbb67130c18e41b2676c00363b456047a14c9c32ff523f6ef2b5da433d1bd2e1b117b40473a25bed74ed2851f6f5660ca3597d6a3d51adfb87c9969f4a57e222dee0559e3295ad3c980037c8c573c0628e3556feb6812d2ef60bfc8f0b832b780b2fb6f26a5e04e82a48d659194d161a1abfe9291d2c19293f59f21353cffca5288692dee5f51104d4bd10d3df86062903cf84a94135bec0499770b33c5928517154b3de24ff52c9cb06c378bb7e0b02cb6d57f7a56556d452a6514e30f201f7051be527adb103554c573425f0f5110fa2f1dd1e470994fbc0da28af2a880d37a6629fd9a4cb6df1272120fb53b96ac1812fb361bbc6e24e8c60bc595860b55b6ef57361c1fb5623e708d055621e5e07eb5a1f5d983b259c2d69df31006cff844b616332e0bffc054778f2cf4ec983158858c541e787c56151124c496ab5d54453a298a943dc3ddaf6f79b1556a1dd1670a7705c6ab0a6f05535ee4c669671b631d6184f4ec5cde5fb72886173c7e80e0808844c8ded50d280f244d237d61b5ca94d717a8680b46b3c274b2a40a481dcd49040226aeedd3e5a2ded931a70297d5e21ae424c2eab157e7ec3f60ee43607d35294df88015b5863a97d395a1731e2167a73f813de604b57b4d2b38dcb51ae6786ed0865ca13b2492d77e51dc17dcdd4ae9a6eccd670e370b8351c746599631b3d19552ae51831d49124ac31e4384a2871e13202694d38d736171d170875c8a42c290186587dbee06b053531e3a4c09fdbe719a219b46a3ea7528ef937431b4f0b8d2d2e410d8fd3e8f4f061e18262c0edc78352a222448b391b199a1f84495e4e2f1fedd86c7f211460ff575d407e41170f878db39219659716afb1f3aaad7be018546aea7c058870096a35f59f7a1472e0906bc40a5f42e8fac8e6aeabdb050d00a52f2e3e7aa832012431d5d3183b04642d17355288018b8b5f305adde593aa00011d127c7db4327bf355e46e6328445956d18be978e4313c6053c98ba3342b461a65d290345124fb5769b61f635c672d72835a80abe012d08426e690617b4491aa755272379f2af9b93343773d650de40169d72f949f09947c26cc7bfcd9ab6ce8de4eb46c863663426fd158d2c490e40f688723200a542f4ca3908f6bda59416ab0def745517149594f6195a1ef71acc98fd3cc2d043e2051c740ba4a32b454f7764cf3d7fdce450262b752771bb6f43f8e6a0dcc5c3d342d713d8716d6dc2d4824888ab0efbc7bc54d11ac959f58e72b26083fb6e3d6e11c2c8c19d01411a6ea422cc6dd8c9228aa4431bc546b6965f0b403b8fd7d613a2e84f5d2dcb01906270befc38d1e9dc6bb3d621691c2ce84257cac6d1ca683d8b400a273b30815296eb4551b24fddb4d1e2a92815c33553291832e6e58db8a37184d33c122274dde9d92db12c5e7dcaf8d2b9528f4ab1afd70e2b09618edfe99e946dc96081bd185868053ea8bc7383f2731d2c939807c2b9356b402a3ca1bfc2d836a7af3edea66b55d9fee66cdafc447278b18ca6b79010696671a727aa3e2b604c3e52a14a438e754928e2222d0674a4deebeed98e6561f23734b430c0948d9f70a86c56871f2bdc47cdbf19e90b38c37686ccb11d3a0a9685302863cd9d6eb4dc9b10286b82d980f84e4e603926dda41e6fb33d844c80ed0b3892f816975347fd1e1175d64278e962fd1cd52f9e11e20dd9617c266b2a4e512315783969740da7c97395bdc3bc4c4d9c5abde839daf575c14070a37c0f2f8198eec1eb5340556bc9f446886fd1c84dd84e1922bb404303d847179b9fccb588da1cc9b40f1b8cc672df95672aeff25acb85ce3b27bd423d55b0e651b23df868dc7f6664511136bd3f56ca2bac037dc645120cb5276fa100647d2a954f574bc93e6af621c4357b31039116344de63475bc0c81d46a0584554de88bae05874b72bc9737eb38c96828071ab4770f012ee1c058099544a1472d7345ee8183178e92a08d8831594fa9af96d328c2bb8f2415bb0d1378b042269e23a7b2817cccd19b2cc5dc093843257345a2f61261be456109dd74ecd473f30dddc08b7ed293e107df39ef64dc130041f6cb84df71769f8e1ad0cf06f812630e2335f7aaf3886c6ac2e6e7c3ca326734cfd5d0d4e720ae45a61704ecde889025dad45504a98d00546db70c828ab745de37eb2b5a37dc8b6ef7289345355ab6822e4c4e99e8c8fe30807a1038815f33997f0871f7530daa3f79722d1d9cd5c2f2fa22087224aae187e5da8d93374309bed5759176ac1c7b712b452b804121907a3b709ec6599ffa41f4757840a2c9a36f7e0a9de12560296e9a24389d9f5e70aace13579270396d7df83b2775a5fdacfb80ce5cdc7bf1e2298be24c2a19f3d2c41f363d7662c477d5fbbdff918310a4f039478c9d9b38f6f2ab9578b3d16750e12ab7fa937b7d70254c1cfb7a6d7be770a9bfb69f96a4feac502edacd8161430b944239fa20cf29fdfa7f4dd37645563d43ea76495a15c25cd44f7d5ac177523ca93a64e59bf7262b94ac6435848f90d72697e1fe22cb900c667f8a4eb73d551c5c0b1e2b0ebf1ae2dd3151d9c42eaa5e11716caebe75cf24ab4a3f4591d4260686a9e40dfc4557175df227c174fb99959e7b69baf3b450a86a8ca9d576adb0bb1ffc1a441aae8d4180abb6ce4882f96d8c61e4c058a48ef8f2f6e62d9d621495130f942fff7802a4a0978d4ee48b44cd2bb013385280ab7ea08e2f41589ac32cb8236079d7ea09ac5460f28d2b5c7a002c7a89bccce5370c24bf1c2fcd8806a37b215df0f3ea98fc29f3ad3a6da7670578ffbf5176019cb79810d27d51ee56289b4e9a72f10026b580897bb4ead3ce17500dd8e4c618717ef2db0835a4e2f4bcc4a055136228d5c2bf81ac72de50e593e5956083fee95e00ff9ed11fec0a331c8488a8152e8603026610739ddb8458eac19314c42e3992477e9415d404df15f74d1f900823e24f8d95f8be10d5a0b6b53ece56db484b447d8b061700e5d41252c472e3306cba8df1c1257fb3705223c1a471152dc82e8e8af4ee9e86e8b53365b8e6f6a69d3040bf465b04e8638b8c541ffbf66beb93bea65e023625c8dad66e1bcc3a71b68f992c9a400c2daddb24ca37431af16c236f747a07139c0d81e5638a0d894ef8e7e6afd1ea8344084df93fde00f84c8bd593ca6402561ddc5cc51c7286fee1006cf6533e1ddbbbabfd5cb64e1f78ce4ff29d2727aa8cfbedc5605e8bda817a19d16a39aa73ec47a039d718858fcf1cf2d53113686e9222c170050d41d4fc527b4dc650d4f4718dafa1d8faea1779ebe21f9579780f724c3590d4ca1a87a2900df0f8c4b66164fc399828eb0d7f771888bb7e5f80bae4d0a2f2f59092bec65d38b573fe51578fc1760710957a785113e19924739f49a86ce194d517683d803ca5d125c1ba6bd06634c4ed266461343eed4f50cadd43edf2e286204df21474cfeaf02bb5e10d7e9edf6b4901eaffe7fe93a2c7e32cbfae8d9c0d0541341fc45a5c669e6c63237c1e752b60e5af8b904ee894885133b1045b0766f183f8ae0e89a19be34ff4f376e67120d2a37980eae3cec155cf28d9fc2fcb69709c7cf608addeb2d1cce806a135c7c8e9ef672688d1f1dff2d7d98297b71bff022ed04e1518af5b278dd7bc3d65d9ea1730e072d8fe6a269c55b3537a99cb8496b845f14a0c535c1438c067afefbb2c6d2d5d17b26e18412e606e8f29f1e91800651fecd915e3b6ea2c2e1a142e5866f53c8fbcd753e8ba07857c4aa0ba7162d3a1dd88b21bd9b33d28ba4f47b84e246583c86b4d5c80df708da82335f0451933af4d0d40bec5c872bd2d17442ec9b01fcbeb65d07d77c9cffcf0bc7199b310e2c4adf88669fcdf80e6e5d962d98ea9f102e3cfe5508ccc6cb73117e79714193722b091eb8eefe786f29d9d797e19756b0d9635c8f11bfc9ba6ddbd8e59e1f36039b40c1f8261de43b1343e6f4a4a72c1f056e5cc151bcc0698fe3141d960f1714fed1cb7e09d2e4a557d624696835441f358e3ba6de1d474993398b6ea8c66f01a25f60beddf013c493b363a5a2295110db3e6746595ef2bfdedf98092cff8e55393935dc3ef8fc1de11ae622e3b774d9f971f519322487a2cd78bf2a77f39a1cd9799110c0648525bbdf493f30bfcec02a7f37df547b973e804deaa5792a072c6ef4f30e0a3db523e87101a1ab82aca4d29ee2bf6d53c2913ebde23e5d2ea16ae1cc49513b8c268f255a995b955b7def59c05a25b659d56828bd5e474b44e84d5285bf6786da9911217e74a0a8f3027e2cf69876059a1d8b7cd8ad10695344005ad203d136d6a4cfd4a534e6ff7ed259125c957d4f2d77000f1f7a2b44e2e9489a533bc49248101e5fe1d8ab2381c12e2f5e1228bc7ec3579e2fd50d5b0a8270b53d12a0ea9908262f81d956f38ca731e25656aea78a0029a82bf63dbde91c6ccd868b366633fee0f33668cb4d8a6d18887596eb5275bb5676822123a8c1fa3917c51692f84c9343f4a5c762c07b6453b46a14fc7cfa9932556db8659a0c1645c7811b267df8d936c93d5ab3c925831bf68d749cc9af2c8595229098b7b36d4eaa3144b17dcecd3ac6240002e14caad7779162bed3bc8a0f6598fd87a6931a72a0a34fd436bbcf757890db76531bdf848f2952b2ae8931f47277002e941fda008b611ada1a80ab39291cc22b4b61825fddded6ee00e9863b145b1eb3066a415d32f58461c4f3e268bef1b0cfb7b838b7d9ae2e9ed204f61e4369473cdd0b1941241abc174318afa64899223846e7d080ace538e5a80ebb51c6a6ab6b9601fb0ed23c356142e8d53a0e1893848f8ad86ee1802fe63216ee64272eb0b08a5cc1167eafe81581faa81293cec809713cec305a1ac1aee3389bbb575e22ce81329e331c0cd9ded80fa1a18f81f9b48e0d37727d92837911e0e09a72293f3d0f336825ee6a4792c029458c835d47a7a14c1e0c149299b2b28e420871dbe02411cbb26281335bbbcce986b403a4602551add08eb1bee6a3786ff51946814f44b1066f73b764bb6314ab006b9baa643c7d1fc6023c15d9929b4a29a1274ef904fef669208f7582a9ed59d507a06c0821003335e86692879e2976cc0eec10866e24835258f15f924385a63a339c3ebfa6406470025bf48bd6b3f953804f31070ea65280106b4ffad74f0a43a3cb6c00b1c030ac4380c7ce9c4ea74dc1fe90d87f712d434a451cf0324d9ce5350a633b1688ccea8a0114b5ed4ae5b636853b5aefa4101342d24dd7e00eb15d38b1d20b72ba7140d8895089fdf07d2fea1d236d5e10314541e3abab501b5888e5b8896d638f4977fdba5225ac23c549db1dd0c59921883a4406cfdbcb50f63c396ace29c457aa3719e730535bdbd76abfef00acab8e65cc05c5e37e9ca79431cb28b7c483ea23bcc46801fab1645d6c6f5a1a940adf74c01ddb29a9b932276115e55c98a85d79df7072b9e673e43f75d8b77cf0a7dd0ef4191db460995d970524f5a67ead8174f24065709ff152fbc078819b5aa2ddc4741af65f8d26ddcce95b34224cd4d2c920c1bd6676f010c43b907ca3dc51c16ebc4d763ed7ebf2ab723b00b9e52e0e0a85b757888842d13e867a53b0ea1d9c4c8d6197258903b3958259170be8ef7acc688a679809badc2c2efae799cdd7538d910d5532941e9c44bf38248d9c9a56b8a4d88ff1babcf4ca350583923c86e548555baba5d39fd0b43ef76a5983697a83c231682ac6396fcdb43a0cc8b19d8ee835509f67d25782e4e8af40dd11e09f90da3346b221c3d66bb0b4525f0115fc79eb4855974171b0c55fa288f1c516d8fb759d9e8f8a17dda775402a0ee2248117be5256d1e552cc134f5656dd710a48e824b0ce0c85ff77f87f90224b9b701f05c336c3fe9a5a66d65f01e8d1df776b609b432088b3aa1b274e3a4a64355128c0ae23f0bbd1b445a63de94e0f549da2d92443fba64ce90fa6383ee7a18fc15f7a5d1685332fc659aff7a7ad04b21c8acc05d9c9e88ea2eda2ee56f007691175754e05c572df05576fd72907133c41b690999437f22592a917d9361a9dfa88392c38115c8de23b18d9bcf3b99104b9e22245e8c7207cec72d8a93fe72dcb37b220042c55b97038c7f65cc0363dc97f54d545fb8d73751b8dfd8f857a77473c63f72c56df34fd184fc1ef08679aeb6b2c313de9dc9adafb7b896101d311fe98b7f5762c05e986e780e9dd9d4fed715a5edf16f0760e4bf47f35dd1372ddb3fb47da2717e4cfed528ac84d6cf874d710c2a46f6e4f9c72b81b043e494c039b4313720b3eba9f29774374e328deae471e1085eb3445e44479cdc253221b72290538a18b2661fa36d0cfc640e1175c50d66d7d1cd5124b79b0ed067cc26255b4e5dfa6eb45c72facf157d1b5c0dde064e232e1785509b4449d421371f98ea8fcdaf71bd4047f9cb2d9e4524f53dc7ecf9bb565c9ab065db8752b0fb8971ccb4fc2021e5c4d950899437d11f4363efad3b60ab4672ac0f451e8ae4e456d94b5ab5eba7fba2f367d4aa7714b5cea64ee47c1e22139e404462b92d7466cdf2a36299db23da5676fe0854938827bcd1d9b4abf6f0d4ab1789315cca557ebf3f082bb351f6a253d0b2716d15b04fb9bfe594efbd0522703c912fce7a5f2fd0b6488e5bb186fc93341fa069b4f4c22f24bd68387ee7cf2770d8481d9a208669211270fd94f56760f88c31173b878dfc2a1c8a8a66a09aa7d1bb2f61eae97f701137e61cffd48aa7207f6156fd13b677bb1946222ed8cf2a45f5e813538eecc2a2ab6c39c0ae82b05ee687d85da29c73c8a4c46b9ad8fb84f422287e8e619df84fc3809592c851749f3f6a719bba91187e9e05e7b54069a1ce143e16285e6791ec6392896dbf0105b590818610be16fdad59fe1e132c6ebd9b14458699c9c91046b9c341f213dba578bdb57ac3448ca1a83e1333289816a49175ec96ea3722f8fffc7ba0b65909117affbb2054cafab116e9d8605b4dd0c8e84a1af7cc5d0bf57dad175d3af7cc62389a6295ccb7bf6f8c24bc013d5c469cfe47ed337edd6d959742b4bf378c346709d134617002fc84f05f089da61170b48ac584080189d5db6289c6069ac14805640f0ac3c484cd5b8698d7a1bc877699c1ba12d59cdee4e1426e90829c3e34519c9a9915f9708079f9f3ce6ca9026dd4ec920a6f9b3af2474e6fe296f547a773d45cff08ebd0f5512145b40cf9ed5b6f92bdaee378c39fedae569914715408b57cf6a34926b3d84d5324e79a6fe537e08db46248f5eb6fd0803ea5b90e193644cedc9d384dbefb0db7dd405a7118817904c0b673eb9deee0f1f1c1b28448a06017f183d664fc853c358a197b3e86294443f3b8317ed5a8dabd20e3a34168042409f8ceb6d588b40bb9a3b12e5292af8fe3543d6cdcf52febb3b2a2b0e4e17205aeb1e51cdfcf02484072ced797ee22e419f406203c80e6d3c0e08a1f98b4da3340c835b15c69510cfa9d6801ddc7738373622e4a4af38dfc06b0e39ffc27f1f4fe94cdd0d50dace3518dbea5ea02b6b17b7890b0a0642805bce8c706b8b8a66567eff15c3504aa73f23a2e253be09429e8da3facc864e388dc0a890089f073e02b5934b88e0d4e0e128f98f22540e0195fbb47f362ebab4d5b8f55b7cefb52968a0db7401011b99d5910b3dd0876aaa2fad38bc722dc439584ee80bd8b6a49b3ec4cf2359d8b5ebc363f81b30cffde300c4ff4fa42b06148c5f521c4603c1933acd6df47a922233abacf3ec7a4978997c5073ad665f4ce4e00f8ca03e2efef7177cc20b4765f3799f0b217b40f2a67366018f394de0efc01d3baafb93de76db2b5193367516e5b6f7f8234fc95bf90cbb1e7f464d5d01b2a571aeec07d92fe957aff4e8edb529decdc66c8d55acbb6fe16cf8650deeab33fefc7da0ef04bb7f611075786ad5939f0b3a4bed05b62294bf92890f4104eebf388f98b893a85ca16a18ef679d4eff5f0b1b1126f8e087a5d131896c40fd245f59d5a49a5638e93f567990b9ca787264cfbfbf6e159f37c45624d6d1442ec130940e8947ed3a0f5c381e7ee0e81df40fde711652703be20839bf01fcada32822757d084d1320c47403a067177608c347954324f1b992b4cf6c988ee0c82fdbd331041906abd37f13643e935f5caec384e2e2eb3e82be32ab4640fa0ec5c6ff231cd9324255948a20097665655f2b7bae31de963ee80cb1d174de0428039f55e9a99265e9991d178c63dff9fbdb8643d91c8dcc4445db397e3704e81c7b4eb3e6aed02b3571ac48143f8532b3514a5aae7e80644078cda715ea17fd4a0a0d00a25bcccd2fa197a2e613c0e104fda12b419d4d91bad1ea227c3750c27f596af446e87a1bf30cc2c09f29e99a8edbe35e11e641623859e290ca6099a133ff99f926dfb500bdac127aca6267fdc99adbe18908aab30f18533e6295bcdcc40e98ced70cef4fdf3eb6f5dd46fadf3e3a9d4a9419c3c8a0e10d1efcc79cd28b2e63d9eb784d97014f6f8128177f753aecffc90e1025078f3e3fe36c3608d4f4bc82928cd9875d0a238fbe7752f5b8b11c5fc6f3562820bdfbf65c3d5d531830b62c08f40f6b3b8133f6d1982dcc1445e57db960c944be63cc9f7c238a1b7266f299ce61924f75c985b2388823fffbe2180c399604764d1c765826e147ecfceb7673eabe3ec351c7a95a997b109d955e5b73d00370c42d04c45c542d89a5053931c5ae2c4242c54ccd6fe6c1708bb681f511ec89115e084fb5690cbcbafe0669de905bbb38459376c179169163f21527de2865e97c46779a1ed6b7e51f6b558d3260c87bec86ff97b1cfba8e0a4e8e70f9f01ea6ca5f61e9669adb84a8ef3ead79052718e4dab8976979c60d5bdffe6fbeb6f1c56c1f1cadcb1e59ab425223719a6bc72edff3cd3c450d8cea5bd6c5ddef6e974cf63b2c2b22bfe0487fd96d3e809a4d21c5af288976aff5832e16fb78db0266bb59ca483a2bf8830bcae56fcb1b709c0bf61d088cbe6d56133d5df9420b61c0e2975f034ac244c3a20cc5799f94c138d454fd49398128232561a770f965639119def9d028634cce79521ebb507b7ee3fdbe652eb4bd9b99d46f2cd43ff3c0deed101e34471ec9f0072362ffc12d957efa7acf51a215dcc2f90705ff3312a9d6d8528b0a9f5b5a3537973f8a709e1e983092be36d8288c5af001d7d39a58b42d2c3da7a7ad84d345817e7e09552fb400220fe741382df4ee926ba7fde2700bcf1f309707c6fa8fc973d36f425c181ddbb588305c3f27497db36370019cd2318dc07ecaab5e72d16ab285e49a4ecaa8fb0997ffd6f523cc4be66c9d3fd92f45a563c5f1e0c8cab601631032d3d2e33f86755e57075860eec81f0ecfe3bcdc0e4fd0a71db24e73aa2b3c2a4bab7da0c6039868e5d5fdf6df72c45f263513ecaa65e2ed6a8625fca44a7e29cecffb8fea8278b6953da69f4aec4a58b4977a391af1d817803e82325bae63678b8a963b45a3b118e217634a86893171fda7eb9cd26b872cd3b8f7c8496be4d1381d421642e720a4a11d7143833a3e7120c9c94ad27f7f261a218350032d340fd06171df2ab4f27756bae2a7b20a664ee3608e710f0d48a5c0017d8c2ed1e0e609529447276ec4322bfc6994ea9e483a512977d6a32567ed960f5a197adb7565ace31f56fb5eaff29d7de22ba814e86edaba38e34a5bb0824f500ca9f3220c4f6e463eea07d720fed62afb34ab5e7a38a98170277b6a33b06034777d2a0d3e7eae69d560d9851ad6ca3feab0c78d481d05db412179f67bd62fd6323a34e9ad1f03841beea28d9ae66fbaa8762978e72a959ba3de541515619fabae3f9acc9859399b6175d85b293421b8954c8825ef0f83f60b29f1aff7e2bb72c8d43fa3cbb9c2477eb20c7f3d82a5e45dd3000c6e28004c6b33647f19fb26edfc39ac9050b1bce813ec7538a3bea5d3af9b7ab0ee571eeed5175d93946fe574e72e265a6700a7dd83e6c0a4675429ad0567cda9b6170cf9b84cfc2429e7adb5a6afed73bec052461a07bb32b6eeca9b87c7c4d1b53ec9b40e2b8f605f3431de4576c45a093b34ab315b79fe9af376606671f06db0538018928eba0c4a6dd35337fed8b9eb33cfb5bc55257cf02f440c8d1aa5ed201451ad1e303d50ea9f528fa2d6647898fe901f9b78c2da73bd8e079c33caa5e0464c9f6a56e59a648fee033848d3afe8312e326eb53038b0ca97df785952cc8c219dced55ebe73917161282dedcd7c466e5bc1275778c16e9ec0fb11ab0cf4de57faec5edfd89eeabb57d7c475b7d82fa624c3589bc2b2482fd186a37eb575d052e83d422a96850b3c70d1a8efd99acda59d8b332b50db6e22953b7fffdf97ca2cff410ca92ac4313c840af8c05ff80c49b60138934ed7bbce02324856a20e643aa6641e2422250ade4e440b2077ede2ccf020115aeb2ee7f6086ee8fbb2b1ea02f509949ed14e52020f6ac4e8182e5f14f31f15867168683615fd1465b38173237bde655b43650b2f973e45f243802b44118d3b93051899ac56f1d2910d25833d32bbb6f948352815ecdeb69a9eb72813ed72413063aa030bf54aad061dd2cd50755d00f138125150f200b80bfe7edd2988557b88d27eb68e874457dddb8a44315326cb0ff0a5ce78ba29b1ac2e65354c15e1bb2bbb27af30127ef6c14969380d9c4434226959ac97fb3eca3bf4af752e5cfea20016550596e3224dc10cb0754790b1cb7cb3d9876128bd756719736b8ec43dac8902a014a0f1cc18554be0332f05de015ebbe4d989addd58ba50262d77a47c9af928aa6db2431d0708af369468478993a7be3ebd68b121dd2c097d8c7e33c9101221ac1c8dbc6d5dc2359d501a14202a58e93c5bdb09ceca238bd3d7000fbd4d64bdaf3b544f29427f13f3f75c0cde1f744a074b7f47deb320eaa2eae49a0eb9526bb55feea0de54aba33b202dee3316b20f3634c51fc017099d01a33306dfb223779f300e5ea5e0898f85fd4771428fb2ede773d8c94f6680d68ce68d8453c0b5e1a921e9b673ba38e8221604f6a73accfd6a52277ae75e89fff024cac8de4bd980d50c1a87e9f9973ddf472ce173d1788f1fea0b3f028c5d7a8462da9727751076fadefc609e8368bc20e63204fcfa69b804c7555c86d592b7667df4978aedc38f78860be789603324a10f34a9ea33e4c6a27bd8b3d385fbc69551ffe2006e6fd84f57c8cd3123a324b0811ea04d4b3ca541004ccadf2cdd91944d572953d60c7857f52c9ad20b0532498401691b5a537043f55a68341904c8bfc88932564162dfccd24e09bbb7e3a3de9c2de1f0614d5c66ec5c7c24fb55aff7bb042cf358fade3d0a4dd8768cbaf5d1391d9632bd6a9922164f5001183896eac934272c40fc6c177b41a5be237dc6b2a72785118616b479ca8d3384c8651f97952827650bef13caf2fa321f39df83e90a66f3c0bae016d63714f831d66f6b33d75aa4b7cddd3de422b5e3940e52672cc9070433037b05301975f56fd910d65de0e17b0e5e68e72c20e1e2dffc6e5376b5089db9ffbdb669e29a6b130a8c9a096dac3015f3b8124a850af57c06dfdc1fdc3d4a26a1b3c8d98869f5bcaca257404c2c8934c3858eaefbd272de711eb2835922e559082c056e9b254fc57d552c9c0bcf238bcf4739d4eaabc8d115237c1b9410e9ab63816ef798cccdcc86ca125a600179384614eb69768640f94c1ce98e4709565cb473461006d74d334342dc1b0b2cec6c338e58a9635d6f4f3e579984115f70828ebc863b23d70bf018b183c99fd0b1e0c5ee133afbc6628cddc21826aa1548bc427b9db52fe627eb00ab9e4cb16b6d5e5ca7a057afaf6ab4fa47b054fa60f79fe35b674ba5a78c9c704c7babb12c414ee0c8e3f65ae1eaca8dc294f78a4771ca89732d344e2a7fcf83119f16c174b9087f2634083adf1b74fb2e303b9f2afaad6f831a53d5f6bf0873c5e6f278ff2e963a26582773edd494a617aa7fa2039909f34100c62e1b3cb45f4899c2a941f1a05da10357b3240bcc2a2901abae2fd5c63bc80e390af1d5bfe1b76db0df1a011b93945cc8d4b0f4a94837e69cbf5dc874a15de5dc6364f62667784720d4bb547faa05faace1f4cfa7bcf1bcc3191493d8986dfe8a5f6e0fb7ae929abda3cda287cb1de4fff2cc2b061391b06cc311dd5f8df69f9697504ab036840f0f96f903993419e761b54cfcd2fd3c6e479c4b52d00f2db72a4652233774271377b7da54e8846b71a03ca4e84892de68b774214115f374030934aad125a7839d3a6fb2dd2a43c9ea355802cf7868c363240b1e060a7b35b6d2face24114cc032243fc6d8d18451e62642f183adb66d75860d316584c20184aea02fc33079237855b4709a13e026704926486d7c42e2b295645a5ac640aedcb46b9880a31a9d7ab0e3ff8a8d03945a02c5aaa60571e5b2da1e1c5984d38c563c1a6a49a7cb80963fc18be2ba6d36ce52b9a781ff92257201f96037add69da196e59de4d0e113bcff068a6c4a32093f9c59748628c9b84ebd5c5e1bd70f7cf2bb5dc7c310b1ba4248e5f5a73efe2f620a94dd3af4ddd1b829565c2142e006b9d33468a0afbd9a6cdd07e78067b6f9f4002251f5daaa140aa90d4845f0260d3bba4ce69f560cb44b5d56221fc1d86c2512830d3c91f8c53f0e6b2ef742201e28022cd9817a1cc25405975391b5aada165ace05c0b9a7e8a28f586902f1c5c9e13d740f0618863396dd76e84364351dd47f31a1451a138b6c948d329d13219bf128d6f9e01af4e0144d9bd660e3f4c1e5ec3b239304a8d0ac38b92cbe6917d34a471715abb0d06c88a100525409bde4991cde593d7a991f5664f158c9021e2306ccb10de898faf038007c8fbdcb74adb777c569c418a6fcdd80a41ccb70bb40357251d32b1a20a88e6994a9761c9c1ff414c74145e3f0874d6c1107e13654d484d5e4dc0a9ed4cbceed435e19b9586d5a698591b484cee2d55c04604dddcf17ca35bf9baf5105acbaeffec5b0b44fa9de0cdc19a6e77f3f55ad3b653ce21e300e4b030555a82e9a5bd14641abe295b7ed404699953177de73213e395dbd64b64f88d6af2e3842a7f1e8643619b55441f68ec06b17b0fe1b63b9e634c33bc2d39514eb84ff9a3d106e90934983fffce854f8f2b675f55583cb2cc611e8254ad2990c5e1434672b5706a82e4e2d8f14eed547d3dce4d6cdd4af5b9b6b7f96066d02db3668185c9af869bd7ce4db6303e5fa1167757656e38f114cff341b8ceb96647ecd8f76d6542a716e0ddc7b569aa657b35615de7c0d0851e006275e91fd758da5c331f1ee27e21f54a866dcb757f3edefdd64f0ee263636718c4fc529e8e4e5dd363f7543ea0ef6df985dcb96cbe7d524727c291a805f07648f6875b48c741a247120d8f8fd568a5c8845946cd9a27d3ca774c03adf497186ba5bb390182f3864b412935278230db36df2f8916533e8a6703213b50ceef9428544a54f5e05f89cbc7b747f5492171ec9706c8d252b7751f65e58e8384f0b6c9d118d8a68826555ae54676b3247132f7ff8bece70e2e45f9db9e454e28e6667834edf10b2f88071051589b3e73c4ce89eba1462ea14c1e9c86910fbeaa09a30ac3483ae5bc12b75268970308ecfb3ca53d68bbfccc80e617725bade62499a98f366b2b5fd8e445a8aae059807657051171dd99cc8d0a7298c67c43fa332b990204c6ce474572d2a5914342ca99b2c2418bce38d077a9d0b134a5204f68473ce4a2d335361d495fa59e57499d750e36ad03cf36f264893ba808604cfadaebbb0406eacc4659a7fab67e524ef06b9e56796db49242229265fcac8b998996c9006d90d5f6c71a3c080bf230fda8ffa0de99c60adc6dc00f5486d0bf1aa2f64e55e8b0a2a03d5006eb7b63e58529413577a268ac4a8beccaf3ecc2a2eca1e5136e6c471398b879a63f85f6c172840957407104a3b552f10212166ffb48bccd71945d647cc14bf6f16200283f8dc546286ad95ea443884f4851def29bc45cf4aa7e7992d308289ab4d50393fb69af5581501bc8c908a45885feb1248937c3d4251cf80392cd6ace4dcb4265f59f7cd1104e42015d232c5d8d53d1a19c44c48da8764a95a20028d2e6d8b7d86047adce86c5a82c3d40d71c1736c8189a80925d293bdbe7322d7c23920dcff8cfd5dd22fe3f31e619cf7e14cf27c81279e398218ff2c3e20777cc185fee37a9ef77922cd9ea1831eac06c82cd1bddd757937d602233c6c5bb4f80227ccb923a56e1f1deeda4cd6f78330034576eac5b8eb10df44eb89e210624981463e26e726dd3d957e3a0a34c20893102aa884e741010e0ef2f97239057f5b8f5fff7c6297a6b57cbbc0275404df577c5d57a0cb9c301f863552e01b208a1b52bf04eb9ef84b24a5d218419eb21fe0636313b5d6223c6ef4bc4e26e01528f7279a33bd86c65b7be21a91de1fe7e2b8df25670446dbcd7e159ed5b1664741ef4dd286d1f874a8206cb683ec4e0d81127b82688bf563705c89c5ef0cc031706a67e19950027f43ebd8578a34d015c386a993bf6ad2331866048980f55a134c49b9b609742bb380f6c12607fa3d2bd35c240bf762cccc60f1c3be36d48962ad5c0be5e6823a624a8e42b3469198db4b110008b93930fe0cc81659a618c23be6550c42c806e41e91f531c7537b3f07f8304b7445b532ebf87301543f047448a10c1cbfa1b42b59be5d6e11f4f4742f8e6c5e74ab8fcac7f45c59be21af9f1ffb6116325bcba4dc10f44382e80800741d1ba65fd4abcafbb648044255dc235ad5dc4652a01ee5c3457cb86cb1c58a600b973223709cadd8ec0944b09c8e926f71be27b029f57982bb7eb6f90876523c639215c6a5ef859b908cf85b8fc17b529aa052dd0fe160bc74eb2c8132cf54af6192ac0c3511ef8a3501c24c800a3b505356781d452ddb928af67e3d146e9256f4d6658afc20e2e1b94ed3b60f9cc8701ee197bdea2a4e1ac5e690dd6a0c94fae92ec0c223afaed01a59f1dafcc6d226d044140c2f40e45767bb141228394111d7b51895d73181a596a9c148c45e9833681e3275cc48fa41f6be829b4bcd276b2d7dcaf9f30708d2d182bd7c0ddc2066184b5a4c9015d161c944aea8178a307afc2a505885dcd20b26086d175f7c133f36bad7c19c0d00b3b634a13286b80b0441a43cb011421413fbd7aa3f5b11293c9e2e3fe21c20317bb9e6b11ffb2f1da04ba802dff62e538e63dca6914c20e9561e2e93c6bc5789d0f1414d2060a521e73abd80c6e91532ced6d3f68b6357ca0fdf751d223285a97d65eea48099836710bd44047b2a56414eb37138246c10333fe1ddcfaa4831391d51f77de37b8e3af01eb9e794be794fd9a500b84a1f45e95d7f2208513291618e2c18b07db7f5d4610285aec1e09e55af3562ac08e0caf054a04a393129a175bab25a5b8a3b7093eacb45de32b0839705fbc75b4aa66c2c2ea23078dc3b9d8aea47a417724b1707f79949de71511bd1ff46ca79a275c971035fdba0e0b97c4bcb742d8cbbded43bc9d3740cdaba2ad9966cc5f5219de0f1403c5848e27beb1b1b581d7f1359c05e5b306c70036a3a093fff2904ded432e511e9ad730dae52124c0d66147d1eba3f5e6c5c9d495a69277425aac09a78757919086224cecb3742df24c61e15f2221fb0d81c78955156ca92f37255ced862793febf90c045317ded9df18a876fdb6cba766a9303d9c4d41ad2e1bdb6f9ba53d72a51fa544b3c7ef7d126444a67053e2dc409f1057becfe45b9313176e7bbae26af5e76abb32537d1b8d4bfdce9ddb97e16c5f6e660fff4a31b42b287a7ddbc40dcd5d601f043cab247083bfb68cf9befb889d4346dd5c08a57d9b9671a50cc32bf4a5ee6042c42119b365f64a8ffc23d78b0ad47396a71692777020a03a1a3f23a500cf2296051a587b40eebae3707c2778abbe70c385e155490b802e2e0862708e925daf7be4862092ad9837983b290a91e6bf3e7d24b7db9979293054ca613a40d9f7960a29a6b5a561b84f21a9d85506b9b3b2f006eeb64aaab4c0854cf62036fc0f0f8dc2b1cf10c24f00e0e22509f1262994f132f8c692ce17662bbc8a641d27e6ac36abcc98c6f1b7cf14ba840144caf9762e3786dc967321b52a2bd124f6319d52f525313d8dcde590cf093fd9b6549898f4bf0b72c837f42a7a2f535e9fb8672c6e8e6e8798b470a527c4c6f5e91af7c20c4cf327b8bb85bb85ad3a6b8ecc8d18823dee2c4df34d63e7813ad07986fc6817e602e3e20f18fe9c36fe4304ac1f0b97ea3f5dc42da9f7a4369ba5ab7b960ff43b3166d95f3e9ec3c71f2123b32d8dc748b1b76b33853e5a6b0a31b228b9e639dcdc7d3edc2a6932bdee2f63c9c1ad19d85011dcedd683e0c85c63b97384562376c2e647d28c7aebbc98125ce6733fb9b98f857c991d40d30580ea5aca4dcc5abe294945977b40add8783b54979f273f662157d07499526ec1264e875e6944482d85dcff417fc00f93313f1d9c07725d1aeaeee5cb3666117c8e3bac7a943565952d361954a05d811da8325c874196891ac32f17809702dee742eb692a641c9e666865848414941c5f320d9a9812774665d3c29794ee348103783e99b2d40dbb678ad9e0c8aa00e0a5366137b799d0f56c23d93e89657b872d1258a1f0714777323fb17746c59de0cd5a43421f640148805ac2fccc8479a1288180286a90e55e4a58ab4840d638af77564f0a57d6b969fe5ee6214e44766dab53379319911af0d7acf8521f283935112a36c3bc10288c5e679d9845c554ac952211374b0c70746d39be98416fd60b69e543f7dbdee5ac48fba34e108401a4dc9726d03624d3c95988f5fb7f703d0b02bb0fa1cce6b15b3f77c2ce2aa0c6f11bc52a7138e3402d6d8cf73c3823612259fa3496d702545c05232c52548a8ed32ab66e59e2a4569c4bd56483c4dd77a7d96c076a5b0a6253acaeb834177cea77b668a80c472255061684ce3609e59ab158b00ba07d43e6098924e9d6d2913dcfe8f942db26afd351dc72d03329c53600bfc44c088a013103c5f32ea56f25c5ea8d8baf59e62f11af27834d35f2c58de50e894cda645502b24a05ef47c636207a05c40326bf97da45ae36307682d1d7d8203c058d3a2b7ae7e6c0e748a356cc5779ffb276f127bbe7ac727d348039daa1de71491bd97a22418d6446314e8e6a3817a47462ec73571ff54f50e4338e4919dbad5e737baa28ebafc8944a9b08d1c35414cad78ab6095b2abf025b92429454848b3534fed95217ce4bc89cf904982c36d2c9306c352c24f588227a03702282719156f43acabf03701e70fcd9ddfa1755c7b6ae2863063174970e0e7e33871910c52a07841b8ea7b904f32db841db13c3801d5eef8a8b29d5a19a5dffe6304421bc0e43c5ccad3e2a397d3b4e05a5dc347bc1b472dd0e4fbccead1091d32778824e3ccba61eaab3333b291d8d4a8606ca7f1052af8c2ca5af5e95c6d592c4ecd4533f87ddbd15fb5d9a9836df0a168c0e6bddcb4a9ad9f1c15dc3fa8468675a9c314ec7e10d317a7cf50a4438e7c70318d8860dfd28778ded9731df60630b6d770342faee71b4da4b0af3f43ab05ab98d64558befa70ff3035bc18ebc4a26af39ca1b4968b24c6382b5cc9a9ea6729930f9631bc2560f918624df8332f3306a4b2b1163c97c2448963bacd471cbc9996fca313a25384c12ab01ffe659a991ed1f3f102cecccfd3615f30684a2ba8b4e41497662499f84416c1926156176a7fd12e66a8db6414aa54f9b93520c545c586573408dd516ca90b34ce477500e692ee12b33ac26c65d0315092b482879488f512c5b8ddedfebc80c28fd5f828af199ad2e5557ba01d4e21b124a674bb4c96417d52e1ef4a2701978550d9d4c954e5ef98d5849a7bcebcad42bf0903c05bfd04a7a07acc626ea636d827f0c50a361941b1f2e511bc7d2d4da0deb109abea9b54a0a11e34f8b2e07672af02a21c8d428ef9459419c352192acbad557167a8f579d47f8fa47cf61d62a04b0fcfb4fbfd196b72bfb479892cb5d4dc200bebfd7227bad1d9c7502b8817b6ebc6b2a926f6833ff023d309eb113659f708c7c6990a5a5b675ada41252ec82f05de195e5fcd2ec8306a89dff45f6db74de1e580e4f4cb4078dbd0052bd1db9c94ae327ebee5bc6ab5ab5c4c561a25fdf1cd7ab303c0b39de1a3ab98627d49393130c3b42309300e62f16ded06ab2a42f82d9a923d5c9097c881a266d4ea295b9340a8f19777a8fa60ce6381a10cc91d0bd50c8be9963f707efda87151970d78c3a4f97810b6a4d0e34d5e1336ef1b1b74865cc5255a159215b73033596c14af61ffad57e072506a6026b2b82e3babb346f4e75729085211db9b6959682051fc75bd0e802f60133e55631a16a74598730dbb94459f350b91ade68ec246d9544239a0b15b5faaf2ec0354a73c8f2c31f147280dd4dbde5a90162ac550beb4251273e3ae757260dd4a34ea17e5965f54baec949e324324fdca5e7f42d409cb886ae416d1ada9d0469126bac2f1be60abea566c1fc0522ace791a476841d2556d17c9ff0951a6cde72813904809c6d75cae861bf8b5a2544822e3b749349c79cd3663779d91777666ec42330955d72d07146cf13a81564815f849d5b18b0cbf1cc892fd0f940cef97fea4f6ec23e77bd7d7a4d23019ccbd26eac29d5364d4b7bd1373080dd3235417a6e71f35b0e27b2649be8d29626988009117426c18e286f1229a40052856fabe0156f9ef2208323b19730993ef3aadf2a06d6b01bf936731e1156377b63f9ba14f5712faddd76cacd7249966c1c4c186bd9378d589bfad96d17fbbc11ca5742a87528e7e7b376390c89dfe6bd0681e3d2a344fd2a2123f29adbc71a61cfa08637fc34ae69336a8d6115e21d08833bf329f736242a9c536d2f92ab97a54255c2eb46543a010cb53a4c363256eafe75ed4c600a87d6719eaf4f47221ee5604016ad4d123fc4f09a2964d6a36bcf3af4ec7ec86d18dc5541a2722d594ec2c7f7234c53436f2ab86429a189a42f64ffe44f26f250beda39a6db4350a08f0c2e5bf88f95203b214f0dea581f6f8d4e52aaa29b1b655a9429997c2b58df541083b7f7e94153544523fd9915f73de6bc51125058b09742de11b39106403e36eb87390317df4cad8689f0136299947d038c134f7d71940e12f78e42bb91d4788e1ffc92efc82bc6aa59105a61503cc611fe054e439c67cb2840170716ad02f3b79db4cbeb78026b30fc95a48bff6d643fecf7146f892e75808da7335c704390053fdf10002a1fd634f6cecb2c4e2e5a188e6f96eb2a8cfaac2eadbe8c05322e565c1a4f0d5335cd11b1acf5560da4f33df62f61dbdb68781b38adbf7e5bcbfdbf30e47a95d06b240f1845e104eef9fd72e835f322b4b149abb6926299c3167ef16337a5cc65573aa5a1d4aefb3fd5b9bc87995dafd48785d4c48cb1ec7d440c26b07286e27a2aff8b3cfeba45989fe30a269d0d5ae8918f1cc44cdd6927d49f61e18cb7c666352f8a5a9f99f52ca424744c556c70b47b6570215408ed42f57c34021f1e8deb2d45a086734c041561b0c3e0aef231ecd9e0c2f8f89d69fee8f918aa3ea943b5729a2fb9e1ac4906c5b9b73316a3511233dc7da89e186a8375f2bee77b3f41e453fdb3bc762fef9212cfa8d1c97178fff0047f025512674ae4c3ab4243cee8da2430fe2358461a2a217f9da3b56ec4e21bedc6a3f9a2da8e339bae8dc09f142a37ab73bd155b257a5ff9ccbea8d1a019f6cf390603c6e37c211b665f3aa4bee82868277dff5dd24fe19f1d85da3f864761ab0dd6ef11eca7d42eb24bed8a6a57522ee69a16f077f1f4d28bdca931080b36b10add592486a0d9e4bf87b72b87441b7d2967d8b31e3db8f66715e62791f429cb1ac87b65228bc420bd3783353d2e38875ddec4bee867b90877da98df4d53bae48500a7959e891222abf11db300d00133c8cef0b9ae3191374b1cd47a56d6e1877af0082a9f33eb45684b9181c98e6d9cf60d0823f5505b0c4b7902fe5c68a960620ce79d038d42748423e8431bbf1d6342dff9cd4986fb38a4972e5bc6912baa5dfe862b949c131fb393eda5df8745e7192a48028fb32b3b0dc17ff058688e0d981a646e2ce44494e05f5fe652291255438b078a97d47b9bc2d14ca684e5f67a95ef2ecfd855b5f78e5a3bf5bc81556b5cf42b6e57730298335111b2390590da63d9d9370a251e0b2509875678e9399b4356aaef8ee252feef06ef8bbb195c5843ed15a5fb81b575eb9a98e0af25471c90f0ea1bd73fc67cdcd9edfda38ae54c6ba29729c382e3bbb24d3ea4e8b1031e145ddf3ab1d80788857e6acb25adf75c48d5fd6b8b7370b90f6d48fde9354c014a5d61440a3b8ffa06398c63c060eba5f84e6d043af7a561e4c0556574116de22a25b21ff271edfdcfab946936443c6aa8b84eb66bb64292e22dc5cd300064a58a17b5bc3f68aece2ad108ae5c6faf849e8f398809f89c76e4345c12f08a3966c6de1ff2063c776affcb761ff40d21fde196a5bced89a13dfc1140bcbe9f07324f01af4c9494ef5e1b7e38f9524048bc8f3aee88ad7766efd47ee6373d580a8293b842849908c051666f35fa15f409206bf54dfddd3e682ec03b693b62275a6cc8a3623ea0080aff75841acd7490ffb96ecf0fa26d766f6c54474ce575535bb7e91c4c9738541f16ff3c3fe60359d502e12d6c7e129f222b9d19d49270e22264a3b1051da59104762f7b9d3d941bb63428a568b0330c6de3aaead54925a3739fb67e6638c50ad36080c83ef6fc3f06cf20c6f60363d40b97b33302e867a721536f57828d6cd76ee10148675d0c2b06ddcec51d7bc0c1b6ab87ecaf48976f022e807e9567dfe56ce4999406e50f65380190b8b62eaa66c37d769402c7d96d127ba87844a10088fbaad0027ad6b6ec91a6efd6860ce12dee081d01a84bf7206729f1a508188c5b64f42406561054fe207c5006bb0768996a0358faecf58cbd3304fb930d92571614187b3a75e43970f1f659759375650cdd4fd6188e87c29d3bf1235a7c11be88b679f2c79e4bae1c0c180daf593df7c0eb5a072aeb50c8e32cb846efb183d89621fd105c2ce054b214057d8974d35fa6d60633f64c763753187fdbc81d2a7d13dc712b6baec132c7467d927d7fea88f2fa3dd48fd013b6004f2fa7c994f23b492a835bdfc5b70f37138960dd4cbfaf2d3807a103cd69be0d343fcb3b8be473f758ec6bfed5563f5acb87e858025846f21807a7ce8b59807909875fdbb4c00ee48ec19411ad49696435a23a3c396f091e56b44168c18440b3420ab708085975dbbc36bb68e547c7bcecc7b7167cbce3bf61230302147856a61a767410c306c1a3e966b5e85dd16d2e7f13abf1c2a2c79e9762ec144def6e0e5ae872d3d3323e7b7d1356949e425928f8af25e3ce46f2abd195b97276507a036d72f0641184c9cb2337a6f605393756430ffbbbd7be75a95bc661579c692466672c7dbcdde38e44e9bf2b8ebd4ce544e1ee38081cdabd3a6f4105d4da661347019e00b7f975723ca1ee86049674b7bfc739d2289de3a8a1c566c8c0a20ee298253cf38ff990dccf6111e2a5d5a8658c2feadf7069185d130e56bfea564cf80c5d86d3d561a84ab6bc151d42f5a2fb90313e399c1c36c3a46d520db5e1d122f9456a341a156e0c9172c4a8b7149e0382efe47b0951062c9a32b518c7949263ee0471bc7a4758bdab864549655449f982aef4b75baf3577abc5479d7486fc483f17f3b5d4c9ab473f90c3f75e4ce0bf6370a188abfbfcff5848f9613bbefdf05c5da1e537a3e01d8885afdf7bcbd1ed6461c92c9fac6b1f9491a24ea0ee5f7d8f4add3e1f924d9a7494b37522b52e8482117a8d37fa95996b8affd13dc6bd85bd35079e21c2114b00cb3ece2746d389f332c8f221546294ae074c7a58422c976e2485d4b89a69b7321289f7793847d4be623e3fe961c487b23021f7326168c53a0aaf7a0a528f07f7ac5de7043efe8fcbd572a1da39a4de47b76c5f61f464d90e3777e1a57702789b4c528c4dcbd21178a7a5952e4fa1c6d81bacc0cb638f77716cd2a14f91dfb69904aca36784e1ecb5320c1be1dcc4ce8f4fd7975992e552aa50245b12b046fcc1d5a938131bad3721ed3f6d054a4345642ce8f199d8d1b3a621df5731896984e20706f2dfb64218310c60e7ebc906be36372cec39fc63712868be81db479696268bc169c32b62557b54fe4b7bdc979c1483831738cab723b229440685f438455e7574c6b94884962bf97ad1d335915ec31833395e0b8fb6be3fb8cfbfc2c678adaf07414519eaa047cdad61ad20820f4144fc6a5a3cefdd5836dfbc469acbe77dcae705fb53fbe8c20c065b534a02373245ae073521a65c480a12bd02b4aaa7d320e7197d83fc85fcb0262a95bafa0a0569c6c19dfec3509c73eaa2ab80fe7630894980e304e263811d84c720577d264edb452c751c8e506f3c8473abe641e34e5d65d8794130daea37bcd9a01f553e0fd4fba9a9a269ea430ce001a39ff909cd05644cfa596c4ece5e4850afe9d60f7abb7f93a6e7a9492d34eaf9b865df9f60ad830e41a8bd397cfb22a32aab66ee91acf38a9b93d4c3f92ac2a14692a31773f9d698406eb26c2306295b6e359c17f26688cad922ca5fb0484b2243ead1d680029b9947be80ca82b4766f324edd479bc98983ed007ee84157e1be42cb2f58020425c2fe8ba328ad38d0787d7897a8362f48800843df56c00fbc07dd780ccc82da5bae14a04794d6d39efbe892c2744dc2930e1cd1047c19011fd5eb4761b193dcdfb48770b2c94a8041d65c0da935a31d6877ce577cfc254c1448e9353d23ad99a51089777e9e033723746986358fb3aaaf4b36707ced0924e959d250b66833bc7a84f52496d54a0d5af3ef4004445ad754ad9ca927e2a86bfde559ab2609462f5ab96a66876bcfc83d96ec6a1f025ba748669b716eba9347a63b911f0eea4d0c8bc0d0029c3b2f4f9a00a335892dbde2dd0e89ab306cfb739e4542a6d32ff84392108665223c0b06fa192d44db4a989cef43cac0f9ac5d4ebfaa6a3b6fc4bdba2566f5494932f8e3dd154e611bee22d44ae382c559a163154a27231ce1611d1aa908bb40ee6a95572cf8568ac93386169c126d0debe12e2758551df5baf511b7975341a6a8803da9b1c1d9ed20984ad4ff0cbb9de9c91f223261608a1bc1e10baeb099cf6ff3495edfbcc1df597888934789769abe49f66991e67b873141fc0a439ca0e8a8309088fefb5965d0483af36493698da04255345d101faa1663fc33f189d26c8841b113ac061eca444ade90f0c69913ecf66720f2659ec7d3416058133369928d4a8758ee38d03f9e11176ead1640657be1e36ed23ff9bd4be5a1dfa5dbb4d3605fbc0a8e3b0d37752bf881a47af6b7b87b33072c79d6d761eb03db83e9db6daa5eff3ac09846937a8bf1923fbec3a4bf18e2439bf97ce8f0880e7bb940d1a6160599b074c0ae4b4913e00237517ee09b2b5051e9fbb2be41e590232031499999a0ea0aab7ca806dbce77b2833f1e4c63ea685a3d2cf96783b1c2d2d6bee5083bbf8d35b1cc34af6371c63ab2b61cb348873205ba7cf8fcd19986829dbb6f4c66c9115988ee16b4355bca771ba507e49c61640895a020a248354efde8c17937fa4d6e1f26592f9ec660f0762c31159a367d420dc94defc381900818447771079d106ad02102cbe2cb41987db89cca9bb62b8f07ba82c11a0ceb96e32c022cad14cfbf9c8a3bdd1d2cc4b940f25878b5dffbfc1e39e71e631cd662517c59721a0af24c2604dbc3e5b74b4d0d892856fe3ff7ca701bdf98579a3d1c1be3d70912304632c89d82dc4338e480eacc59d190c780f23a008e46cf270e997e00a00d3d9ef4c3388873a0dca7ef21a5926c5bba0cc2df706d5d9c4c050f4efa41ea595238b936a4a3dfbf01d85c8bfb3df3b654a1dcaa44374b0823846f3ed2b71b47ba48a76852035d0047bb8e43ace451be73a8ec6a6b51e94f4e7115c244c87266166944c40f7f31256c8036c1c4d05e4518a814ec2cafd87ce52e5e78a4160e0abc409c4041dd228a72033afd8166f740c451bc4564fe0d31c8002dd33b2778ee164263d7252ae350489dc98682dee6a40ec490a15f6968e940b6cd7e7b2f0b5afae115a7ca0f1aa2a15cd60695611c2aef85f324606f07cab3e184e0323b72d160dc41b0c530d740973ba3712b3a4607e61baad104fd67b44997fdf534df5b16a896c8729f361551b21445da317bc4c5e8a038ce60e829578eae0e462cbb60232481bddadec9d1ffb7f0e6618b5cb413a6f4f4581a023a11eaacb221f5a3d0d2c980ccc58b46825ff1d2262bab837c3d454bc4b13c985d68d360ccd1dc3a4a49f4c03d16130d4f7316457c90d7507d09bdb4aa445624c2aa22919d5aa372de807a4bd7b2074d5a9ca82cb9a4fbaf02f8661fc8e832293eb9917019c4ddefd42864872cd4fb02edd6ac95d4aa25a60598a1c960ce41aa465199d16d9b4b64916f003307ebd2d8b19aafefb9759b3a400aa56924b0200ad21954184fe04eb6f6a12a0f6891c652cfed614b42009f212c0b858de7105e8783cfb55b875e22094ad6bac7b6ac9070fc1280f290b58974b276ec55d4334607babe57396d049afb78acea4250719d8e2ecf552ef671f6638ad1084a6cc665a87ecad476375d1da96056f8bb9c66a44b25407ca349a8bb1f62646de4d99d03d24505f2ecb9ce61f59bdb21710bea6e4dec9f56e475205411a80394e864972de15024dc357a3e62d0065fe688c9cfb02df14106975f34f5ef976ecd16f6cf507d648cff277ec7d5ac04d9122ecab972bb8350d4a703b7da41ef18f9c518fd237ee4a8951acb8654ac1cd2232de11f9984d3eab547f1d549d45154981985871a1ac988e7aa6d24890ef1fbf9ed2152700c70c27fcffba64b62fdea99d51b275c3561aeda575910db984fc05ecfbba695401f6633d4bc83f284ac4ec054178d017745f51821c2d1bb4dd013f72afe645ed69163e374683a43d03e6e147b42a7121e9120bbdd29ce4920e5d6e821960d0c6fdd3bd17eea77d9e53c3aecc2ea360c637d2a4f1b6bbf2fe2db399c051fe4bc816ffd0fe89b876da9811390cf84fc3e3d1dc6cab535c5cfc80d0df612088652df1615759505cb57b1b5431ef11de252d74dc54182d95b3be713aab7ce4886bf36afc71b546ffc091afefd7342288e44f3942419873b98d930a014d58bda44bf6fa56214dfa5c31145d32d747d552e272a9bc738556c7c58b39d4ecb2f54413f25df646fcda1d1bb2566446c566f4423d0c8eb7ab687883e1bb5ffe42de70dab1368da95bffcca03f428e9841ff8c471890b16831cecbd71f9b0021a6cfd0afe96112caf154634201508a7eaf30f731c7f7b99c845a282d3086e57c10d0ce18388639749a71341ec980199dabf46a22e5a5c3e1991d07eb153eb7294bed40663cd847d1e5ae333c45c5a36ada5755a0356734539fcc22c7cff23cc92dead6f1bb05bac745e77349e6324662be6a4aa180c2b9ea2943908a961b36617de869d7b5b4b73b2aad3e67025c43d11c3cd8b727ed1002e80fe93107b764c34422e57893c9c85917a8fdfdaac91d837d132b61778c2f0767adfb01de248fd85cd8fcc08c1cd40d5585a08783d20c5e59737bd031c7291f56cf39e14e13f962905cd55226578f8575eff9221d9c52f28afb3283fe30cd790196ca807b28f19c926bb36888dba8c1b05f788f0e99fd61e1807b62f6510100ab616d7b8cf7001ed95cd2f7fdccccc17750f3edfc8867d75d46c38222aab9777b35dcf4ac9913bbf2392624a41455e36c7dfc2539f5baaf393246a0e86c563fb53cb63be8a04fea95b9e5540c7a60f4208fa1518ab879019967f102777b38b85acb2daf4aafb4feb1ccc1759c5e8ad657ac3b0d29b26f414a08c024b5249543cfa3835a17ddaae4d99ac21c523175f109230298f1d4e454574ac312e055825d4a07b45239b5b191ed937222e2432a4ec63cd32a131662ff9cd99da1962f50e64d81013c83ebe1e053800bbd5f41468128bfe85c2ce1ae375c0771709e3ae7c1e23d16b1332a932f8ffaa2a15c5d70b274c79911590a7504c1648616bf3c6467e9b4cf20f6be4af75588cfdd8a5c1945ecd27b2d9f381c830d558354cce49dfce58f2a40b7c9af48562960b28c8cd67e3c24cf41d79643448e9c6355935b154055d8ee1654c1444f396811a3707443dc174941212dd33a520a95020695ac0652ddb83c7bfadd974bc5844dcccd807b945d97e35aade71634bbd1f136433b8294d98238bfe7852826ceb57e0f5052d67210f367efc35a01161af752000e21440128d38aca4b667f897ff4f49c2ea307173dc59094a2eac98fb5e36f545d96c69faf122bee13967eb214362283a8789922056a5e5b198270e3b7370d83de9e2fd980613e1035bc03dc0b4f7fb7decbb89e599dcb6fdae5cc867f56afd6c4be5b359c8cda272e55cc2bd35fa42f3856a8ac74c90dcb770744cd162619b29446ed517723dbaba7f84dc9377115c34308b3b6f8f0c51976bf4f53c41def35fcb94f420c981d4e27ffaab73c1d0d809865aa824348c844ba92ae4c77ab2024d6385bbd1c102f131bc20ad3b0e16027593f32432f1d443776186f8614ef86663520d53d92d147f92beba1b24acb89f6fe3376876bfee1504f45945aab226be41f867d50bf03971e565a240fd3279dd62b4988e7b173b112cbd7bde651f477840d82511461fe71838f679dbf9079f7fbefe48b23a7e2d8a04c576e438d0370a53f2dbda309716afaa088fef044198735262238fe71974f15cb6ff5419b7d88f18583d0eedf7c9e92f443d8ce1b500e2e894e70a27fd4d9655e742814cfb811a00ec9cee0d22c94bf58f52f4fc6b6d344722bc08a2813a3e0825eed606169f5d71c6b5f9739f67c585e543fa2cd1da55666f954fa88e1001f620d98874d2817466f9799dc042a2448792afd039433eb189d8b48e75dc8d2f347f05c013f8a7a06b8406d146216f4de796244f9c34e1ffa450f110af4b45460f73b737ffeadc081c214334d15b37131fc0d914da5296d299da6e1d42ee1b3f64249b060e870a2328553929ab0e42dbe78bfbc7a242e7c67d6feeb9f775e4f1096601fd647046671921b0c0747d24185f0bbe1cfc2b08c92c89c580136600292525753062f7476b4759f07019b5ec1313dcc39ff575c4abf3a03e39a092d9677fb9c08af4ab85df2b65b3b8cbfb58538f26185f9ce556d7684b079197c92fd14a3969a1d6bc128437b7b98c285280e7e3a3edfe4d4573e75a3e66a617d27d368a0370c759f69e1e31c0af514d3767129759e5a21babc8d0faa8c63bc2a4d8a9d60bf6b2048d22fe2b3b802c414f773e02c0baeaefed3e6748ca96b173a61fda015b8ddcabdbfd56586714f2df78e45ffffa6acedf2cc634bab056c162d07fdab21664b8d70b76775ddc409736436ebba80e3253aa6465f938b253d7e8a60744879f3f9a1f0548c81da8931863ace1e891a3367bc6ce757732a495ba5182460b0e06d04e7b91afae47572fcc51b801a70bcfc9f0b1a362e86ddd2a77d87f19358ab8f61a370cacc300189b7eb499bbb42d50a9f6d98676e0358a4ef0b7141d50c5b9c68b276f5bc31a0aad4e6347079270119359184bd1dfda70796abac0936b7553e99e0bb36d704b22b2b37e03786f510a78c6e20187c9637b03ce06e72b8a32bb307dbffbaa3e5647dc34964b4fde02f6dc00ceb989d392d0a325c23020c7b8bc672d9b1664064b3508cb66540c7ff0c127c1b10b246ed5f1618a4c76811a10d5ad4dd3af3cf11650df63798eda4694957a0af2b3312e84ad4b01efb68c1876de0eb841df68d7157002d731062658d0af79be8fbe649a3b53bc72b34eb22db6e193908a36eb7c1c68074a3d839c9f387d00390eea9dae6dabec55e85d625e43159f9a5897564d42d2fd78151ea7ed6e0f720f03b66a06cf8c60093337232ad874da862842570466a03796fed1a39a85223957a0ad31da8f00bb765031d05bc541a2ad942cc8bd43912ae9c849218c6879aa5e5e8ca1e5e6c3292da557e146c0b6db3980fe3615524b9b47e4120ac4c5e44fd3847a02a06ad5908b6c91808c61057fb03e4ca87811e8bb17ec37d8878a4f16c49ad54921000fe01999d3a5cbf3bbd64af89bded4735a374f03a7f80e52fce42b1b2f20be8740a53ecbeaa825d0ef1b71cf7c7921238454beeeff2234eaf66284f48aac41ab5d69c23567a4b048edd0a270502eb105d00d9c9f6269b4d284f99de33b04cfa3d3c415094279b7e537d6307a00861951f0469602021c812f99e06e91874c0666ace1d60ece7e338d9a8f67b8e6ecf1356fa25fae31c543de50898266ca5c5197b38e48937c97c6c7458b2ea7862afc0bd91858f7573e0c363b31980253ac55859d6f2cc5b8f4f3da5c25c6873f2d4b1d926edcab36f33bc8a51d82a74f15d75af0e0c9958e9517270f061df382e87d1ee24b00cfac4b05ad4c5ce9dd0d17e4fa3b819ed30ce9ee14a0c6a1300d2a6c4ac60c0838834acfdf1145f373939c8b4a5af0bf8f9755090657e97e3b21f90442eb606af6ed30bef602928ede9a48272d3f0a200066c7d3735a261786e51cca571c91e6e212c48d88e738bda9be506d4e1ca4063b2c8e4a9f4dabc29893af1432a20938ce0bca69d6db108d74ab26d289c4d94c964b36490c6f160d0544ca7655e74149b073e53041dbcadb5380fc2fa72d308d4245df39be04f12237295f0c0b44b623f0fa1eba736b10549340d83fee8e4847e37329aa916f15c139666def456f7afaf11997e4b348a21c22ea770b061b5b442f972339e3d187f970297503066b5aee80c92a81c537c0214fe41684a24205026a9c1eee41206a0cf74916635bcf7d1b0b0e2a9d50ec37165d9026cb36edba8f8f6ccd4077893ce2c4872767727a0e03aea69fbd33e2951038e47dc4bd07d884094bdd1d4137660af0f40bb6558ab56c3f1d54bdf930e0beced33ebef109b3583594d3209a7a8a177428f8c43c04ef2fe0d43b6272dd1f223cacfed5ae67bece56efd2ceecdfd80442dd772b2843f659d3903280307b78cfbf24357cd5c49745e64e58247132f1167c6592e96c558d4e75f126b66af39d380fe41453426e04b6296af57f63139e8b8d393f9b0dbd9e2d2472209b4ee97f4e613f61fb4414d7fa16a919ecba5b53935fc5484213fab092f4d4d42eb556c30bfa4589e7053c19e6b86a6943bb2902e2353b69e9560e1b057d42258db2bc97cf3aeaac7d69cf0b4882fdb57a2406ba755107ef2d1d0cad1dee1c18186d78ec9811ac5087104a9fd4d28cbe944fc476f58f881d16491cc645fc1978d605b5c0d500f1b25c5b1affd647a3e8efebef3292577f14866e22aeed5ad2e5fd3c9d8b8653ee02b933235290e026ea9272309eb35d2efac263cdbdc83732182aa7ba2e67f03c93c182f6cc948df36ce73ae10f06a03cbf917378aac01d33bf77546e8d1128e484249a67dc9081ff8f3cd69576e2d675a9fe527830dfdfd0076dccdd1170b47e4f44e2b2ffdd1e098cd0756119a2b5a604531276453053062d3a0d90a3616b779c5b16ebeb9d561b9e7cdf0d52289a1f5c9555f2893a585acf630df4da20fe80efa54c7f28374041a6b28fdd96126961e7e10b9137f2b56040e9c9b53e0ffe7b6afdad834fd06b649dfb941b72d0d7c30d0429f737a22170c8b95d4b216271e7156c7bc9c1ac25ce819007524163e529101c58872b43f47842e492782cbd64232ed4cf59ca2ec5ce5cf43e6963da15344fa55a16e1222f82228be5cc280bbad0a1ffd2fbee98ae481d0e7c00fee347b98bea3dd533b209dc6e7590bf48b542efd816692bdb1a1bec101bae0d43d23082a88c04ed2a3cffb44a9a1ea1fc4329efaaf90b2001ad7fa4d4753d98cfa6a2147db756f41a70719818fe38d2fdfaa6b236630a24ec5130ab28ae6e01a2c897bf5eea3fd51d1ab5870a5d98b9579e4ef7ed47df7a481684fda0a4262a7a780d58a3642de3b6c6ab1a09c23514f9f8441d42a93f94ef14dd6ad341a57ac17ac69f63c510c4353c28b40e0b2d24e2bebe69c0ccab44a8815c5c999a467455f2420ba5084337bc6a071284d713ccc6923267e159dd8b20f360d676dd894d30a3205babe746ef5949e0488de331faf1baf616054c718e7e116f79e544efa7b387e1c02d1f4a491577d0bedbecdbb00ee431655294bf8b62ff0d3f493bdb217097509beb7e5e74e5c64fc53598a15dd30f2ca62dd268e3a4160cce345a9589be75cd4fe05e60a814ca27d45af104d8b58d45245f2fb0c4b044c7d35b2184d3aa843b810ed6123c2134a36735b6ace649cf3be95532112fc968d173a8f33c3e83335ecb50ac4461372c2dbb10737107be2b48ff211a2bee075930b67d149b628bb5102e1380639f3af1d9c890eece5c6afa3ba7b1be4b03b5597b8418a3a3cbdc2325e9d5cd4b72ee29a542976c5110c39f7582d03b6317cc2bf34e86ad706c8dbff12ee7113e8800d8e8908f46268ce4fde63e5ecf4608d30b227b059465f7bc23de5f107833423fc64474f734fb8399c8cfa2b311800d7424ddefb94a1024f0c3c575c8ad0839ac1e0d53726e6ffbc15e1393d319ce3b1709a82ce9f8919b107077a36ed954d3b16eb9cafc43216aed70c69d1b89edeba9af799bf79caaa2212759ae448d04e2081cdc561d81496cdaa5f7e348ba7c28369c4e2d6dd336607daa12a44cb6716cfdb03e49dd768c7f38d009103ad2349b59bc464f5e958ebf6f7f836e3b7f6fa0724cb3b2265f91dacfbe77b6a22791bbfe2516ed5a6d5ea47824317d6bf544a9c71e3f6629063f62de5acee4a20ce3a2455ac4d959cf2a355f60136c5ddc5a480bc4ff97a57c4a6f9b5743ce63e5f501d4a005397dd1696aca5d7dfe7231584168916943f236066ad3a46f1eeb16c87c577faeeb3aca526ed37ed1ff619f4507da551982cd70e61d930310385cce6dbcda4434f73ea4e6534c1803a5359c205b3fd7ca6a07d31de8b45abe7b12c03299c2b6846453512e40f9cba86213bf524f9bcc1f307c1d5ff9fde03a9c1a680bec9e9b3e710914a74f74879c5c1b491aa7d69d359047075c6ba8e9fe6795e19f7167feee3abb5a5d68569f71a5b83a46dbcbd65604c08058219d76a0a8ce78ea46477af853a2ce1324c2af20eb13c909fa06f77b7c8add909e4a5f525330e231479ffa2324a7a97edf90b60e600d802aaf63723fc7d323a4c70b98a717d5990342d36f22ff4a8caf5b6c6b7ba2e35a39dea966b6ba0942ae4dfd53ef136d2b8a989133b3468598f07a8aced87dca15495f31ff7d9aa778a73b27140e2040940d5592bbf5203a077ab91863b0b1b68914970ffe02c8a6d508c1cfc44b79f2211d33c7b8c0eff5bed4055752ec43ca647127e284c7720dbc6cd1e7011723308fc4bf1138879f3065ce3c22685ecd6e985cb366af1ed7be08750208a092e07e4e4d29eb46ee4235db0b85ccd5335e40c14d59476ac075fb695854bd7ee5044ffa46a268cfa5433b017687e58bc358abd76fb7db5cf2c27a2f7d27928f0ebb807958a27a21597fb0900a91ff1d2c4b5bab58050e38a1fba49723fb1a40d5d83e875b809a03b7ab0bd3246d967aab33ba296e566d967aa7e1784417f4a62b7d63b0dfffc66ad92e67c99ad37bdd4b8d25d546bd790aee144d806ff411d50cb4837009234d226aabd782a8326994a95b0a59642619b8e931dd199303c9b6606caeaa44e2c0d788d4b656b6dbc91864ca60631a3c6e9e0d5d457c051faa10179e35fdd044edbb7ba78059dd76389a20af61e40ad7b7f5a81fccb99f28915bd23f271cedc91687ef56e3785fe79394be4cd3ee34855892e65481190956fa98fb9eab1845768ff027c78aca93fd0ea3ec74fe2e19d9b49beb90d71c93a1ee667713948169537df8bbe1af4ed0d7db235f61f676639f3820f16307b104423188cd6dca31123a59795f16e397b75de6aaf7bf4b1c1525323d2944a8793390147562e31f84380997bc9d560d718c256f60e25830001555c9abade67a1a33f0d9064c9e99de4708042d3acddea60ac1d35a4b2f3ad573793ff44f1d1696796d3dbd0d68ce00e769dc7610bb46691fedffd4cc72a15e2a6ac08018f86541c22a9c1020b00f394f1915975b352f0018ac0e1c3998f34e1bb86a9a004f08efe0d21e453cc161e38159ccf494a6d9f5acb6aca12864b7662cd80300c8a8b02ee6127511176c084658609feb673ce9d5d748ba6d299dd68d39c162504e456ad5b6ac05c4065d61b67d62b3bc2304024327cbd29966067cc04824bd8e3f1cceb6a39fa72788476e9735fcc5719759981a80412a49051f6e6e5168d5ebf9bc554b177faebd1b29242bda66dbecc1abed5de5cba1772dc0c2e71f137fc7ac6b218d33cb6f7ae1608cf49a4c480131aa9ba49f966ebf1070e6a93606d0ac3ae4b114e09adfdd7001db335de14524091e6b58acde135bfe5a8f22331c8442ca8baad12b348547d9719c197e88bbf6d33ac266bd01874afc9ac7e6244504f09516a5d4ab55bb71edf610342d923206d7ce323cf3acace9279964c27365fb13fb27ec6d83ec09d7824b25d04f7cc81210164ad44b1e6b585081bcc81ead1dcfc6e266bbbf94f910b296cd1f0b0950618b40f2d0531bb3d6bd01a3aa63401bfa7e121d531c662f08f98097ab8f9e5b0287d1b1bd3b7aab1c3e3513ba0a61951d30aab997da860b9fdc513c1b7121f66f78f9febc7cce18c875bac06a55b41c7279edb1a7e89c317ed5dfecf53f1b288cb253154fae9bf82d9626c6e73fc2f8e05866da4bc81a9be4b3dda06cdd3c463dda07bbbd54ce15c05eddcfd14e2d7b002a7d44c0c620d586c286489ede850f596072ded3a6dbbe64b25428a02a2afb180576824096e2bc78e6ee4ab6653e3b30d267c0b1a1cda6c618cbceb52b642769c97db6ad878d63125e797319ceb18fbdddc85947ec7fdc54341c954735032b88d1bfdd6526ef5a89234b22e274fadeefa18c0e92072cfd6af0144e79309d1d7cbfb1b946dcadc46658b59b0452aba439b8e74c56b2126aea4bf8417d5a7717cfaaafc5e99d5352f739352eeb30cc824b4191f2d8a6be2f86a7c855cc67905465adf19e03fe87f97b3e715fe26e6a0528c3244e03faa001479ae9bb3408c7ee83eaccb9b843aff6564d1d1b1e17addd5e01e843d055b893a649d65ef518de4ca678418363815b9d836beca2e89da28cc9f609a5d196aa6049635b8aa1175851267e54bbfeca666f35a976e403f9b6415ba11989a17d79400ba67468ee67011a2653ceaab99ae173533f38e9237e9bd47e614fb12c7672d7ebfc4f8d22441ff31710d7aa8ecc5ae13eb0b02077d63f819162d622f95088a8097f3837210a55950bdfc118274734947c356d14af3e03aa9e99272e3d55cbaf09b0080dfe67840ca5a591dabad034dca6d66a049b38488703e7656afc6920111e7e2123253378346b0bd861909efb22c4149a462a3b9a31fd5888ea0870034df71b8788da0157651d4b487fc57c4379dacae12afa3ffa4e28291689fb370d9070bbaf436d44dbf0b1b5c002b2bea435997118dad788f99465fd96c18e15ace5190c888cf513afbef2b04d2ae3810dfebf060f4e26e902fb4d5e922cdce925d1c0c397b1683dd255c847072b559762cbb484fd0cbf17227b4e7d189cce69ffe67be4363f17697b47241d60479771c5ec774c725f4a9d32a4ebcde57a66ddd09436f3f605215847a79b95a56c0214dba0613e20872be97e475e3c653828677a170f9d5ad6db605c7d8d967de02672baa015272a0d68fad9770a48eebb2c99eff9a2baddc417281f40990d513ca8093321577d36dd50cd58ca0b3507de2b87d8ec5730a613c2475360f4255adb3b2e859c6e67600cdbd860df2fa6fc309b69f9f516a48622023dcb461a31c6eaffcc3fb66da0953b01ef9b33912a0c55028c200808f8eae8b8576fd87af5489d9194fed463363434208238b303f458401591d4dc0e678160da16ce95140ab433c159b0298efe9281920a400ae0bcd53919067b51bd55523e1856f8a41eba00bf133bb7a1901c4bdb9c3eb514dd30e72f106d6e792f18e2063c4183e1fa6e9fca6ea707647c86808825f7cb007ab61a2b839b72f6e5c4c9938470fa7027ea89582ad6f0e52c088377afd25f3a138ccd5cbe5b86a77476c76c523f6cad984091c46a5f9f4daa1c5243a1fcf41d27b6d342de8e09cdb30145b6c561279aea2cf85f14ac38b28ba0a4a4673f6032a9d5a7a15d5a5cbbdf823598664bf6ff88bc23513a05008e4bf2b7d7649d143a8cc9fbd48cfb87a8e4d34054e10b0bdcfe1577ab7c6dd7cea62c7ef3b1bb77a749fb9872573b16cb92d33760e949e7c34fd4a8d410b341210a00677188b0acd321a0cad809f5998bbd5ced78ae4330a65f8cd188721f1e8c4006a12c03d1dac8783009f3a2b14d75740ab0f4b35d816bff25873d03a0348182f723b14910d96692c361ca684cc39d66b9641f530f84e5d7fed38a40033373b8bf123305c1e79e374cec58238e48549f4642dc8661d5e3e1aed1e9032b0583aa14632123219a0a3f5a519dc0b0a59053fd016a615ab588391a8e79bfa0ec4dafc592e97974718ec5b6da259ca01b49a05796b0ed15adeb9a7b032dd4e8b59523c56b24abc1ccc192d643d0bec319db9e3b2df9493f5b6a961faa6456faa0647877f7db8417331f4aee0080a9e9a0d197072d304b3192563806b424b7af9456af389bdc665dba6ae63f363be1c95e4958db6e2ef9375c9c0fe0826eb613b25308a62523b9912f0fd9912db9dddeaaabe67e4b9f5a7194ac2860bd9338d02898fbf2ac83a4bdcfe894ef71a0e97dbd0a67367060d56b8c35332d2b05054bde3504e12b13446dde6c9dff240aa7501655f89a02443365c1d65d1f6e34e38341ceb26eea32c4b95242ababc4ce6893f22822c8b8b5f50c673ee970ce8a638b057b5c950360bc06ba9f51a2dad260574fa1ac429048163fe9e82f9dc7be1c27814320b519e0710994e8558fd05311ebf06be2c462ac136b29462758f2f27244fe6e95589c6f6aa46523f3ab8a86ee6d45f964e44242530e7707724d8c8619702886d284ff42fdbdedf98c7d0265655058da658a8175d839e078728f9a8125f50fe2ccc33038c26b5db3b0442382abac4e26845c85181a44002e02374a0ac04ce302315a5e490787da6626b3b3dcf186116f5901402b2a130d97084a671f28b09e480b273129e5fcec0197ddf59fd4fca9dc1f635c6885199c299369514125230b3b2121d9fb4b974da42ee76f90a56df9dea73342ba8a71c1b380d4c7fcbd2f225127b225fd694a16d9ba89a7c5b88a4bf4ffaa867b2214d2883c7f79304fb32a5630b4e9492b43afcfa7dfbe2fd1a3fca8f75f259c1c4bb1b60e210711acbc87c9026e696a60d16b06c60ca7da938f46833888f4a35809608d32d2434ddf66c42beefb33b97f6a790ffd1d3e9cfcfb61d207162634b023ccba603b72b61f9f23fdff9015d63a3ac6fb973d0e124c9e27575bfcc434d257a6585d4f7f45b3418dece5957d406fcd21e3d504db2d6bfaeb9393150f104c4cf9b1396e16977b655d7691df3c6e74464fa81ab35b93bb7e0f5d5cb3376d8e39130e86424ebf29a71b28b85dfb47080ef656c07399dc6543bf0acfb8e3b9065cb38518dc0c4491bd8189c6d8bb11d9b095e5835b32bed190409a066041c4796aa4420a7710727ae7e0cb908dde073482569356af40c5cd20096a67ea6c327369edaf7c05fe8f4e5e8502c9c22694f14215b2ac376ad9e7158a27e4c45ec9ec292cef96d60091f59ad651c4ff3a804dd2629f3243b5eb86cfac41947f126c1871550abb117e327516b5cb2881ab81eec4ed6d4231a2f1a9276952cc19da54993e10d81812a486ab67b97d234cdbb3f8f3242844149c517263b84fb070c67de448dd816b802d0ec9ffa7b9862aba86234d7ff8723e95abe8c2a1ced7fb3598354d1bb90b167b869824cdfb3b8af339b8c0e591afe0cd0f3dd61802ddabe552e7396ca518f544333e2bf72bc535cf9a57614b4a60c52490d82844f9021bfbf5a713e9b849d795b254c6cc5032b168fe12f5952edd9a6e68d091535dce4b5a191c04fa868e68240bc946a94c8433950ea39434203def3727767e03d72e5a35a08e394d20de8adccd5c9bd9bd27c9f5c42f99104d56d7d2c2bda2f3bf03a86fd056171bf01369d26bb4051424d41bb4eaa60525e9d656d3e28caf570a8b592a563f3903ede05d13f7d61bbdbdb7a0b78e00f975d6e20c21a17c99859913f13eb4ce375c6192cdc88d690d49c5ab8d09e1265b382ea0abc7d32e25076b6d539a926322afefa5930e0ed79746218b7a5857127c36bf489d393d1a6272d137d69fdbf66a8cf58c1c8aa0522ef7b71abeb63b88094583ca35f3cdcfb5e52621ce8ad4e04398dfa0c0828c08a79f29ddc47166ede222d58c6865c45bc41084fd3c19af387323a0f22e65c4ed17e19784fad384a23daf06a32ed0eb417bc57c5c05ab2c540778182ba3bcf03a44bd0d61b51ea2d832bcf8ef2a9f686ddf286b485a43ea010b05325e823f093b1290e943473bb10a221bf2adc383a7233867d928974bd69e4b3fffd73bcd74ccd8db169365c5d97db47974b83ddfd517d61eddeef4ff9dbe51f775ea0b0c97f00ab1e838336253c913829a709c47122326007eda9da715f62de6fb8c6dac32e59fcfdfe0c0654d56b5e9fe9205440d85ba176b9b466227f44c42691624da65ddad6002d4e0f4ba04b38c78e14824c49ef3d32018edd3878914f3ea710b48fc342be610512e147d8bbad2e48db888dd498bb7367185ae87b7716ed41669feeff103cf2646f160ad85b0337a0be7b54e4fa92eace0a3a9d37b6bfba0e986ccc0d710736daf8c3159e42afd4c72de184492da1e754e3f4c4655254c8c0ac9b68f63f7f45da4f24e060b570c8498734d6eee4e9796f7a7d2054508a82610d28990c73842485c8155ed50f3d938a11ea52f7c76e4aaad7935efcd04f1466448c724196a3fe171acd1384d10c37a15be21ae4758265b57c7d158c442022f024f03fb0f0a6d930f54ade13d855be45f94d2ea1fe2bfd2746430cca96c330520298cf148eb414339f6dc3705d154caf5e1d3174f12bef45905bb7d6a382bc3774c44dc66f1a60e0f16053d888072caee7e3b13e896a94a5de3ffb38c5dda502e70be5a9db588c757864b77cd485c802f338810ac69f70b06372e47d9680764ea6ab487e4e28419151bf7455e03486567794b2635d7bde07333ca92ea26f82c72ae90ed3fc63e3925dadaa90ef22da6f7eaf67417192ea4f010ab224e22366f1b24ba7fbc8b674bf74232d1623d6031027f77872f20c11a23785b47214d0575a5874d30621da76cba487b5005280225da11a9ca8c31453b0aff77e6f429d5baa75a6f476213916a977a202b496ad0af43edaeefaef4f7443e666915f21a19821ba4b3e60d8881837842f2903400408acdbe9143fc2d1f544b8691c581054c98a72c6bd2cdcc0d6656b920439ec74499577a076b02e2d5563d27f10388311f65d3e3f619d72953f9039388a52137c50e0838c899c5c25354fe535475a0459fd2b0be10d0e86bf65ae49c75b917c96ed2d13dafcdffc90f362c406c83ed4363532e7f36eb178eb223272e1a3551a9b1f8e1bf04cac29ec93752fbde676130ed051ade2706b5c71b46732956e55ee17859f2d31bbca287702912380d7dc604fc7a2385a4258df7644d1d8bffa4d1a297664d87f8e2684dd984d4d42d539d41aea6bb35caa237e4e5b0ebffd36dec8a4797cc23619b4adecfb92dfcc2e8aa56feda182d9250462254d995205e7c6e13d015885c700c7de09e1aeaf35e6a061eaea690222b33a7782d4c78b3feefd80baa0a0c7bb05de634691e439b8bff6f3db9dcbc99ea693a16bca08480570c024c177b8105213437ce273e7d7135adf742089fa729b2fe548dbeb4fa423faa6737531a7c6a11093dd75cf0ab023338a71d5a7fcbaa02e71279c0eec5c31f8d4a451e5611b8defd58964aa01d339ce290606ad68077ec09c1ffca4288457005793b8bcee0f48f296be92d7b7ea075d47759b551c3ecdda575a6a6da170b2b51c10bd56ffa777275a46f46714bdd1f5aa526c6e873bb04245188a21443247fbe3465f1245f0472e2b0e066cd2699f60247514b7649339db6afccf5bc26b9b02fd69ed3901f9acf368ad535740a6cf8859067833d94ef912097126ae27eec1f72ae1f43ef1351a6ae83721f4fdb27afff2821d46d00cf6d344c2312899e76a7b06bbadd2ff9bfa86190372729cf460fe59750acbfa68b97ed25d3e8c828198475e69da5bfb00c110aa606dec8015f33a80909f0148474f1f448068f21a0fce1d21fad885e6e4fcb022aeb7d66bc4568bcfc15c7501c5d75b8911d87857658aeaf7f37df9d6904cd34ca56676a679f20abe26ea6b1ccc9f3d333431acc091ea123752e9af27cd32ab14ee74845265005d6572f23d2bb098a4eb8198e3937d372003b80ce8936e20fc6d4ecb9dc3541a6a62c6e16a6ca9aec14cb4515f22e1ad7b4f3768b9715dc95dc8a0496b16f1d8f26a81eef1b21c6897073d097435d179e32efe1787503f8e5997dcfd078e9549f568a1860626bb26290449ca1ad720647183fcecd26ce0c81ece20d33781b55fac119206c787594dbe6942abadc5686255ed4c389d96c7b26f4e9d4ff73d005ee729042d560050dd23d196a6da3b77bbf7e5dc21a50be7f97356ce38eef528e5c65d94e6481e5fe7847f18bef361027caf3cbfb74f5c3f80a3b8f62ed700c94aebecf27e15405f1b8e0faa724947e11058c2609742b5c8f1649e2f57702980f3c1f5c4affce6d65455bd31c3e45225b9bc43765f129c5a751fe8a8b3e32c3235f3f122f490a9af48c7e78255aa2c39b6e17dbab40461baed5def9d15e774bb7ec60c31770f4632c3c409923ca2e8fac70372216441fa08c755eaa12f6a9b0f17434292c6348ae9b3a662f4d65115fc6f6f9d8fb6d76df4324d54498894b24e8bb1de67346f2f2dd6fe9bb653b537ddedc5fe275c28860aed96cee2bbf41437c77513924561c1665d52c13461ca1e3515e964fb6442133d7b2f9b3332a60ba2706c29c5567d26b4450ecbb41e27a71bafee6ce41279c6e1acb093a8e51472587b34a1a1d81976b62705186510d04583a4b683f43bdb8c40cd9b1f719081df89848d4bb2f020900b050626ecbf3e23a7446ebf4354946cb894458d3ab17dec59b53d42f1c59722fd82860ac8c908597251e53194ae566c96aceb00b6a8481db56631c9240b0804f7a84f359b8eaff1a2f475f82021f7ad86300751bb0294d098126ce416f797a684c12a27f716ed15345ef4846ded16f18b19f945a694e10ab186e044e1072c7ce47bbfb35017e0a5eda9890d090923ad7ed67a309caebc8839e47b8bd301660b2ec8f3ce59a512b2af7bad6861591d4005a0eacf352ba6dca6d7a49ba488d6efd54463cb158e02abf2ed292247129f2b1ec02d56ba8a1ff576a7150195085d1a4ae52ae5838a949cd1c0ea7b8332b89076dfaa73c4b5b8aa9b2a1477112ffe64d378741ac990ee412bfbae3a5c1882fb55895e7358be484e8f974c44c10e0c26dd5502f0c721c422d5a56c48500be037831c89e1742f0f3dcfc7f35bf64f7b192d79e9f1ee329c189da0b176c7c8b59d1550e557b852523d92987e7c12ac3646744b74c2c2190f27f1878b639ac81d1b714e55b765d6fef7074041bbe0b9ee88d4a3fc1a6a80004e9933edc7a61c99efe221dbb08fea0e94c68163b471b5cd2027a5c031b533a38d8a43922c2fed4fab9d290e750ec34ee60f67687df1d958d7c796029cba70bcf991cc3ffd659524a79725d96cf6cbcc9b9bd1ca96e3acfb6c12273fefca47e41bce5e50f495dcadafa0e78cf9aaf61ffda0b8eafd143ee4fae1da35c88d0031ae21353e42c964d6a1482841e761cccea9b6e6b04c449a22f6e350dbbac29baa0d92a1ff602de1fcdcc2cdf06db9241924ed4941e05086ecbc2b86f25ff81bd96dd770bbad0d6086153ccaae207753d9f83e86bfe2f681f9395cc3c0f843be8b4fc86b6a1984ec46a63e5c0fa59076fc1388cd4b50f0d5b4de58cf6a1a4514eafd26958741bcfb862df8a6bd6589c73ab8eea76c48c3a9b66ee35b60286a0776ef2ef8559cbc414bcde8e2904fec344c444d4afc0c50c5506299e167dc70f0f99584451a5261382b82f5252d260df95a0f03e723447334aea6acaa95281c65343cea0e4edaf2f58d9a4962b4dde0b400fa77f4c34f20f6d3b390bdf026e4ee8a4f2614a055ef3b34a9060e9d0792eb99d88e43e7a4302c12b8cec963a5f1b2b3e6e02f3b6a0668b4a95cbe914acddab814f59e8acfc52f0271f2805abc4537c7b86c748b336fefcbfab761002315a750187f616c71a52e1840eb517a199a7f7a16d857685447e26a9f91636c70e8e83df0b0020330010e431149ec3f5ee12b426e476a418f3876b5a94a6719d8bdc7d5cd973e2147954e05e0e9c803db018a580d4d6d3eb21c8dd1a379240fd067d9d90ef01d2f9af4211f0219ca8625e523a6eb3807f5523dcfc56a7d2819b59dbdc268299d006f853716b81aa21f67a92b5288ebabdd281c127c6abb36ecb3fdc1516a555f572d34449c3406c5d6f2115b535fc1e4926a91e2eaa170b42e82e4cf86681449212ac734e1f6f330a61c7f75a81e8d54ce484999c489a5d6d90f134baaeb73975a8fd61b8942a1278e3e1f5eff51ff3ec38c732f9e8cd31e7f495901913f0ce941e6e54f65478be72057698617be20059b6979f80ddee33b4d5959e9d6c5dcf3f8088647e87702989e394ee2b9dda09ec5ade632274fc62b2bda6b1dbec2377046c981c6bc969a32e93d649c388b88f04ba867f313bf226395a108c17b774bc120c92162c0a01d08f4709bcd89407b8a9834181b913a38217836d3247bf4d04affea03bbd4f5c4807f19e69027eabf8045dbcfb6ab0dfe280216b55a844a165f706e52a41abd127aed10a938a843435795a21455894b6291bcbf180b0c29f74520b6619f67248a967bce050240e8df4fdfc453e96aaaee8fd2c04f101165c1710039e093d2dd71720e03cf4214ce966324b1347d8f0c705762601780a0060559b41f71edae203653c5385e9a3d415f7cf483ebb19aa98ec369e46f9d3300dc4719c7c3264da155d9f5061063d004f1878e6a046848019a95564bff159f4e4cf7096006ac44caafb5bad918742c4312f66a13e50be1da765422239628e8f8301f0ce43241aebfe4661443285eab65d543761bc356aaaebf537c5359e281971005180e6031273f139d3319ffa93bae9d5094147e75ad9f6b4bbbd50d0e201c60f04c52423edf722fe28c8a190a38c4aff9a77dc215d58d51369aa59834fab9e4cf437b535df1746f5fd4dd049a3e34bf9b320f87da314d5cb38f007b03320111596301742d820eddde6145e25ccc70b47f40b9f46e557bc8ba473cfaf4a6093cf13c26b5442a2540afe65ecd5e27909c5f2b17aa1460742585fb6448489bcc6502c716539f309009f70b53c623f87269aab6080b2b5d260897c0ae86f15217b6655a92f5e425f3e718c969767b30af378692ef32ce3f62d4f05d14f866fecb679c88ebe2bd45a9cb8c3b385bbc6636ca222e41b3814135f80ea13652277f346343b72aa0ff731564375b84345fe5207136e5d8626fa6fa18a76644971f8c35437430010236ebdac796e8fb1c15fc75b7e9bd8fbd7bbd00595b9e822d04a276219276af128c4c7e52954f1236f90e9df2358f5eb27754b854aa0d8cadcda301558c767d32fa9667a2fbba41aea9e680f5576619360e91efe7b923073429bbad8f55ac89806502e32249f22a587767f78d601762d83ca12b3c10f1aa4b942762e878d57dc5232a02ca65041d2103a4f6444a53e6a704c17eb01e22317e1415d4b42f2978f17979e09b327c97b0b84a5c7b32b09029814801b2c17e2417d3b6905988fa8d543cfb5f47e95803946ad4427f65652dc4fd2875999a48ed2c87d3c97ac3aa269116371ca7b4b7eec44db19356dbb438a7a3b434511d36de6f81f1e8c48629b6a11806498eca6af23c6ea675b0e8c521ebcd64e1ed75648dff935643b7a4d6be1048d5c7d9e5320296e9263ef4921460db30feb5e61c010fefc91f3ca73748e450dcddd20c00baa15f7daf477fd801dd3e597bd4d8cb3f37edd24f7cfe850ee964c3cad2529ac3a9ae8432768c130eaa81c3cf7d0449c6e99f8768dbd6d5eca62459eb49e3f3b2a826d84666cb6d581ba17cb92f6f82854d0614ac911f53d52ab12cf778a0ce7025d48ee0579d88e312324d9c54c35a97d2f78900bca2e8c424830df6461f567654ff2230e02c51e988926f556ce42a62f7f0893bf6b855555ea5ba5886a54a469ab3714d90fae36e3e3b2b43a541bf0b869d91e5b9e1c56b7187254256740d2c2dff863f67054f9e1113bf7ac404c417bf83d44ee6c92a2e1564e77966dda814aa05533ce8044bdfa7deebb63f39bef163ef6a05a4e62d1e22467c4333507e9c8e9f2fb1b3489e1770b013ce7bd8598ef1c04f49909ffa73dc60a3d0579dd8fc3756d07f2edc24ae77e998f053de7236c7a60e6e798bf636e8322c3c02d6abcac51bfab52ece1450d3c28d73face89caac6f1f58948fac0ba3441df8707d71b5c33582e193eb41a74662cf1ad785924f8183cbcbeaf7c19925f304e9abb82b1051ec1ff0df1f20328117a455de20b72c75501adf98b6f4d62284f2ef698318a2988b4551904b6bf406114eeb02ccf0505a9decd93f789627d2f1aa2a3416536ec34cc8cbf7344823acf1dbac9ffdcec9cc562e55b6b865a630322e2df9e093ad7a7db384d263023db6fba8e0e82f8666cae22aebfb6d9bc590c32697b0931261f470c28130720f70d440458d97c83b6fa068a21d3c2cfd4582018d01afb416f3751b964b3cb8b394437bddf3319640c4c5a21b4d015e1cf52a812d7b69f4a51569dcffd3d6a8b6a260a8d8f6ac15c2fafb884b4d5433ac2d8e86d918bc7ee6251b84777c0703331bd035b9f8be9ab07c061c83151a5ff4a67891c92ea0ffba8e83526e2f3dd522dc773decf3d3fe52b15987edac1737001f9d756eb7c4bd506fa58e6ba4b354e921848e5e863b1c684a981d0edacd6ac1d9486144cf5f850a7e87e6488b52894d79c5899919da51d3df9ecc395e6318fc4fc003731ad0be388109c9a85519eead35aa4a4ab7ea6a2bc86c9fdd97b42ce162d422ac1a993701dc3d3e0633a032353c0adfe8996d5b705c3c6e4bf391e334d94677175507878dbee5fafd6c0111d25d5bb00bccdedf9808ebf8d3c1f0e9fe500a21b6050078edf89dde92a26de7e0f5bb99fce15fd3413eeb1849720511abfd015383728d7f62feefed725058cb3aefaf1da64f01bd89cacba2c7edc00f41a0fd2023170ae9ad3b1db0eb96161499e5b8a57e1dd203a47c5c51fb587fd1c7e42d944bf5302359fcac7c003ee1a47efdb5b2826d223c4ce30f54a7d5f1a217972f359b8da3d3f9e72b9a8395d83048016fc168a707b042ffef0de2f4610798225b216e8e8322404dd547c2f826e54eb03b16d47ed522b95b91162eb029023d953781c74c7bf8ac7a835f5d6afe7a25f232ea24352a9e166d94e23c79ead418497e365aaf271abfb94408a23ce028563f17ca631c108506d96286455b074ee8771982448f9f939821c39ba37bc8da9e69e4c6dcb9f64071ee735f7fdd96c5832aaf333a221fc967e497c7f6d01f021042247bbe8e992e8238b64e130b5b2b7d620a0500e3a6eb0ba6f13e648f7239c010a1057fd7f58b1ec5a39e0eda42c36c34a22718ba7f401e9dfc42ff5748cfa324437c7b85182f3990d7b80dd8d90656ef09a0b33a90347e12e39aad13fcb2e707ff7f23a6042d92f2c023a6f5eb0916a649516934cc7c63a53b88a9464590bd530cab94b7229d3097286a190491e5700cf1f56dc19f5edd8a14c6a2408731183c8edc2b01ed15fee3d74c3c94b1a9d46a59144dfa14dea797bc5b221c0ec54fc3998235fb6d15893b52e54cc00f8b7d74fdc042758f4eaff13bfc7be994387d4ec9ddb01d5b36e861f1312f1cb9faa272ae51a2e92ea38b7261dc806bb8be77a308d1371f4474a3292a5bfd9d35727004d2c468f8d3668f90244f67a58c0ae1841e5fa9388fb994caef568f9395e23c161b02e909754e38d4977d494172d6c6ba62e6818910028f821a9b9250d1f88843d7950ed232b2152a50bcf6a3877492261cc3d483c6fc5d4bd10f7bef669dd608c9d6f7982a3bb8dab1958d47dea03b82ee232f3762c7c99c958abc6b9c3b74590875bad6ee92215cd9b1db56072fa786997f9037f600ebf4fa6d9e9d5bde6ea112f13fc1717afdf4931a1152188c339141f0297776e6fed9f56825d0a8f3288ae656de76c46efa8e9eec02d2650a0b460ba4c403120a1a206d155d2354b95474fc50797a4682d3d4a9c65ceccc6a2446bbc52829c37478c38edbb7471076f824b195c4aa5ca96daa7800c3a41745b4ca7ec0d548c75504e272a750a531ee4f4062271b0c55689c5f92b4532b54e8c03b353636d0836feaf087584d067ef02c02a024f8740823b4e9ce8bf68698c93947291ef37943f41b7ada18107eaae4f95686b41c7c3d00f94d1132c54dc6cfb07ebecea2b6536856b1811d663155d254f3c84626ba217804481693b45370d46506e52352fc0b9537ddbda52524e748ca845992220fc18317d61d15d911747b6c164769c9211edc406823ddaa253314fbeee8940d622bbfa9f384a13aeaf8fc5e7f2b4a1165b43b6af43e9ca9734129cf823132227fe2e3d19f3c8ad88f52def3b8bbd8682c40cc8eba29479eb1c0ef17698ec2cc74698d1fd85dadcb839782d3c83dfbe1e0d8e61001e754cdeab1e9b05ed613454c8983ad939a7cf12f2100e759c51adf731c44d4f672a7d988d1b589f6784544b2eb5def42d16a9bb7be367367b747d4615645c294c5f3838c754539ff8d50584b37fa468eb3edf064b2e14549ade094b2cb74d91265b76a4719bb77229512db7521767d98e299180c1297b0e6f338c0bead49216d4368c20aaac6e4f8df3437b0f73bbd05db0e0f9a81273174f14e715d479ec2c4a5e91f3de96ecf5aa2e2472ffe48f6c5d1ef425ac6d5fedadfd0cc4ed8d008641b6f62b7316f8dda3f03842e36765e1d625af3ff192c5c7627bd5baa4d408b6fc267721eef4a4e56f087065fa6b117b8357928d26c61171ec6ad504f3bca862f0c0bfd7ead3eef7836a31bcc2ae8e873874225774eb512dd629b7d0529f1917e3d2c17f5c1a90c1d8ce0bcde552b03d57f2365cc216e85f014afac34b0c141a68d451d35c8afb0d3d531e1caaecd7b9e51ff539a32c8011286b4b9cc487647ed7cd41f9d93d84bdd2c449c88e8634293ab37e028dc72bc4f8152123fa0850694247fa64ddc68ac84fad124e0c6bf3ec8cf83747f4f0be49552bec0d50cbf6ed0ae4b94333b2c98e4d6059ea7c21bedf3fbecf06542a9105ded7d84ae4b1d23bfcb68c22d4d83d9c71d7128970c5d691fefc0dac55eaae2a79e0ce4c6b5e6fa343074c35accf566196810e6b56c4b118f8682cdbf1c056ed0443f9e1bb7e30b018d387f14294c96be4fa5898622178956a1ccde34c0b6d7a3dac9d4df299eb28fbec8494b52600a1975d6213360275b3bfaef00c7865c05bf85ad7777bef6ede7fb61d5e9bfd2fd72fcd3e081a5933e6b3f037a96ab6f854782ffce6b591d5ef9e175a9203f4e1f48bfc6851eb2df064ff06f00da89d0bb7fd39701aedacef89ef63b702890c1296dd455b31d23703d5c38940235e7f31731f2bd086f42717d2654ca0f9a11eb75f2adb5c322f933793cb643a7df1221c9fee00458d037bf98c3f9d70c8f5fe9b35c3b855c764e267eef160f1685549475015e29022233b33c5550886ed84f9d1048bef5b517bef2d0f65e8993d7dbf10d29829cdf3879381a630c3bfa8746c95cc9ba959bbafd6802ce82aa06625c8699f959728375e1dc33cc02274741942c40cb203592fa99e2010931a3fd4a61ff2209adc0e6650c11e0cee1a94590094fb9e70a2568939a35d78cebb88955090b69bdd3528ac1ba97c5ebc1e33eb5ff034d0e1414268be0780c92342bb82f1bd1c2b5ea41d13566484d47691ba4ff0d4072b1875494f264d02e208b3f64b454ae498e369aa6db38e7199169b78e33d8c2889b5802aa12fd8bd621ca6c0d95c2e0065fb61a787f0ddf33155c3637fcc9a5462c2373f107b116c192b61f704855dcbcbd87e270223f9249774468fb380760650cd342b5393cc69ef64d073462d1e44ab156bc94dcc8f2bcefac4a30b3795a22a06b0735485d3f53035eb0f89a97911dfc7e06c3a7d56cd66b529ff34a827de35a86f736f78442b6c47016b725fd6d4e6fd76b6b8319a54b4f8dbc147cd3adc3d18886a00dbeb82fb6aac18ffe695bc22773fc0f194fa782cef3151fa7dc9dc6e238f116f5b71e914fa6942eb6f68bc3fcaf0170b6bbfda20447c4c3ba8371c80eb901569f2387320c8d9e4c9f6d60ef7926e432adddcea9dab7cf03358694326e00663fae711d46e809ec37fae188c6461742b125faf6383db493d07df7af4a8487a5af11105d3ad1b83b5846e93f6758575d719c55dd5a8a6c27a1df406ddfa36be5817a660b7155f8b045e2da3012a355f60dfda0dc8b4d3776dc395b8b9001a9948fb06d9b989881f6ec3ecfec285870eff9667dd67975a85a6f418efd63358d4329ac0d4d1c68f28bd294f6c43600ae4396322e4a9eb818fd29cd1fc089af1ed33bcbade6b4df231e430129c0b0ec04f3c4dd0f2fd1b8213d34ecb386f5ed05548ae0c7878481e9841550557f212c9ef27fd5ba4efb6931c52aea7855f1062bf50881a37509606b092fa498b23c66f7a0cd3aec2705a44119540dac96c20154d5dbdd80700686eeb5cf3be3a086fa8b31e223de1fdb0047f26241c433a4d0db158d3bd628500533fc56d17831519927b99cf617062fbd0c28bc1dbe7beb23d1b4f21e1f049dcefff1e09d5919794dcc4015f8c8d562134f512ffccfe58b80c2f453395c544a0a5889e6306f370cd88eb7b2c360ce446c615528416db2d53c2810a9eea868e10fd5a3636f5a3ebac5c5f140c08573c2620f70122459c33e72381576bc78badf50a5a6352bbf8a62a3d7e49837181da7df78f137515b6d50f213e5966cd1419ff590b2f7b2370d7b633e1f1f73d322ad14ad4771cfd4f949abac7082ecf649d9a945765d8af0c69663daeca90318fca1bbddad962d03b1d25fc3a5b13a51b75922ddf07cb67293c6e7d1c0828c9d22452d621bcc0076ae1124f891759ee4a8b3211935fc2f5ce3b468c220aaefbd1596106286460d7536a36bf7a048a6438a5a3972f857ec6171bb6226338e8ed656426570d73b0acc397ba726b2136b89e713a64964c583c6b641658734c196208fbdc123be37090e07296e73b6a7b5b222775c039a2cc438bfeb6a9fc08936a6036b55e3e2781499b97f33a6d1f6620ab71d87835df73ae078521f7096bdf554d24a0b5f687f7ab67d12b20fd20c57792cd4bb6e3117dc65df3404c36fbd924788d571eb1b881f4d0ff7d401c1c629ece085b9961ce51cb78d7e8af4aacd72ad97400e8d65e5fcd8455f6bc7e0e494a00660e4ef7e25e67e824b9a24b5c760028d4bd2d42146bef06d0e14f6c3dcdc8fcd6c0ddb5ccd80f2c72f649ee8fac27d2764004cb5b5bd37a2785c76d409e3757f46a77d1d1fc7e441b13de5d02fabb7df3a90e1611b1bbc4632636e5031df64e629b3f2d9bd4ce04f62d9275c4bf3f84fed8b8c529e6ec2f6eea4f914987d399f638f5c2c3b0b4cd09596d27b6e042fe03b8fe55a83738443d61fe356fd11d8db15de9b34e4d694b932dc085e6377c5a9abc132f6c7ae8baa02628af5c9843a9da7c041aeaaabc5b9ff48c9abd7860ab96a11029c023bc18e5afb1c93e97ad1c49870b69913edfa38296ffab64bcc52d65794d5229f1819f47271e8a2f06dfac9861493fc85dfe6a76929679966f87d6b3cab221dd9650ffc1994efe4ca43677aea6a1771695c960bbe3099c6c4e831727b825de5288aef741bb7e332cd6fe850a1a89671d7fb8a97eb4461b453e19ad7f2e2c7e39a9f0fb90f7e9cd4c8d107e633e5182b80e59bc65a675ac20608c2bc37887fbc1be82895ce22a1b41b91f29ebabac6c4c5d2fba08114da38fc8aa2d373104be4be436b64c7c7e9e121f4db26a994979428867e1f0e477900a407667da62797b5a90bd3c8469860e6a8c389de54fe8231f91a8e1e4bfac2c07ae548c351ad647782fdc3b8f00f57023f79bb0babe828ecc74b3dc5e7047a751c540d1456a1ded06de5ad82e82318f4ccf85423f43fb98e53af0b8b57423ebd2a8fb4ed0077353fba64a54353edaa8628a7dbc4d604de2cc3a72d64cc6870f75b0e2d2dc5525102c09d764fb6db98b08d40ee2427f6fad434fd115ec9593ccd32be91c9d77a5d934029f99df85133249ce53664a2d64244d62d4224d5e10268bd97b04d059830a1f3d44b4552f9f2027a5fb9cedd60fda329f8332bca3323f9a318520c4a160646a67dc03933025365cbe074c29b81aafc9f49e858e56d43a8553ddb9579d5e75092c0cd8bbcadb6d3bb0f7fde72d9fe18a99bc03388341879f6a8e1080064651f9d29649456440d136bae3ab10d36bda81553abb6acf659b0caec567f9b75bea4983db98af7933b27228e27cdf63dacbd495e15914bd3c6b437bda2a03a6d22af17600548de0395567f020868332501c9125949d470eb0667fb8671d58ad15c000ad1474e5f28cac1763ae5a8b834ca9b021418b9d8c3faaeb2a3e4df23ce1ee9457ad73cea5c4bb5fc62fc4854c4b904f3c9de8718478d1dfee637095c15be5d72781a3fd5a613b46aa3a606023d8bc84401dc27f29649e66cd62fb0120d6e1a011c1398cb190454aa0d7da2c3f64ec2fdbfd7aa84252ffc3534f982d5d6ce4878219e48801f7e05a663d3659d322072aafd2ce671ee5c7335808f05f8a72332893a23c55a0e2df39cb1a5b46a9c2f5a67fc3a7057d00fec598ed47823a98843b3ce5c176af50ba4b37a15e00e459d91b2b1c7e0822b7308c91e4382c7c3c7a1e2fd5b6d8d530e20129a1dd5e64d1cfc8adaef4109e86ea96bfeb1cc42cd06c9506a614cdfe9f325cb9b0c13a5964e0e8f56b7c0a3b3ff90be52b79bbf17bc584bddbfd14ec4fc6263481d1bc72aeb1a340148c2b2314efac31d4caaa8b3101caacae0d4d3c2228260929c33eb664da6565bae802d35f9f7969e17ebe094b841cad0fa58f6ea754640c0ea96d29e96d63bcdc52952d79d65acfcc305062099f897390d4d6f629f27199c7e1fc6d486bc11f159d8c44a53079c5052df80bf466daebc787c33b5db70e651bb8379c3e2ae1a44dcb5fec02248d29f77ce84fe1a260012d73a2c3da02332fdaa4c335d008f2ad5f508949919301ba652e5a2d4325280f729990018b8a393b907d984c262dcb080daf048f1ca8adcce1133e31d2195ea73a57a4685dbd4d2e8548a195783c2f42435079db02c74e9b5d637c971545bda4add7866c039a6f9a0244bb755cf15d82ef84531cd3abcb631e80097cd42dad0bc069a430ade63027938b13c0153784d0a4cd0bc1ec1ab5e8e726c097ae91e96926f7da24753970b18fdbc1b951ec49a0004936cb2ecce0ea88d2863f33d754298aa00adda88a686e60ca1555ae52a661a4abf761b17a133ff893ac4ae473214e61ae9957da3746b6fe28116a23cbd401137b9d3b84bc83a78f05743fb182d85f3ba3eb9865fa5ac888e161ece5b2c4c3ddefa952213c93703a5e41c0609ad656fc98802668f43c2f2320f61290c409db5dc94cf3d9bba5627fcda1b600f297af32a16918be27701ec13794f51dfd4b655e41cb8e176f4179831588e3e81019ea0a3aad93092f475d641c036f9b85b3fdc0f7d6c0c5f3a8c9cc85e344eb5500f8a268bde89b96077d45292c0b1330c1b0c0fab310131c3e3633a23161d403aa404736a710953570cabb33f5825221a309eb3454477a99f31ac1081a43fdd57b10981b3e863efe23babb93b365297615f78cd0555383c20e42280dfcb75579dabecf7389ec9e141f7ef2abbb8d4051b3af02b85199acc6f523b36004bc6810ac8fff4e5f3b7f995b1ebeeb1db6bdded542912e59784020e28503122a7885060ce3fcd1eda1e2e422848cf5db3997518ad81f2c4b1170c644330a2a7b5dcf1ddb4974577db8436101242a35b021e0a0cf574053d2e82f368e8e0f740b7173a4acdf718e70662e63000cb2ffa364326519d97c25bf1d1d58474fff2d364d7deaf797c81d3fa9b9453cefee3ee511b98c0666865f48b1f29c551d60da5ae1bfa7f0c05ab0557a0cc06b6e967b1e8458e14f4e6dc9f853567550d50fa73d2938aab79b34503f02f32e4ca11f68ee25806318fc6f06c722f8fc46651610e179116a2fbf8cb8f411a48b3b54fc72eea4de69ebd9a07e8cf39a74c4afce8dc4874caacf7a9e91ccb7fc26491e52babedbbc2e06e3de81b011229045a888239861b9866240b727ac12e153596aac5f75ea244ff873fbc0221aa1e129bc356d441184dce0e4933b64d2b0326790735018b750074d336b7e4cfcd334716971599a20b53d0263b4de32ee54033e846a0ddf28ae15ae959da2eb9b5e447ed6c3685beda184609ae2f31533e6a836ca61944b252fc5b4bac6e2bceec1efe0953550d78d2e51fb07b0afab69b5f52ec4de4f6bfb42ee60864ce99b863cfb3b151b829ae3111411ccc31de39d00f86b9ce42559641d4b956297b90c507168a729fb52413a0689a7cdaaf68de64f3916dbaeea67963e3c168c2f7c353bc0b9aae59bcc2dbcde7522bcf2b764388e441864be6cbf6bcc514aaa05418bb281c4195866a7beeaf1ed48442647543b6bff009afc21cae481e2641d99f635ae34f31aa31319c7118ddf20ccda171bf824e84e9e6ef91d7b20cb7b18a7978e210e4b52fe4002b603f41bbd89339022653e240079a9fcd8b02164a72b64fc6ef1853417846d03ca70e2324538927bb8434ac85cb58b81ce7c2064e8aee1dcecbb3e6ec87d0dea7b948b550b6e0734dfa2530524af636da55415d2e1e66d6a68ba5088fac7d9d09c8a079abe015ade27940401277336856d5dddcc6b009dca1e3c39eb0559ba3d36c2f438d5920d8abcccfb323e558c3bb2e3a7b54c225b5afc69e4e9fe3fdfb442128725be86c3eb138aff4640ef7feaeb72ca89b6a8237eb8164e2379efece763c93273fec0d20244daa6cc6975f0369052bf965e8a6c0759abadc53692f7dd02f5f8f224050db9b47979932485e2e0d89b177cb46a3276f2bef0b6f840a0510ab6ab5b6ca29b9399c32eb56f03bbeb293e7765842b494503bb79113ad6b4c1e51cbb75f499c9ce14018a2310a0c8dbb5945cd9d8c70d778c582ce4a55c472fa028fc3d24c9cb8eacb2e08905610d18d79e9cc71b04dbcd1eae9fa163aa2a5b11c26c87ac0a67dbe7e80d0d671d430e226c09fba058352e3454c788a07c01632da2ce27649ee7781310edbc361a974418cae65eb34fd3f92f135609d75d88ca586d33c8a4268bb04c8cfe990fd9061102d306be8ebd94331ffd28ab1390eb1c2e963b5fa0693e81048bc7ab3e87bec2fa8cf8680e8c70f75918e7d59f7ea17e19b3882c71475dd459f08939006a8a71460084214a959768b37ecd3c0e67f05da0790fb807f2c04225978d6a635e41ef5cf632e40a308193a5498a5116ef75c99fa686f8015a4d2a4e80757d19537245b3cebb597d9fb2f211e4c82f050f3d6893b576b05c9088fe5fe2c619084d73360a3f0ac28335b60abefd6a6ab81a34ea0a9ba489e22026784d6dbe872ab9f6d512b098660f8209b5e90fee45b0a860fa84be25e259ac2b77662e47440a53e59c5df31aaad6250496fab6f7f0d8d1c9ceb55cb7a6255519073ad9d9a812faa422f97fa1ba31aea207b65effbc3f22643228534eb580f4aecd6b3229a51400d129565efdee0b07722c844f3d7893f1cd7166388c735151613fe611a52872f36501ace40ab62fc913ea625f74b2867cde8ec32d4e2394ed49737ef22579696af6c03c533e8cf7f7c9149864463e2c08ee4dd947a720cd6e543b560993260f6b1f217e9af85069bd1837529431231c482c33c6ba2b5c0fc8a45d614c01170fb24adf70b998f3b23cf2f13516647162eecde7861c6d28c553d57488b94ec8558087ba851422a417535046b645e23681827d0eb4d5558671484836fd2dfec81615dcf5bc58579a04515f290ba60bb748c7ac76a505e404bb67939a85264fc53d7735a30f000971a5b16ac07fa9fcbef7d41d7eb8e8ddd060afb3a81de8264b00b038ee876001fe2e0d775c89ca7e608594cd0b7cec999c76b70c835e70d890404053e50ced5322f9e1a505104d947a55517749ed11ff433fab229a069cd3591bd2c579822b2f14701f6e561b001e1cdb6c38f79f07f0e2a05645419b02f65633b4e539366e4cd2dd538596111ddb8b18bb1dfdb2d693d645e4d444528102624711ee282a9ab5cf2b96ce67fecd2599455843087afd3940c7b8abaa1df34ee00504cf3dd772460cf976e6085347ec6bb1dd3fabf92808087f829b22eb3f554859f52a7bc0d4325c0e0c881e91a5e0aef232ee843f3c064ad2f80f6c4297fec871acd8719ad808296973897f064d356fd81369be1d6da9b4856e9de7223e94044d1106626477be6804833ef24489894e6c4e67ab210d3d3198978df11574d32f1d87758241c24596bd676ad08ef07e514ffd70af4a4d985f1e1da10f7cf4a4a9179341b0cdc0ea5de4712b05f48ab79f6145b68a7ce7a0157a0d9fd01a8000425090882c6e85ebf759d15d315065c5738a84f29176aaab003491426e406bfcfd034a1f07fb9e2874c0a33382745f280df34757656266030097677b5835e6d1a0676adc84f0f378020d9473297f78647105f82834aa4815341e18e0684957fbab5ac615d7f05ff16a1230ef3b3f8a5f4d352307c8de37ada369bd4dc3b139e73115323416d8dc013abba37ecbd973fa7208cc18686f18cf33d78e4322beb2ae4a3f13d87ea09771285ba12a9cd671c4e15f8a524f380e833236ee32b9d2fa05bd278530ed58ac2253108c1d75c149f09b93ee58e02335c70b1f038aa83b57488d32e7f54a4a553b11468fc74c8a5688f712af23c4109be94c1d19e702b0accb541e4d1ffc6ab8e136d164a536b831fe1e7fccfc3242c3ee568231be822997422c04af7e1fb9cc44cef098a512c82d2d74acb3964eeebb94c0762726f63fb8fcfd56964cd5251b75974cdacfb0b513e8c9d452d19c358d36b0fdf85a516b5e8d7d16b295eed97bae4284e2e572f7aa52a4f620f60c772ca5f48e382ba12ec5bc8821cae8ae2b8653c73407e2c188fc318a947128f96358e0248e419abb8e0d65854aad1c43007eba665e74fccb0af584ba907d692b2a0cc7103d15e52592be8798247dcd7a2243af9d77bac056159496efde0910bac651eb5421c5307e9e11973d298df966bcc1103ec7b6fc3cf6d6d97809263bf106de45bcf1a2981be0aacfbb0be33952c67767ae411228b4ca177b623af4da373d2a4a310e6e39c82495aa5763e4f463d4a707712858bc19c5484651906243c6c63006fb76e0aef038f216f903eb3ffc87b5d2e0643a319017240aae46e43b3c189fa91a749791fd3485f709410f027c83b29fd969e16f6db21cfcd9468c5f730ca34b25650fd5db22156d1560b8cc8073b90832c3ff71167a7c9681b6582534e1cccfe51fb896c1834356630bdaab395230840631fbf9e4d63723c1c1e0b47abb79094be43660b88aa19fef5266ee663bf86b705496de2814b67e2202825216114a510b66d554972e868c5359533c6d41e238ced4c16ca3d0a221b0066cc785c9ded053deb10fe4960058d043d249a060a86af4ffdd23da9199b5dab9dd84c16d7ebc2d3701f46373ce37b164a3876a6ae7b2bb8c1f4b46686e8391b28d1837e6efb0307146955fa031e3016370001ae3f19382e4c80871fa0910fdb6bac7930f5dd53596e92469d95039650a23444779caa2119c83199064546ee55fa09822ecd241386314e585b274896e104557a48fb1594b17236a44f4be13e217bbf6eac0d37136cac0ee22f39d969f43ec8d286cccd77325f4c273efdd786b859ab1448f60fdcc69387e3b9720891e64645ce6926a1bba69801fd8b1eb6f073002932eed70d859f5b94c722a3f507942ad79a5210ee9265e36ddb1679e354300f502d1b3ffae98ea73b9d4a89304dc6d8bb25c7837d2a23fb756c307495ea061447f542c23f315ce8fbe7a38458bcc5a93f06510d8608902a48d41ef6fc8f1ed61d9c53b6913a2dc7c06faf689543b06b7b4d7033047e333cfbd22158188fd0006f9adc56e975d2d754b4a95cf7f0b0939c99405b03da9ec8ac35bd07159255943fd96dd394fbe2c22d46a8eab645c6ca4fc71abb3db7e5b91251f9e3a6e50ddde5ded47c637e673b35ffd275fbf36387888a2f30e8f30b9d7b71e38a5cde3f53331c7084740199220eecde8d4be3b3273a0235eaf646b68831a8360aed9fa6ae93ff66f7bd0f57599c4189527544b12b023dfdcf60d4429ab683f649752ab081280dbba4510e6a387c5cabb26d4dc6a9cc2c5091f71ef8378198e4b1a9ad25d1db60a6c6a78dbc44b5edb389676545e0919b7610c616baad1cad447a0be9136b3e2312502ad793853e8b2d7665ef45767a7b901a1555f052979d8bb56d9946322ce7f6779134a866f8ece4683fc4c37cf91feeaf65cae0e1bc4e6de933e29220ed111cd8eec50da90c4de7301f0b7437502bb07c9837d230c78c0b28744adb5502c91d3fb65bdbf8b7512f96037e6663b6acfc16df44eab39b216a9adde93f18cbb851ae64ff83e847ec2a39f9d7a510f72b9d12ff17857052ca252df46b6f5643ef1e102b7f8364eb6632e400d3e41128247dc82e4a9ae9aea872ed4df4ebf041cda07690905c592ced9f8ec66f016591ab97bd4582e7666cca2e402b0a75715d6fb10f75f5ee1cb7b288e12c1b10b461a431a52e7de3e4948eba1b7b83f6003e07da6f5f05d9a11658436649a0d6911d89548a47052cee9dd47d1180a91787ec00ec08b90d19af5adfe3df5e75426cea7b167c60a878fd14646d0593779c154ba017b2d816552676a1a936ec8846f1243c85fcead7bdd9f07d2f510bf54605a053de34b980a8e3b56d745ec5695213c657a05cdd1d6b2114d0bb037452d318a501a9636dc6ee26fbaa01a63dc5714cc586fdce8075b708cab3b18873a34e01408df4f0563ce93d655797775f784442d1845b7542974b5b3f1abde1597de0bfe2e21f9f20c38bfdf5cb18bc826353262c722ab893e9bcc4a46688a575b5ec7018946ecd0f42336cda77f5f2f2d98001a30d73c3ef99f21f2b06c6078671a765d9017eeb0fd7b0992e5a2965405854c3c62cbe928bd7adc755982d662b1a53362cf50600538e5be078207ba21ea68638e405a6ecd1403bef54021d073cacf7178fd05e593b64010f213bca93b7a535cc765f6df4e36aaa9f129d55d6fa6e531b545c3f59b5f549f70fcdb4eeed49c4f71d91ba544bfe9f81f22e394367069a4bc4c599d41f8cbcd8cf37fbf6efc363473e1e21ca0c0ce7491281f5d2d3ffe193ccf07e92cb788e9a5488068c4ddfe0ca5e5abac81a6fe43726008e257bdf8bf567c4f20d651c3320e8f4ff4cd2ce80239d2f42efabd8d5f9684c2c89933227bd0a6d9ba8b4b19fe2170d99c7f528206e795709a832b6266d75ca0eacf44313a43640dfa96a6ce20fc792f8be7d0d347858d22f8a09db77c99a2439853a37413a68d35543c0bb44ddaadaf41bb18dd71ddebc0bd9945d028b04134130d3cae0a4894970b302d84db71634a3973948029b33474ce6ebd5d07e1715a445db21e3c8eadfac0d099798207153982b867434c160ff4d3a0211beee7f128a9fbc7d2b986c91b59325b514668c1160e16bc7233646c513517c8e148f664dc09380c626b580714c46b00408c2042e276ce3acc4fb2d19a0c7a0b551b5ac4ae0925c0c9a67d5c807b4f61e73ba64bb34ddda02c302b02ded9474eb643a805fc9671f5084c3971c277fe210ed25668ae68e755635472da34fd8209c01b4019f245a842017556432c4e1e37ceecce6d6641a54f9653b75d046b03b2a2c214d8fe6e7525f10b1761cc8970d41baf8895716e3874e8490b30d291bc15e8718bf07672bf6cb88760446d036d6e61b39c6303b6a29bb635b97ee34b604081c5aa770e098d598d31256661f5e0592c345a1ea48b7a3a5115421f555ab963f19c58f55f39f5cbe9aa2b328751bd768984b679b49c38492143d5f9ca238402cb00b17096cac2d31fd2056dd607f06472914d24bba764ba776a6db2dbcd718d494042e4fa4939d71151841328aa90b39e78d1abd05ba05ce93b15b8f859b16e95562011f5c7b5d7f1c969e2423f16eef0a0f2c7d069ad8734472575314fc0f4bd4bd42f3b738fc9ac47929012294a2b00bb8e9c4612269ea25e19dbac9cdfbbafc86c3690277ef0116104720e81c5148bbf17734cef6c54010952d2dad80543974bfdbf4b730c852c70cff504b3f043df8c485c278c39d2865928799ffc8a925f6ffcd3563d221b4262f1a138ae0d9ca9de01147ebc56e80c49a00354bcd46d4a7ca750bd43f4c9edc7632d50be17aa546538e682ae11b7bc1a03e10e08a1eaf5d71973ccb08348e3e95beddf43a183e49482c6e89603fce34dac562008c4d5a799f970e4da45c44314ad8d9cc41b5dfa496b4793a81082a22c0a06fcd90b57a5921362dc66eb829289976be5252bc04d8b7baf94008534e15b9aac945c7cc3ed61f5732791c1d0e24607b7216500095a1034ce42074d1b2641a365bfe71bcd73a510f19a10b31e974011cd698f7cd5002c7cdca66af1603e1aa30ef79055a4ad6769976ef5bb39a97e3e6b7cce4c6f687c9db06b12c688a80735912b248e5c70e8c52880a327fb10509c588e8f9686c7d0e773631ca940a2644c1557b17f11dc01c98b89e9c92d16dc7e90e06e170ba8e5031057171f3bc83d3e18098d3d520ddd1dac44a6a97eac1daea132a57e3aba9fc75228cfe817d62f91478a4e5a4405eafade81cd4dbb0a02fbc63d6629f37f89727cc9f0332e753a11071a44f23dc188fc44171db41325fb433df7db81434fde61aec9b3a916251ed0d6b0bca6e77230867cc47e97ee85309c80593693c18c126a7e36e1368e822530a0078277b076951ef56ef753553d1558952566e4b57c5b7cc5a99526be846601afcade33451d0df60fd147d4139423671241b778778f1c8ad60bffeb821632aac579f29021d420091f2de9537f9ed11000bbf2a757637bec0c0d316f127a606d77cb36d1d63b39f62a18da654463be5190d7d27cbbc27f07a1eb8b4f646e687f5d9db9af50eaabea2adaf2ad97efe61dbb445de0f820af684030f43951681b5fdab947203741e157c797c9c60a97b6156beea4d4b6285bed78a1fc1f54167292a6c30ecf39778e289acfd02f1fc08541f08252a654180dbf97173b34ff3f3a2c966093b3ab7e92ff21327cb9801211db8d2ac0cfefc9ef6907667a8207b632a2e5dc15e571d160c704068a95147fd2b3a8e2593e5a86e240484ab30fb2ca5334d02949477353f6f56ca39a83e237d7ec2e75311a69982ffb024727ccdddd5cd6e49b16caf81d09a96fa3c43e5677c41a61067d12a910d83a1468fbeada3e58b08b1ef66ad9817f244ff82312bc1ee00b1ba292a6db68355ef000b6b985d823e370ddff1d1f4f9b55f2861194fd0d1fa5eb63099c6ac935bbc912e716734462abe6572277e347c64d43a4fc40ef2557f73a317c45aac5c809a820333f19440836d42aa16d54e2463331c225c039d5e5591b24a6da25f1b8e2fd41f402df71448c038495335e15eb10720d8e5b59a13404525129d21fa11f3707121d7a0332676058be0bfa04fce94d9a5022f146095d3eceeebaeaca3b6b623cece40923ff61bffd4d62e162760603d7e2bac1b9d5dc2a92ca567d1b1019630fab7f0d14f13e333b0f87ea3a00573cf0af86fb6d25dac50c960b082de5d39b65198eaadeec20b6e8e9d4c0d57fbc4ade84ce2792ca1703cdf2c73c516933e77ebd61e3186368cd15fb3f5d2135e986924570810fe086172e28121dfb7d1c21c6960767b1306550c6e2ff207fa39c125153c6d0009773bb18da8b8e0dd2bf713bbdad019f56409e70d6a1583cf73912bb53a02c9631c80f95e790ad104c353cc12ae74ad438676a6655d14e07459fed6d8736eedb6c132e980ba6ee868576dbd63fa382d82e5b932f21bf76e53cecc848f1d014f3a42cf9ee2ddb9137d3ff17285fbaa26a8704c61d6dd68fb3f16d5575ec859953a90a5e99c116ac69ba979072c14789ffb2381d847de73603714e77041ade6a42d354c1e9739e7ea276f5261353f5350a14425489913314367f338f2aac9374569409e98b49694c49dbeb014b39de762c9329bb63a5af9ce1b6d57450aa6815a5c0ad4cfa7d509bd6ad50a2f8bdad6182b80c757f7b089500f70a3d8afedca211b48dbe2f7a4b3b92084890977af2f6c57d0c0ef68227e14b5f879509bc7d529f6f2a3d0e62bfc534890948b35a35fa6e836dd37d2b49f581d0242cdee3a19bec0ba3aef734238b8df29f16c870632e87f51ee1e9a1de676ad9d4d5008dc5082b22ce76a2a04b757012683cc30f98104b2b1b4727da8446320ace608ab9049b123161eb0a3c0e4626927ecd50dc4e426cbc4f353657e159b088609367e86736163596827b5abd76ba5bb35dcee5974b0e62302eb7a77dba59137c79cd631f8c0e6393133a58b4ead6d2bb45c9c6d5f1810d4493631ec95ced91e0bdb3d0cf58d6f8c1c6a007afa5d1788738dc3883a2fe5a1308d23785cb61af7f061ab1d392a60452608bf27e708d6d8b38b0a0deb8296816a3f653de89e4d7224cd6b572cb5ab81b2abf1101ae5bbf45d6a96be881516e42dbc52f414ce8570fd03ca0b39333d91acdbbf040e0f8fb3e0e79c7d6f8a9bd267d15e2c268a5f67a0e0ee9271c8845e65e3b371f2421b9bb7b2fb95c94cb2837893a6fab970caa0aa4985c971e91fe2e416c4ec2a149895bc3b115ebcdc6d7668e66b42293e268d473e679e5facd8b85232619af6ce32c79e60efbc84f4b943056173d155139332d7e266388de176b58691f102a2ccced9fc6150f61642f1b62e20769fcc9669454dab4386b6eca72767cf0e412ed434c003aff950e021f0d00f5db33de5ffd6f9b684837413fa8e30b907ddc5256b9ca5d0fc97d568c47531c79b44cd1cece7efbf0615c8f35a16dc2865965509886ce98617525fed9c9c6332727eced978f5f955440867c2db49b6cec339d4d08ec26e40e501a9a91eee761f47a6e44523ca4f18e9849692a487cbee2aec9f6e19756ebca54631d020eb20dfb96d2deded76cd7860a825d90b554b6395f69bff27355f8b12751acb9162d70b9b7f7068f64ee8827328c02ca739a8795926a868fc8d4e99977acd668be1836e0a6dbedf376f6b19c805909762cf007ccf2c1ea0bd8022e11d217232b8af84668099d0b54428b72609c321827cef921701c3196b271aa6efb43ee04d2b08b59d80c312277a14a13c3153ed9c0c2a10cadced1161e99200cb42b6edfc4460b4735ad5cfe91bf427b9a209b9294f246aafa32bc6fd5af51ac6c8f9268a1d79d2a6bc5042887dff1b405c18ef839d2e5182fb1710897fac34aef63ba77e013753fccf48856b27a9543bd7605da5e65bc320bdd295ca88b7e653620e14a81da83011690cb3c02529a0c38ac8aa682fffad6134e86a64e09c0a455cba430ab680b5fe8dccc3f24b1e26c866bf3c56c3d17344b9a97da1555ed7d86101b766b40a119b0d4dd596583b90a602954864a263ca4126cc6b3fd9ee709413de0e135c4aa672c68c55c113281ec82393681024e1e9d592c04a6da29a05fa1d17bf543ae566b45e6684deb1301c2dcd54fdebdcba85fa457c31fe1921db99d97a4d9b773356b55bdfa62a4819a539ca62e34ba6c7ca6f7861a2aecf4725c1d0f9294b4a61568694fe8dd138bbf29066b82ba83f6bc2a10756e4eb77e39ec022cccdfbd5f4537494590f8488c4c43b4844bcc3b44656e121edeae5c4043b9d9990a9a8598f203a69efedfde6bdb20c45619a743e10835e14c93ee0019afcb44381c3722f2016a803880e6a31cf70edc697e42dc8a81af5f9c979fba95a878160538a38b24da45d19c23b787058ad7f29480a95f4115defc9e134bed78354816a268f783e64b1e8d233a6e7b5271e9482ee5d7ba162769e84a43d57e5cfb7026bf5ddb0481d260cf1f1ab82c3b5080f85fcb5ab73ef21fd31d0e12913ec8606bab541268cc815a82409453ddb6a974ea2a14ad3ed11cb7d75b16b8c6451edb019e7ac1ebe3df214cae4eae79420984f060f705775dbc6bb89cb2b90eb7cb2ed017b53801eb615370977a843a38e8d6699eb21fa9759a35c68426c4d10856a5597c738a85eaad293b1fde5936d2bc607bd33b49782f89eebc52f6b82557984cbdd0778c6bb45e223a6b7e67eb4af429972fa1fbb03927355683f3555a3fc75a3a6cd2e44e0de5223acd3c3da7426c7515a8596060609f4838f645f89f6e307ed9db9a604284960a24cac5b532218b30892edd3938e03c70191faa2b9f1437d15883fa939ca026196f6d5295e5b5871dcbe7728ee55d026686533e0706b6b3a40a4bbf5b3dc4274a03cecc8b248ef7d490dead3b8f8fcab6f730ddc25bfc09894ccc1d9ec75a8c5b9f43e77c8a438c0eac1c74a994de79b0dd479fab58e7ecd6eb265bb39ff0726d353cd4887d9e6e8ab09c8ceacb5193ab34aedb9f453ebfd7ad24ef06de4c3c11691d60581b53b966d4ea8531c6ffebbead41130558cb09ab8758aeb932f4f76703c8e86b139e3ec01b1998de53650a4f02fdbf2ebd62b381f4986a318acdb021015d644f57b2f37ea2a4fe0c7c48749e182d61368583f08b52e05669aadf1925a53006f6d8cb276a88c81ce26b9c04032c5a17ef30ee0699dd2f326ed0864d4121ae27bf9560b93a8901c2750d0a69a3d8c0e862279cf4b006514ad04eafd74fba9dd936e025be62cdba893a09930e47751eba04a9751ee15b255b3f8fcb6d72fe7251cb3ce7f8b7eeaf983e46e1603f6f926608d738e2a5233a51aca879754530978419f5dda64c21e3ea3d2ace634e3efd57a228c1684b1e54b2d19fb03f08c11efd17fbef9282bf1bf4ff8cd994a88f2f6510e3d9856979158aff7995d23c4440c14e8a9b0f48e8a5eb29975a2617192f89323c8cb6919988919038e1882b6a784c93a4991301e41c48818daaa0827ea20b50dcecf32cd5a76028542bb54d4248621ac3f28abdee0cbcb4a59383c93c8b3836e0355bfcfcf5e1968e2c5e4f4a069625eca9dcdc14d8773829b8ef9bdf6966f9f27252efa9df30a2847a4d1f66881f78111d5700732501558531b95178a5a40c17d947c89c6a1f5665186e26e4aaad25c85df85568783888adcad34128ff8eb4d9c8ab0f5520da0dea18c730424899e1f6de2911773cc63a0b7978c9e6cc35af6e124c9d3cbe502c167363731211d57bacb1610f6a2ae514b0d6f73e4b645f0b28b87f3a19f68196ac607f17bb4a945bec4392c683b0101d7865d3b8dad3d5ec429c676cfc2977787632a260f72a95b2367791468a3712247441f55ec767d8d8d8cf55f0fa574a8f2f8d924d28a1c80c08bdca2388afd33d2c92f61bb62e526b0f6073d3eb55cd6e4d4b123f8cc2d00773a8f4e10ecd7f51bc9c0e890fe0866abe8043181577e2a5fc0a391a9582379b4657795f09b59b98ac1ac1b469815bf737312644cac38358b4785d4fc0d62452f5bc01edff446f1174be40e5c4dd32540a25db4b2faa34d9791a936911877b829615c0d41f93655cae08053d91f44427699ec981b52480491e54df1e4f039197a58222445e8adb8b189c1d5d9674c7797dbdc4be66599671f47b3e24897b8cf4ea41736ba75fed3b7eb0a9d067bb6ddf9c9659eaec54975cced25a20c67d09600aeb7c214a8b3b42f418d42c50045a2e6879d6da7a8ed0239f4ffa6fa852924bc4801e11c6e526a0e7a36f6a192f3073dac2a802423083fb781c313cf075d2aaa3d0f4052f6f60904fd5c9df7f85ff07a34944b9a41faf8a7b10a118ac0b3d1c2aadaf08726893ee141c8c7ef1c779840a7052d351132462686e030390e36f7f064c0782db4a1fea357c5479162a1e3f29a378c5da24d03e5ddfe9311f85f69646f102de6b9bf404c83e015715fde63547b8624a37720354ff686b2d3fe08ef7f15583b480c1c3ae000c6b7b50017877e6c2e61760a35e9db6b27556fe349732d3bcc52849f1d08ddbbf4c53191ca8cf2b7768176457b07a01558126c05ecd7f16db1ba35bb399404ab390a0d9a212ea0f4c15152176a65ccc188e1edab9f17db907c9ea4de263f3a240a38c29c9646376dace85188c2144ccf77c503d24a0a9acfe9a546610bf058edc74df9ae07c99b61675a997e5829ef5c01344f1e8cd0d47495d6c7df37b47b7bd2e4f497ec90b3d63e62286c180b58c26b41462ca1af0f24a2dccfd37f9d7654fe6f7bdb756fe0d0791d2d5ea4200c508712ae8042ea173dcedd79c47a864596597582fa8ce27ed9d52edc41d393b81bc0b04e73a7852d0f998164f9696b3e584c8880fefcd5f79d3c76066c82bc400ec9f6f1e94c4b34f82b4d1bef7b827d3beb39517becb45192b8b99013438501291c96e8345eded7a906b6c949773ab95f6ef45e62303dc3091e738e0f68a66ce517e390f8d4201c976a6abf0cea357d01fa1193d5db134bbb9cdf9fd58129d71e3190c64c341aefdae5648e3a0ab24d2489f89e65e43d8d6e5c52b963c3c1216f55b5af7c00f57ae5e3f91c92af42db560ed506a678142bd610d86725fcbd5d1f36300c300015e5520bcccf810e0e9141f699e01731e2192a91c5b1eb1783c4a1b1f393cb496f969964835b06ffc5e7d738f822620de474d422cd8e113e61bafc75b6aecd1d5b9e2984fc0bbaa813223ad77a952720937c0db52dfde8a5a78ff85a95625f56c45900148c04c2a429bcab3c8b286a89579dd2f19c70c8c422adea4ad08236f677baf57ad322c0fcb493a88f024acc91f59bab000c9f54de1619d524757d37f8358456d8f3f7e2bd271a950f5ad62b5be80d68787731c305b48e28bc216c81f4927f881d7242dfd9810d0747ab548dc1372f3ea5741ff6eae49f60e61623c84fee805021a05b432118e031ce1be1f20ab3e72eb3181a206cb7fbd291c4bfe5f4692894041c4c968ccdeb6160fd1d0526b919f3c9b639622ee726b7b976a2b75cc0cdbac3f33f33a19bdd7baafcc61495e1e7893e0a50e5bb779584e02302d78cf460debf431d5abb1c9bd247b9491d58989a0d84466cce1872eb0005f1c0c89cd0b4c7dcd42feda32a1352801411d6996040d11318a22e11b58a4ed0313885fc80866e9513d2ad6a65f7a3fb7d57c24d95304d000d465aa4e62677e525b0a289dd5bc72baae0d1f57a3ac44fe13753e61b4f7fe7e60dbcde50a54d2d659315f3bee887c27896b387ec4aac2d78665ccd3bbf3da7664651e0ad53e66130a7cbddc6a2ba66f24435f50779880c660c137b7b163d108ba5740c1c7f5f05a095980d4179156754c1cc046f9d6343ff8d439e41b2062f67a8e620246720d85529ec4bd2ae04dcf0b805f920476e254b67df17649111234081900b2c5c4e35401f9d79d7e87f169024d09a9403e7125e3263bb4fbba73996b19786fe5342da46e0c108b6a9d35f5d68d291f8ae0c45eaa0194adb57655c405d2b3a55eb799354cf42fc38161f554eb32e973117cd79a12c45d3a657e2f8cedb037bfdb0e0fa042dbb6756ff8ebb0b27913cd2f5462308d44a5c6d0346bbf3e85c57c1a76899e65c856368eb64e4ab3056e3d2cf4d246eb8b7fc7ee40aec19d8a7116102ebe493bb033e6afa92357e1d457934cf0e64b7b36f3f50a262632b82d376ae7ebb6b56334adec543197d3e429e62a479163696d43f18159cb1c9dd5db5328f0752eee05e58936cad6b12ae4ba9555d04ab623545efdb47f584506ffdccd6beedbacf0265cb053260fbb2c808c8670fda2212809b558a1bd7264a8fecbca20887216350f2c26f67c35d42986a9fea2f4687164d4f6fa7c4cf407e478d53f794dc9364d001d2e7510dbd2a95d53e429296fc7b21ae4269bc7f69f04b3c1e37aade867ed4b29d88b2827f575b0874275c113155a52b4334442d1b3e1993a145e537c70ef1ad029a5d30a8fcd96201f9046bcc4cccbf7a89d363548e84607816fcc574331020e29b8771450796d6367f815dc25561b1ce4bce10a74588844322ebae6821b469354b492e0589c1bd5bb77e4c5c98fac7c5550778036336e83646414d64a275e3b9b6aa9162f537b75e109f24599646884b76b31d9e245bd78614b8c82f656541b22fc9d1916b03daf6094faeb08abcdbbf39bd81282135a78d4867752ed5b34bf1ba024d448fa9a8a2e54d0af524b8ef1b6eb4c31aa34842d5be52d2c69fc38528688b8cde3f8f816f1a9a60ff19e26a0e417a44b9aa6629e09cc547c27da4f99d8351fb24c867aa35a53ef766f5e81e5274209f47ca7923fb4b672f8f9d91addc87e57925c4d799f89cab2a4c9a75787f57ce4b1a3c49466bf7c3e6c280a17fdc486512c0c5be90ac2bc6a799ed63f3bfa0cba2e12c1fb2797c74cdb3f8b02c52918825db5309a9e2e71312a148cf55db210f1e8f3ff4db7953e1419eab3128343d7d91892ad666eba0845d13aa2bade8852d70dec4be8758cb788dbbbe06099d75adce38ce80d83fccc4dee8f92c103c1a15dd4f75ca3b046395d938dfa57fb65f38c4d9d5a81157ed7f669a63f567a71746d635c4d36d17545d824a4bc37598ad8e951f002543f72b35668a0765438ce20d03e9a39807351141fd803b457623e2fcdfb922c664706369bd5573357c26b6e600122391564e78d637173ac157a5cd27f500ec13962d52b884ef9d1b32a17db6f688377dd58d6fc9bf8951362c06abe22ecfb09362ba4b4f6cb77375ad4c8e9b03d8bd79966123325816cbb71de667c52490c6cd04a1e442b9212c5bd9989e4db66516044471c930d8e6c036b9f0aa897f392f83671f22bbea18b842df90d6e082b391baa0783e8051e3eda3b0d0cc34b14c3bfdea47601d3860531be91ba215e299577cdde806b6d2bc66524c388a0d5cc09c22f80fa1ee60a0c52f053c527ffd56434a6bc5b847d9f61466eb90aaf528395998b166371cf313d8ad5173de7e75b2a57caec316214f12ff42e81e3b9356f20db3e38fc5e1b1abc033e77f5e1f4e963e222318125c1eeffd7686ccea898ae6be90f25ae7ea608cf568daec4f0483a62952adb72cb293bcb0011576811f05f21b44b8f5576666adcd73b3015ea5d0a67ba22717299eafcbea916d6a7022818e0d0e805de9ecd32ea77278d6aeb6630f7696ed1a1689d66d5d459244db6b0e1a7809375b8f20f8e73bab882deec3fcbcb85c2979ea87eda843f72b2717199f2eddd99bed6ecdfadde3d89fd8a21d76c4e7ac65e30b3f9507b1c39dfc447580c83c25f167ac86fcccf2a168c56664f9c6ab81a60d6a2c9983cf002023bdbd8a7641939659f5052c5a7107559d432bc25fe230c238fb5aee4979c675a83e0c542440be8b8eace49f17d14da09fdb8cbf8cdb68cc06f6ef8277eb368be3c9b249075d611164c354318adf3b1c875dac67ae5bd5f0694b9bc10287362987b6ac8a8eedb26837d2bae30c3a96bc85fffca0ca3d3fb5b19a69b5d20b13f6aa6e6b136f791a7bf30343940d899b8a91c38fbbf3f4268762b14eab5b9b66b458450f76beeebe34e5995e207340e3e65f72af775ce3ee18f7911f07e99cf44f7f84c9f9736820674b48b1bf017d1b9dbf0c311aeddb53994cb8390aee2044833af61611343f1ba0d30fc4db4b762645a294be05d1f921e114dc95162b9373679a0b80407d705e5307f43119064dcb486199486b0a8c8321d2c01675640f83aec46723be8bc4b176616414af949179a2aadc87bfd0915194f9e83554792ce68fcd24cd35667ed366e93367cb13b9d7b028423f9d9eacc5c4fd7272d6013753a60e297c9e6b258fefb95d9a6598b74d848cdef2e13284ca26fd5ec74e96b4500aec672d191dbf960c5995521e05d2f9ae638040c226b6cab82a41dbf907fc91af50483034b5640fe1be0c63ba4f3da8cfde19db8939dad217dc3a18f7d96d03a56df1c9be52ee297f85ae9b0dd59f991cd3dbd8462b19f97d56b831e8b836d72b0402d95f8dd5d86a82cc6e241b8935fa76c80dad704b1d9924e5a653319315a0e4ac3ded5a3ac7a08e57b86c41d38d2f63ed9861480279d29d3bb7d36a8230455ac7ab3bccbbf5f6dc8c39659f43d206cd9a874c6250f47dad2fbe8c9488d2d337c386df8f772abf6dc587d7e34d7af573f1a9017699c37fd0a05eeb1bbd202b4df64087de7b0096fab7b527773b45242e5dc7d05218ec44026e10186fac4104482db1bbf03b87258c2e4aafb8a13e7ec4257b48f6817498e9d82f4eaa82d236f059df7b8cc257d3ac7135ee3e471d550f8c8afa26ebc181a099aa168567921d90cc79e7107a1049b5a389b4ed814b5575ab4c9b565205f6ef92d90b3c83451fbb7d22f2805bae12d7d7d980f22000feb4f4d4b3dfeecf48ec2dbeff2760d602896bf02602e51f5dcfd2ccc8d88fc7c273b2871bfcb411b6fa861cd66281924d3bd8efd668229af4faa0a07200a136473bba907b45ca24b9d4087a7216813634774112d7381c7b9e9b690da8b9a27779c089180c732feaf1f2a5bfad7c71b4f44f4c7b3e00cc238ff243adadaef0d43f52a2187ef71054604beced9d13a1d1c031e319f53940b716f1c0cba017fc0cb16256b622d0c84ba846fc34ffab82d5e23a612130bac207491d16a8ac9d32e55755629ce2327992d4dd4f4a818efc54af7dca47874f43c6458c77b288cc381eb2bf44109108be64e095aaee254a4dd29c96c397adde7d2bdfcfb3d468d0a2422121c7a56dd40b22b56efdac842140a5f51ee214900f313720507b7657532f94df9f8e2fbd09a394b09972584c682a315f256a268b412ae5678f38e212ea0e6e9958514eed6fe45315579475e51b9cd0f62b29dbfa8bf881422bf8b71732755a3a0a51bd0ef6ac84d7290fb28b0168c275bbeb2bbcaf69316d03ed26e02b533cfe97aadf6ac067b1e31156f706c560652f032bc84f9feaf522890c635b95481f1ba2efdf0d68c43f61e2c175944007b63829aa52cf4f7a6b2c8599c9956bcae628ae68bd7e97bc3140f1327fedafacf0cbb9b79d7be0694fc2c0e87256215668b6cafb4152b7ef1a1a7c50ce91011632c6457ce43beca06d7fd66a2abc8aae84b7fa124fd6fb8a74e1cc12f6909e41951725d2045caf6844688a266a869e51d1fd4fe2ed2ecdd9bde6f0916dcad293f6307c4e681374e29c73a7716a3a028e871a09b3441485fb5230f1323b0977270040c41850af4f2131c2165158e16996f73e64c7a9b683ed273f0942ef3418e401c180d31db3b48499c37d505873efddb273904e4392aef8604ce5c1c1953109a4f3965f2fd76f6d631a3c7a35365dc559aa42b4d7ce320c94f44b661539423f037f97e87ab15028e16017e141e1dd6a78d38c8576da51d501491c0a727751f0c1ff5f5eb6a214f4a20e17902795e2aabe2690d1b8899d6ec2e1470914c4cd1be3bbf5709a00e429aa4955edd9e5cd3969c1cdf4523833859929a7960661abf54734c521c9bc3ec4fbf59aff79da49dbce0d95ee4f372aeae591fab004ffd87351c56c048e696ecd44798c144b3a323c0bf2b6f5be6a22d76efcb87ea4b67ac5e6c3045e22ae2d869414abd3af4549aea9a476a5e557f9fe26c6a0b5b4471ab68541a4410600498f5a24ea20de68d49dc36bd9fc227aab53dd53b8a2b6cbf13d52f7fa54633c441557f1bbefdbe73b60a9f9fc0153b2b6d5560225800ddc469fb4db916f1023a36d5de8fe3b201690ee3190a02ba78ae4f276fd67dcce3034f4a1ff901056577881e48ac0b5fe051e014dd82e4c2b3850616b4d8fc6f5583a4065cd2ba62a510a58c1c59dde2a5e4d2b37fae4c8eec6d9bd58e04e5c94f3cb00b01f53c8730cef308d8b993ece13a743625444917dce52edb43dd966d0889f743420a44af1829631037a45ddd291d459351d624c3784efd1e1787307d9bf87545016aa463e5bf30eb9b691d822d1da7f0a008e6354743627c41a15552038a5ee4291ebea7ccbc3667f3f43ed75b908bf709ede5e459cab58625715c4507c1e5eb5e45482d1f6795eb568917c1e0d7e79a4deee4c26417b5aa8aa1f4edeba1c49c3f814d94d3345560aff771bd835935ebe1c696d6462a23a5a80267439b38c84b50e2cf474182a130cff942a999ab366556dadbe7e616589ff6871fe4eb7a3f0716b5bee1be97986cb55b30e3e728c09d03df370473b61c0511df7904f1da4be334ea0df1c5bcf5f03065caa91238f0a7a953c844bf445b31f76f9262b41eeb0f578f83bde062d48473be449fbf97c542b68458ca5cbec571cdf83ce50d13761c199abafe556a28cbd68f3a7ca5b4cc7e94abbe3dddf6d5481b8260c285035f4d67426a22817d3d91207b688a0db6527a0ba61fff8d731a581571d0b17ddce0a771eacb171390b4a76db5dbda56c402fd145de66c0b6a1e7dbf717f2d54b083b523a451a50622236e868c742a3f6fcbd0b8fb330cb5a16d151c97ebe3136dbe4c119ee38a67499f43588465678830a8398972723210200f34fe1e3284d49d5b0447176c082c18e33a6f15c86f1a9295f6d6b5701ca0dc5365556fa96eb6d4e5980c0f38750d42ac53bd30519be4b433ef68ef69931f501fb5967a3d9ff209fec72e8ac72160914d8c6df1e36987b82e57662194572fcea9d28c0c6c30b4e9e1f8392e7850806e0c19ccf7e5ebef4dc047446f8df379206f1153c784eb5e0da66d64e1fe7f1b18cfc7dba411260444f1bab6aba93c859ec33b72f34e4fc1dde2911f0d5c74153fa9e9bfdfa24297aafd9e2de9908a604b520c93c528da00f084e55ebbabfb802f235e18bb997c2a2036fbe55d337df069112d4baf84ead0cdd6a35358a7d363b61b965f328ade14b81e720d7a536fcb7c8a0fa7e08a5f2ceabc0260f726003f24c44b1924990de8ecf641934166144fc54c84f03df1a550791d823af9b1020db0eb871300e5e8e5d90d7cd984b4cb5c970b9b522fa8cd3802b9cf5270d788f0c321718001a9905173b7306b30079f31ea21a87779a28abea5db9c6076aa736e2b1a5b7be350fe0df73f01b70aab1ef23f46e78d042da03bc98ee03382aa2a0250e3f01f3f47c63b8d2b7e0920407e59b0cd56b6c49341b0443104426c13730a5bc2367660307c01d403d0a93168a954419f3bd95f4535a75669380e4f21127fc9b0c0a8103443fdc6ea2ccbaa99d1f642b7fc7cfe075fb4767b935f65ed19ad7794cff088ded36d6b831cc8d9e8490455afe92fe3a3f01af159b8622795f668d738d7410d0ee94b36a7ac71fa049c9fcfd5d6e1adc2a7ff0c0ab338f32f2b996b05194ac84dd3c7a28ca46885aecc6d78611326f4ee894b552f3c43eec067fb5e9aa5c22c738007d955be3e0d29e7112d00ae28388c2a38f61641123f3368facabd5c6a61077ee85cbb73b823755e65e077fa7a064edac6d2b94d15ee84166f0d46bd73e198c2d34d0344cdb8db75fa2d80015537dba28d826a1c1a67a379128515e40ccca50dcf0237080d515796509a2776b9130ece044c47901bbdf7b8735ac6399b38ac2a3cf8c3153e9de2a42df3eeba448430c2a8d3bdff4d3f326c7b9fe72da2119c2db171ccdbec41083d331b898e4699c3a3a89a651623895fd9d876c412f6f7e0d72e97704c022c426d92ceeedfe80ca1d8161456456bf04f32dc5b4578558bb3c7214e7658769fdbce2b971947486c943ea88b20100935e7228ecc240f0e554b54339ac59e053213d35866b0ab49d8d992d649337c110d9271e8469adc20707f42ee98f85a15be9b6599f13d9fabff138dec664038263f7c7f5439d2ad1477c5c461cbb4541a063d2c2b175e6d788ec432af01a3a03efb98fa5be3911340e30f17b0540237e511d96639b6b49972688dac71ee03064f29420f50d9a7733c0b3af73e4c3407c3ed1452894df23d72abb3730ce2e9bc0331f51ea892405ee5dcb9b1c0c342dd5f0f9879f37e1bdc2c5ca32d8e5e1a7e4119e97e4c4a3625668f5b379b4a6aab0407d64151ecbf188ed5047007ed58266540c8bcb9854307ef19e1a6c92db8bac772d7bdca43bb94b554ec0a43bc136a4d5f7608fbc0fe588dce04f7ec14614bc46323ec9b3331a9de5795cf80c0683054f2f10adbdc4e97f0d06d0b6f0f29113b5e5f968fce881f8a8c7d04d45f70013e2b723f1eca4675758e8ea13614610d0eecb83aa3be3ca24cc5e5b27756137b196ca967fef2b601f2e1c7d1762304c1806fd7925df4dedea9842bbbdb58f9831fb1d4ea7933036154bb92ac807fdc781d046a709fb7fcf0d8542564586bd0971027159a44fc8e4604e1384a2af2bc48d6280ced07979a05e3f6d2ecc76c16816ff0da5364384223299396d5bfd605dfbc3b50986d096e31a7e48f6eaf5a75944faaa877e8f1afe0dc3fe7fdcce2a6069425b111c12d69ae2769254b73c43f384be46a0ede7dd6295d2707059766053a2dd8fb7fcd5fcbe412105fe2355481f1345dc40dafbd49701ff7ac5ddacca37eef947584080bd380641b1ad936bca63a48937749bb9f618ae04c5db829d086bf48062d73df550d4867363b7aa6943a0858713cf602ff1187c8010ca0daa91cb15c38d4b433dd59ff9d1d29019f7e56cd52b92029d1b4a73ce14610aaddf06574d65353da1f4116b1e8ccec95ffdff87f3cb4382eb08afc422e2e6c547f3e315c88de250c2a9ea6d003cc2f6f4d7a64113a9d244d40a550759a60fd5175b1ef6b9ba5d924587e32c0c50e251fd02439ca3128cc56376c5595b5869675a750a6d5b6c58d4abd3387af7c2ca0c12ec446613580fcca619137a5bdc334360342ec358c23fb4a6025f0f00e54770b8790f07655fee2cfa6d0c3b12e8449e6c6b8c3ede14fae10e0712f48ef1000b6cb649c1b5d79bb3a2ccb46ecae45e7b360b013a73e585f6b27bb00f78e22f04a8d5a84ef44013753baeac4e44cf8af705f91883973ca40d29dabd4b30c770468288fe4a55916703877702c0c75d7200bc94eaea008c05f1fe9248b089fde008a3ff1140171644045108dc1e87462cb24d3e40d12b08275b4d6da09aefae8190ea0d7c3aa5c4ebc6614c61adf625c848dc527357d12df361dc847f8fb7a58ae9b6855eb0b62ae4ee12980a7896c03e7844f066c0e362382bca80affc1278aa05724bc64330f28c39ff6ea6b9d4b453f4534aef23671804530ef3400dd8a34dc6b53bf41ea546afcbcb408661cfa87ade8ff059387bd0911f80d0f36d5ec15786a773e69430ac8fac6860a117ca31c41363db8ca83dbb3a79095bf0aba0626a6cc04efa7e15e1ef2fa96ea7aa490d946922654e019c2a08462c9682269c4d833ee5af9811b67ef1f44cc68d8436f07c1e0d94a925882f33edb3216b652883eacda6123ef4350850983b86c0a17c790a23575abfd3d82ecb0a843b4d7d7528d3bbfe7bd8fef463bc92a166b21e2f887d24b77b8509f379cdd4c6e1c6f93b39e47ae8a46df4246b98221483efbff0386108175315acdd09667b5dffa73c1821c5f73318f4781408eb356e57faa6653e98b30329c48fd6e8a3751ddd85c28f53e8cbd1c021c2142c0098c5ecb2c0aa6ee37449c1eb3875ec3186c841e7418bc8a5a5eb12c7db619bd889b0446ed752e45ef9e74e6fe601ae76cdb363088c16973da9d0137fa4a198fe34f6251d913262cb74d4442ce0120c5843b3d357b1777b4e0274f692bdc06eb7e22e563e88d0d8be12915a7de6ed2c2d6727a0e591253db076b9d14712b499bc53783aa60acef1e86349eab97314932a974fef91f4433280c2669e9710b2d28e5321d2f6a7c6735ca6a3fa11aa6d3fc913b7542dde3f0c40617dfbbdfa35febecbef922e2d02fffb9ef0382da99467b8e3b924dd8cccc50060fc57d00f223247c22cff8bd8902050bab07b9be35823bbbfd2d36951da0daf62d3564c5d16b6276f675f5ce315acb3e53ae0e96c5051fa4e8f58de576317f15cf218aa107610e2311d30915c49350cafae1fd40be1641d075ceec846cbfb1483567551a10006aa45595a6b3df5aa801f6bfca4fd483d51c4fbe29b2792cf9929c17c038c036f479c516b5770cd6ce2d6a3ae93df61c2d7dc06d0b1ae8f59217c8c13118c44a6f09ee1f1898a07c4dfdd01cd4a07181171c8d57f973cf5bef481502802fbe39671fb0012e1ec636df300bf1f4f1a766a83d3962f07fc83d9780088fac294d683ccbb5ff5f0b09b933f21286e892c89a1b2d4ec6b67fcdd38b0fc5d036951124e4d9b39ca3eaca90ff71057b5d8fe5cd708e07939841515139717f4711b58256b09e92bee0713e5216a94148f1f8a8041da5b9a6d26cd4cde4d5e5f66c8112190b6db4d8c574217899f3a19fad5b0c4eeb07be6eb439ceaed1510a72745aff2c2a65215340eeb40256d1845930fdd9c624583c36bfea08848ee6b2fc3083304cb721cadf30e93231e6fb9e7c142d05522d654873f4c3bd576ed0b12fe0092f1fcd9f12c132dcd3ca5537d75ec5b754c768662acde25dd98f057e95cf1fa77028a66e0b1de2989268319a317b69ee91205a17b1dcdada826367f3b52f46ebda0a2fe8d60755bb0b5550ac1428b69ae20a8f36871a7d3ad919f4ec4089ae5a3806c6d595944ebd8edef1f9448affa5456fecad8b110012053a43bc16bbb6664d1be2638c357335c66ce4236893de9cd3a3df9c0408d4ca2143d58cef4882c2be509efc3f184eae01388750d0ce57ee86aa2f52c979f5921b603b80846b948bb764feb3dcbe4029e77cf9ce0024f468af1091dd16d45b9ff92bf9b1883e37e9b21e12d64073de092af9e95abe0d65a7e534295268748b73e37d55eeaa374bce1a5e2f653f97ddd9431a7ee5f193cc9953f17f37e42e72d41641169ec586f23d945ca8dd9578c402f1e088f285672a17340af3441add448b8c390c8cd4dfb476eb6f8cb77ce5dd93cd98e66e8b64b670597af7e7d7a468ebffcc2720736caccdd41ef4c32d2d339e061aa90b60e576249bd36cd456e4a2d94c8b221b90923c7666259c45bcf1ef4d74746aceb995b3359b70bdeac5b8df2e4484ef2c057fce84c500d5db2e625fd5fe53d47432f79fbcc6d6d90ef0b802ff236909155dd972d2705ac0c566c1e4ee445d9a37202582f0eb3f11762f35268ec1e95f93b8db5ae52f10e5cd63914819af04643420c70053f833b1033f3d68735c3a7dcc0c546119b7642f1046a52f5d3da5819dceffada233fc5788b23922980e9174f0256507186e54d7403606392c14fcfb6e550d30410a5429b53cfe1fe89f6491f6b86482ab9c182c7479f88b0749d201fc30fac4ba7219aa222ad0fe898b655e9fde32ae71cf25b530172711b4b832d04f95786a62f30494e062864d9e69b30e5868ada98a342651b1426f07188be95816f37453481800e1155591f1816b6ae2e37da36e572725c475ba885e23f65fb3d67d3e8823090c133b1fe8784b72d6bfec11e045ec630148dc8bb18495b25897224e4087fafbbca15ed35334dfdc20825787e95b44cf446f5a6db1a877c3e9049aae9415a45e8d3cc5c6b006294c9abad27874e55efa241a875ef9dae4dff4254a2291b59ac2fc7e136555c2ddca276b433a6e18542c6ebdaf804bec748ae6baf19a1efbccc65f07f82785bd1afdb3fe7b753dcd65b14b663cd7f97603394418e72e700f16ebe0076556d0aa8f1c9de1501d87685c3f023d683b27d4750fd2c14e315ba459b424e9be6d56c78decdafb05f22e071346280e121a88db01c7f9715059af2f7f4df75d467801b3cfee54ba8e899e6aa66055ace43ae1f80305b6574ad96ba11b045afc7f609785d367d80c1674cc56ce20192c8064b14fd9e483d8c3357048d79bd1444f917a42ada9a378e9a52c3776274a34e2027de81e2a98262f589886afa6397b4a5b22b25b559c625d8db581cb8e5ffbf603aec5f2c063daf29b23abb39b6e887d30c669a9944bae8ae5943715733f67a4fccad37d83a1f29dfde5b81dfb275ea35e4abe93190d806272a9249060171676e5cb489fdb11575d3714a3461ce5be9c3a4a22deb464cd0f8e3cb473ec2373ab77b92555ed7a649dbb088c84f9b9c99958bfb651f5d4df280a93fe7a271a05cfa0ced4e580c99f33ba177e2e2cbd97d30cbc7d3c741fce8e0b692de61b46ec5498bd0d7a2be371a87e1645fcebb3a111c3cdba21fc74ddae152e74e32651d1b463f317e43598987ddccefb4673e502caab752dd8694418d16527ef2e43143c3441523cde62f7167f9bcc8430611a9c7bfadd850b7ac8cefd45c77dd18a0a2adf9a3d842b78eccc0d7b2210cbed649dea46d6cad357bba072bc688b42310d0b28089533e971d4135b68ee72d81d9fb5d345c4174b355d1b9b1d7ecf5a3d20b7db84328979ae78f40febb139d67d2dfb87910e44a29154f3d6cf2846d24029afc63b053e90302ab8fa6e675304ff8e8855c59f540db639d03aa2ea3d3969705de2dc5d0372c50ed47467d656affe13804dc27f9b59e944407873a0f8bf49488354e267245720feebd72a60b53b02466b138a76a53cffecff1b326675aa215e1aa518e3e46ac214423f3f99e3ef1cedf118b709bdbe73523cd9a65be3d213458ed34ce6e741516bc992d584498aefed70eb8d62caf65dfb9ac2523fcf0ae5121d05b44d249e082a2828a10c16920c7f0a9308f4a2e35356ce546f43937c58211d55c813ca5a85376baa21182217b315d1f483cdaf6f4872413721e7fcc26351ea1ad45920fcfa344c26135cfef13760a8bd81277702e9615d020e9566689d1aa64ae1606076f655b91c1a31ead13996b40cf2cb2717d5a8c890de5f75805f32b1dfab5993b3ea527beaea87d594838ce0fcc6603503972a4d92153eac0f8c089991f6d1314426a8e3635bdaa4f38b5baacaf094b04965ce74c77ca26384225855b92063b3b4df68472ce4d8beef9bb9087d17bb20fc3e07bb2327a414bbf263ca44ef61469e12afbc51293e8daccf325529cdf00be462fcb5c46c287d8b69237e8459c19e526e46bfabf8d0d4d5fe938e8ce3f65d299d56513cdc71302cd93b7168537724f54910a04e01920113cca2ea265e9793bad35dc6f321751788a3006fcf07243404786c8640e499d1db9010d02bb2f5c1533f4c4d7b19a98056ef245ab1355c1007b2b3e7d54c9be1951d906e17f91b3960737db0be1b331a58cd527d383409268effb134d87dcb9562e5db86dad785bbda52c7727f4568c62b2e293a9cc89ba16abfeb0d327723500f6c27dfa5111cbda29a8217a80280bb988c4416c924c898dd9aff9cfb1fd845a637a10cca843d0b8f8b552d29b2f33372d23b87a632fe62e85f2bc9bd3e78f3b487d2404e2744f08b3fc0fa57ed7af1363540fab5fe65aff3e22baffcf6048bdbe5f40c0040e11d4f786d82ef5398700f953d383bbc0b2f53b3c67e44626cb8fc183fcc26d892d174587cee4d9eadf2a5f00d250b77aae93c76ca6d5c2ffba3cf616756b2b53ae5130f57e48ce53ab8d7b76e3eac337fed311fe9700b5a522041345d6c08d6422ac8572d4785d352b8874327bebd193cf89c324487021a5f1fcf1233a3214ff5716b912c1ae09214e41eda04db532ac8a0fda0900b8db3b45388a4d93a61e106820c0de4921a66c75667872fdcd183948d7f2cec30eab4cf469e854de44adc51253586f4b83adc92069a580878d69054ed7b60bfded235f68591377a003799e57ebecca25ab31737fb63ce6b51c1f4b3d4d0a4946374bd269ed4758daab57fa944acb7848a613a7ef0f1fd5177c9682108f8101474e095360fa51fb288777252fcb1688c966dd2e34ff75d06a5a9db2a003efcf0b66cd9b4b2cda63c7c0fd70ee3d6828e82742cb56b46d7685c4ebf86a5b9053ff6b731c46d6f850ea6a1e1cd4885ef53db170622400bb123c6fd9241d488b702f249222b189a074993a6a00ba1b43b6dc1da78fd45ef1b16728a1de380c3c18cf1662c804f8a0671f36f7054321ce8284acda5126483695ac993f9f7fe0dc4a66bba6061c1fe40201c70bb763d6a7337cd4b10b9ff4cc02303712e0ac683cc7446891a3cda825aa934596a6b25805f83f9a2d2e38b09dc2ed9e9e85e5b4c81046504f282e6775b8e414d6a1bcc909640216b298126e3be2d3648cdc8170770c68389ab3eff0ab908c1a3aed3037800e637121170bcc54571a01637a1afcea81a5f29214708b14a6fd65c745cc0ffe8295ed5ad653372ad6a512bdf8a12fdea3b077ed5ad7deabd895e29febdac67fb4b7128187d4ce44f642abf3348fe6e5daaa36dfb11d831d4715912d0081f7048bf8d35583cec4c844a0239eaec680134c1eefaff45fc918c2a64f70457f7386e521a0e6a8df52b6948e49753fe1b02c6e9a86d635ed854d79f5400a21a9d6e4f1573f63dcfe7a5e42f353731e5f5f4db7ba2cd73dc655204b97e6f20390cc45777199120860d47f53f43d9e4e98645be8c4989d47d705a5fb5202b01c59f8ff834f8f3a4f11a797b387c4196debbe003ebfade91dc2dcd0c573f465b85e102c8e45ad287581269015d2fa674068192c19424a43d4a69b8cb5c3c6f9d2b5eefbb0aee150308540c83053d260f488b9a11188a1e220ee96d4420d08ac4595abfef6b8dfad041076cc410cafee4fc8a8c45b442d478b2abee82218bda8e32ac017004fc3c65d348e543492a5e6bfb9d50f80e827187a2bde22a8465f1b18bb1502c6ec7d51d4bd2582f6ad199c983535628a2cb208b4ad95a34b4a7517d7c969d2504d8fe9ecfecb86a5f1a44017b0b465698cf0739cac9e2792b1a99ecc6f5fa7fdaa40473165015ca2be4548100569d9c980b2b638f37a46e766d948201df43c5da266d8c749f17036e862db48bfb792cc1875497d793e2e50b707145044f2364c198205fdf384e8972bbdb54b8c7a4befb205ff31372aaf1509a00b5d51a6e976b110cca60e3ae7f41a2d7bb7c385b1a5d098753f402e2fc7eb2ee91e4eea4d22e1f1c635115247c47be1d065f8dc156b1486f1657ca91af13493835e9430fd2060450a7d204a5121769fcd8648de2ffb6501151dea2f89d19beb415a8f3ea90b98ceadf4da729e9481c4084c3185ebbcc419f61c6de3c5d454600540fabf34579728c1298d69eb714aab5fb5f3ae9bb065ffaed9026eadf93d01885fe3bc8e7ad87814545c0372359ff5dbb5829b9da10c6e16799f3d4359527e7f6144995b67e9a5341561da3342bd00f3689335a90fe019e6a1f69d0aae8ff046587a5c96885106b39e4e45dee2de8d5a1739f55191e1c48b17f1f1e58a195a4b4b79ec8d14fcfa6a056b7362a35ae5ac50722b70e0f06542cebb7ea6dd8b51fe376da27f9821e11becfd66a470a878197f2d3bc1164940a045d9d2fbdefe1e1b9cb1ab5dde0d1c26001b5bf7c01ea364386101db387aa6a01cdd6117373b8c49fa32c636c4b3bd13c0d43194b2d1d577f65b0972400bcaa07f0f66bc2249a3d5fd6ad64052b434167b416bda718350e94d536cee13015b3254fd890e170d3f1a998451b8281129f562e5e62244e03692e89c4050c349d228aa7528648004fb03e7cea40563ada47b91a025c22300bbc87cadd93093e8acdcc693af78337c7fb749de62cfdb2822e62cf90b5d71afcbfc5575f2e0dc9e8aa83f201768c4a6ec9b3fbd5b8fea81e15f15de93e6d4112149015256018094478b48f92133f4c9ee1218419d93048a0abe8e90ab17b6a78a4819b7850b288ccd219cbfa07a6a4acfefd00a94b08bf404a0afc9d99463b190025c67d541f062722e194c9e4b60bdef0dc9f4897ccfe054f49af1e8a095aba157498f0d3b2a5a83a2d3552a40a78129e6e8af7567d453a2b2b82cd57db2011cb7c449c8b3fb2501fed758f27a6f618bfccb5c311d0d60ccd7d310de94b5c8572cca98a005e6c6ee4900da1b2000c2fb429bb19568d9bffb74b0a2af80402ab55a58f77f54412648ae5f856fd43163432a38cda5691a8a7ed92b4db79084dac52341de585df9d283dafb3cd4009ef187faa2e1873fe75dc90eb988d3d30557ca439df717e2a84cfbee68f056ba0baf7b4974227ff16a563af8e7b5e7de913ff745470fb89043645ef8984c3941b15f5ba4bfa089e2e338e08cf5f0308f83bcaaf1cf41473de6a9a06ab974444a679d5f1220199f368c06e46e6829310aa24005a3934590f610b986b56f7f49a09f3a393d7b288df86a31b03c3ccc6408e6bcb485d1cc55e75192c34e09e17fcfe9c85b42d6a5d4f1e3d248c2f98b483e14c856e71b1327c683ecded35a6f73abb7fb45ac31d720afd1b4efd05e68399454b897e52643fb3e7f85cc4e5d682e4fd499665406549606a9d6ce8b96c77d8bc3740913482a5f9b5d5e7b0f10b936f42c8bea9e8c8c9625f74f1eef86264f86c2742446905b7e70cc4f47e87edd93a8229c557b2f88f34eac487a8324799ac18418fab4e41caefdab9da015465bec9c5a1505e983c0bc68e58c6b1ee5bfa42d14dd39160ebe44b3c6b9a23a075df1e68fe9751ca2cbf31e86a4515009ca9528a2d29f03a11094d759127f1c36f36ec3c0cdbd98ef86d3deb280a019fea362ca5044bac17b2f987796beb5325d98b401eb0060ce9c31d2c40ab8af8882c923bc457dc4ed5505429b6ab2a3663e8803aaa365a764a2f156fc86c863fd2f6c340fd5384afc711b1479473a4e77a9892efc1cf3e0dafcb9fedfc77dfe01140016e345fa46cc4b8dd531045fb386d233913d69d81c78a602c2f04a46b923ac2dd0ee067b6cc59389fe2c392dc34208b3cc8c5ff45e97bde65a5d611c14861f03814d7540ff7be06944fd059c8a4ecb5bbce43e95e9f7b9f7532b070bb3f3e63ee7bca5d85620ea6db51724622d1ddbca2c7e6518dd2f6555e70b5acf60c09c85b776c409fe61995dcd4ffb48d79d2f109f5e1c97925d0efe21b5933c83848dd23a80d5dc727cab83bba4df0a223bef0193bb282aca8a7e3495f98d437d6ff5ce53381030a8bf41494304bc6b6e335eee21af7b319083577b0d5f9e038c5fc3ce4a1064a8107f3fbe2ec43a3ac1f1ff1130b4104456edfa448d866bdb69be4ae37ba6c6da19bb8fe977242a74ef65beaf926445af194e3cfcde27e9aaa67b11681d9c9583b38ce224e9f1e0372bcc0e4dba03b2dfbcdb2ce3ee0c030d116855eeb4bfd723d8fa350c88e13a9953d0659d7b1a1cd6fa429d5aa1416a2694584eafdcafc55b644af24ab41254574d851389626c8b232dca73600d609795b19e080143894061cb66c4e564d00c98d584ca75417a681291c4ec12bdd343c8d901e5dae425f70b5eec264174d8bc5c7f818ee0732cb07c62d4489a7517547b6f9c19f9b046147f24d16ab300c7c9f7075fd4b588460ed88423e86d2093f4d797917a32b445b1184f3ac0670cb4f64593bc91882cbdea929ef8cb82d355f721d2e1d418a47013bf3fd0e968e2fb74a5691fb1efb1194969b11e3e9f6d552b81947834bdd2c3921920752f3ab716ea93a5990e458d0f174e0efce611568bbc388ea9493d3825f523adc717f59a34334c90454f6f5152725cb31419a74030a5c6ae52c4588be453dc520937652f27e35e09c0dc3af8e2cdb61c0901dcede7b2a14066e99784eef7352396d8a303a201612addedab8d4ff1d21d258086fa5877122b20310aef408227b9aee6f7cea934b833841694cf9b4d8f41168c8a3132b7855bd8e120cdd871b70ea9ef486201962af3a1953925f3cbf59e2f0dae64769bdb3b07108ed07c6530d7525aa90289f735b05cf483ddee0dd888381284f5582d71cef78e97af7d511297611cd4a21093f7251e3bffe76fd7dcc0fee34f5404ba1853ec7234c8934356a1060e80d05ced4260300de2d5a912fc2ffbbe1ed1fd3807ac8109b4803616809306348d57d0761d948f7d9ec4ca98b3455f179649e074cf70a9235b0feaa032790d534bb56e59d38cfd6d78cdc08e6f6058522bd1eba2d5a6fa090f2dfe2475c0e6bb7d589d0254777a64452e6127cfb3b639586e6e20f7fbad5429aa6cf4af6ca49ae1e6cbf26f48542e89068041f186b4b6b49cdab02fe0eb75338798800c1eeea3500f5e60db41f43202649bba65b9a24c818237d65247f10bb2b4db51f627024a9c11ece04df4796a6258909eb625a5cabe01d303f5c71d05e7da1031cbd3050aecb1592ac5d242ed02ec02ce2a2b7de3a531eaf793e93056d93fe305946bfb567bab185d0b16855b5a1083fde2851baceb3ae0f1315ce33416674b058932882da4447b421bd2b3e676269ab09d3d0a64fec38b1e60f14f35f796fdb06ced8792d7634590b13fe325e9a72bdcf7ba59a4a258049cf7c9c70eff934fc44905948692108e78770dc2380a2eadf24966bb48f15a4b32cab2624861fbe7b605ff2cd3dd9b12c157623291615b6074483b6c9ef3d33b3b2f8fd1948b0ab205e85acc27a730564ac77e9cf5de653de8443c2825204af91cdaf0fe763b0546e6b6e36a16cda073ceabd9fa0d271bef34b5eac209230b1466b2e603608dab2c04d2d8b1b058ab0e126deb2e053e0dcc7c12d1ee536c1a07161edfc5644ccbb4a4200dc05c8a2cb2747227ce90f1d4d3e3acf5d86e33049eb2c12b215d19985f054b75294db26926eb893df47480307c4e2203d970c9a46b6820d8608be047ce056b75c728a523f9ea12ec0658ce3c49ef6df0e878668758df7e85c28861f52462a6cad23822b69d4caf829dc2cf73acbd706eaed7388c4f42da4525b8732c28230735212534278d2ae05a27caaa588eb1c1890b8e9af6b07c10c5960c405b24867e9e33ac48ac9d780e67331329e144b9d23e3758441a378c4d76e5f93b1ce495cbc7d0284831c5501e4222cfa86ca058d9e3c007a9d569ba87a4bf2919d66ba58a635caea9dacac1818f60caf9abccf730944ab44b411fe60d76463a9c6292a56a1e429ec7bba0ab733b1c961166b72d63e7ac08f68999718cade9c8cb4038e3b676f183d957d2d235abe267f326143cefd27277f4d2a56d190ce201d90d4b801d476ceacb738897b8c255af5718c525bff6b94fb247bb784b65bad230000304f4df9671d0c1a165a3e1b7050c33f5ae2108f956a63b1f90dab396750e349fb31263ef05fd62ab4b5e4c3d9b277077bb8486c7ea0768bfd413f104a1389c5760edef8108571a8596961956a36e7afbde28b7f150a6aa84abbab48986790bb292893a6a957676dc0d9a569f3e63db3e62e36c6f94db3d514264af73bd01582156be4289016c3135b93d3f2c72ea6cf757897a3c5b44eb5722c5635117c4627960feed592bed4348bf41027118749aacd08cea361e947662ee7675d2cb98c5b450ca216bbf57ac9b64973b5897033594500c88603729aecd2c613aeaca61dfceb49d77b84ec922d1edab523d66feb4374f97a6bcc826685dcb959cebbe1e5953e4ec1b161e2ce082f59aa37e13b1147ae196ff8f161448e6f6f977b4f5ef34873216d0863fb199dfa31fae5a211410f7d16dd07d9e66e4c3e36cc612ad86c3a5a40acda4bb080439aece9fc737a81085dc299b0007dd231133b09a533baa4483433e3370a73b1fc5c6c3d12fa95c280dd7b1f38f0bc7558ade23d84c7318c4f260578a81b4ed6a985d72c06161610449209682a7e98b53433e15347bd599cb9101575b535d4a2c1654c8bd2c1c1283fd59f7ca2282b61fbece71a941e213ca123668723b4d7ac033eb48c1c0a462f7b9e562d0d214d7a7f42a7a4581eeb82530c125bb837c043aeb5a1905130072bb8e3580dab3a2dc2f7a8bed0f2cd59c598a01e0534570e20a03ed8bc6c1a0ef71b69265c62dbed428be963688b53b229a48f6ef1d0496abf444722994e9af0392d7ba1b9f4af5c7a41669fc1f42ac36d05f7739e7bfb4f5298785ff43e983a1ca46cbff19a77c4166ba78c9fae2fbc415d11723fa505725741e50e736d711b324ba114ca05e8b7f9427071446c968d20cda8035097c48b70e1463673b0d150cfba4295e9c7b1fa2518d11be8e1de0d7968f35feb91fa55e103b1fcd0bdad96e1761627a351493a6752b71cc1652859c9cd695379a8034a3359e392ed3dc48c98ded571fc6479de8611badd48bceefa4545833784c1e3c56efec4e4a4ca812662320c5e50859b02a2e6ac5124145c123f6f6104a7b2dc12007926078dbbfdc49eab10ae6f99006cf93923afee50cb1cfc1c34678ec3a6552ca598903145cb4b8ea4dd9633062b32c4f529f034f6b45812c5321977c236aef47fbcba1e4550c5c9b320a4360b03b59881aafde1e8d4ade66da04844d1ef0a4846931582f34c86ed1621ce78da8bc6232c5b865c0a5d9b5ab7ce5487f38e09870f4d6b7460000e3d4a549f8b211f9c3c12ed1b28c5a5e811aa3f306a6868dfbafc5bb428d0e4ac895274fffe7be7aa0141a005d7d95e807ff100307257b463cc881f3d3a8f5c179ebfeb96644a3a5903e3f4618d383a06ec82514eda633a337f3d3fbc4217ae26832a25b030ab0af3568a41df24434c115e0cbc3f60dabf6c2ebdf4d85aa6209615a86d37b46de43a785182b93bcb52c64032f44ef5eb42a68d5eca09ad0052abe8b76f04f4ec554e870ce4488122c88061de7153826d776719ec64f7de1690a816e267407747a28c7ad85c72f35d4a0a22227513880c1510e3298efde970e93122fd57b087ceefbf15bd8c63e06c8a85395dc44e5080ff58560812e540231ea77b8b845a62da8ec0d84b4fd9245d780ff24bbe4279bff15ed66c89f588f3d8f2c7251c51a258ea5b0ed551fd26ebc749893cdc4010741794e4b43e75ea971db1ae3a559a3fc28aa3d35ede9b3ed3ff55d6882748dd70ae7f14f21ca7e7a45860fa5620e61c31b85c2082fdee49b2be27d3b7ac45204b541686d8331d6f1b493bc5d19df74d492fa7a8eb91bbc21d4b77550df25e76bcae725855e36c68db485cb40b9d315a4c7b6fb4e5906ed4159a7ad7dc891b129b7304fc378abb46bb3b8e8559a7cdf79d48701f54b201b2073ca1501a6d265c48db3e5495b81a715d9013599c2e258510560b03cb7baa5a246fead0f7093816dcdccfabd98b592683b46ac56224458dc7b46c8d05967db09e090cdd383d13133f12e191bab0b9f5bb7314177655d2f60690d57da7a923c6558ffd85f61c6989c2b75978acd1fc2be6257bcec29ce2949c479426334fa5bc260c62816ed616b43f6045616a969a317ad6b43154f7371ba9818142691320fb1e952b9f575936971fc479cc91e0ee65b6bfdedcf80ccc49ae0cf8d4f08884e56738b4ee13a2000d696d991c24fcfd09b8d90b311df2ab01fe033c7e4905a8abde33488af6894b79eabbd39195f7739763e24a40170ed8f4d6ecfa5bb561fbc1f116a5f117cb16a04a0499aff92e5a69729a15b3a195a877d665fdd7de97c0c2ac7b0836459a55dd2304aafe32da61ecc132069e37de742c3e8a2741b98084c271f64436125f4ee8092dc0e705f1b3774609c48af79c37f7e21596f0fd5085186656f812d8d6534fc0c302f3ac23afa1ff8a1d38739d6b8bdd2be7b307de0c63d8b1c577911d2e2f247e5b14bf46448ada402ffb245ba2f7afce9464fd2ae8edab13f3b4dd5c0e1a3ed974f974d57c3a14bee9a9910c79792bfa17ab84d168d411d32565da0e465223e8a8fd51593659d1a6019877d6e0508ddb5f386d0e97208f4dae49b36640595dc496d1bf542b61137644a70cf7c7677506bef3b21512505cdb9f19469112a7bd966a198f4ef2e41be9b7886658466feb229e5d4ecf2c1b19edf552ff1b204b23b76567414870287280164ced30fd8c9945ce430126ee8caf3361e239073499111b36600155a37df6acefe0e8a2858dca2306f1052db3381e918091486a03b6b58603763c547e69f4b4f699057c00fcd9445fc17d890cb7d82b540c8677601f51df309c3c86792f8989eee0669e1d8d340a7bcc5793c3ff90e46014f41a4c82b4b26596c5e1bd0823238ffd9cfc59f99e7cdfa218d6c35ea444f374b1a15c6e005c13e4b4c719390ebe3e9f222193fe923fb1e37f0f9aaca3d54cbe08df676ff46c8060b9b06e98472d37803479ca20516ac7b84348d6fddcfe9ac7fc75713bc6c1e6867b6376ece5087e395778cd3d206103f8ef9361c6c768af35e2f82ef119452020b92a0ca0a2a2c69e60830329423bd8455d5dfb69b02da38e112a1545c7e8b9efe49920b4f9177c80e565e069cee986c052e82d45a3800bcc8b2a62511c46390335351faab2e40dc7d428e57e03bd6d5d3be26c1024abd5fbec67452ba8826d947cf148584a5cd9f6c748c2640b7d929c9d99f5f35c24e2364482443dd7155772a138c9ca82cfc7c989bf231054d31b7f0c98a3ce0a6e0a38ecfe6ff27fdcf4e709dc1636aa1d39a813e5a2412e0f6dac0ad243290e5fe6fd833c6711543b825e14772a71250f0c181abe1022fe426a950c464bf517b3e54118585622df227bc6b3477cf50f6fdbd5ef3f0213c9004058e2af6124d12779e57ce45bd716f0110947caa803fc8b5c921e13bea4fcf10656844716f6441755589c085e4884b530baa3bcd1566e8678324ef49f9819175882e0856f7e69761697f0480d7b2404be991cd98a6a7153b856343ee7e826843b4c5d77adc3644d8df5b2081167b587304f3c540cb6220904596c85b8f2354199bba2ff7b479038f26871c17379a0403382083b588ed681bd030f7d24d3c6da8b5bc73c805a86d3a3433e8a1f647db8e1fd8fa107ca9b457ba3608c8832180ed02156ba165be12d320751af0cf39ebd702b29cf66c0020dcab1cd63ccd33407480d3bbe10ba3e74933f37d000f3e6b44f7902ecafe669ded1fd2bf2d18a6eb178b372213a017e4ef3c206dd5458e1b4dbda505d9ff14cc8dfb6d507b799cb36effc476b01d1949032c941701126ea4b371015563241c44c22034af59f47b8b078dc700dbc68b1fc43d814bf6ee2885af9d47585a54745d94ad996a65d2c6c32f6664f28ae2fe9e1a6b482632792e0785afd1783cef5fc9d4e5f0046812c8a8d107197fdd2c63e94a22865ff126432cc4aef35fec3c4f5bba8152533cabd656410eef9a80b619db9944a247d4653172b96780ca758d000081e5cba7ad8d8df4ca51b7d25fb4b3f9ab1ba64f763a812f0b502d7cc36697a1268b3aeb4169ec211dfc533b3114413f8fc28bb505cc17e02e3371f62123c4779fabe19a729098a9305e0a826f72f0f51e33dc65e733dda984b7cb080c594f55badf40bff4b7faba06418f800a1513b9b053adef42b5e91855cdfbf9f4e0579b7e57b0f5143e51a200e2b450d368856673bc81ad46ee94fb259ec3f8900da531d9947869aa7baa35b06cfdf344a0d9322164f6a0933f59f4bb0cca5f4c1b20b06d1b1c1121126151ca952b12d788907aef294b3c628fc8fb49c27fca2e9d9ed0870340075d937a6c4e0afe6b34e406b751128eced08a898251178f393f0081efe18fc449414382642ed2e5cda28ed8e233a2b663c569a60c13e0e14c838cf7cf246f1187c9fc6533e85d39afbf68637567eda9f75401f4cae689b5633bc6f989664e5bf8562589f27b6acd0175b39e329b3edb2881cccbcf9ff3132bfe22f2478134850f59d62b9ab25e0c22975740e7c9aa30c43509c139472b888da129de725030d03be2983b0f672cecd354bc134ec38e230873c4cd910dcd6ec94065e5d1f70bf36614fbde4b325f35664980866beed0d3ad640148a39308a03cf77778f8b7a55714d2926070945066e9a84d159487b1d1b8033ba4cee1414d5a74f0cc052fb9b8c6249f72e90ada6f0c69544cc9920fa41ab02544b9700f6eb139a3ad9bceccdffde5ee642c4f1d1985b4b95bfc5c02b41ff7349c772eb82f282880d1586afff8467abdf1ccc4702196414daf9aaf9523a56677cc3851f9a586cfd41dade8171cb4248fbd63d3ed1d7f8b4d111c831840c6863ff5ae2cba2f7fe86b89177f9971dad125fc7483b3f3a4084594dda8ad3db4140d517bc2f0dabf165205bc5c16e87b558bac1d459e8e360d7c249d300d7294b0a4b54d942656ea79f91627ca31fb22d7821bbe4ba1ea08af42e7d74c96148782b09d3717e15b24226494a024102a84dc66a2ca908a793670f0449097556e777523f2be99506388a191d9c88759381e8c02b37082b9acd468cb0440bb8e9c32d18143ae5ee8a64ac76dd8a210163c806bb0785482453e1449aab241818018036695eb1b471219a79e3c0e1cb3806624aefeb4adf3c69f9a23be58bb0675ad458150dad4ae40d1b00ad1ac75fd7da6c5ef014e2c4af7580bc5fc8f7ef9b6c7c532735ce065a8dbc0602e9693068e8228ae3bb13c68d5dca8d118ac27a36230ede55b4dff24bf34278a664f2dfc759dfd24d0936976c4cb0065123ea30f89856813672e1355f4964931e2e756c681d257c2ed31a31f45fa8781a942a632e787be34e88127fccf26bc133577d465b02e8f04c0680fd52285b4194eb4a362e5442621439f1a4dad8d26f5f91360fcd5c80d52182ee03270e459dfd9db4403cfeed5017e32f138028cca760f7469970872136fbb6de277f66458d67b2530087461b27554971e68b6742dc62af0731411ba61632a4bd336792166918dd1b754b934cb1531984d358b70571668e22a2dc5c82f76f342589d975d2151304c7c3d716f166461c34382aaf8ea9a67d3e4c26ea6e1595c15d989d957bc86cf85ab765778a8e1522dc65f40b2ea8b07d0e611d28faf78f385171e09df59e2d1c7fe2d6e89d83550a704cef07e4de09fe583f96925df9da5f6789c9ebb5ef5baf1ed4b45a2a374c77c6a9ffa055fc2db5794e43bc0a6ded6b67935d1f95fb50293efcd25d963b06da3dcdf9cd9b4e946fe849d2d9ca70a2e2a575118fa616cbd2b046341305c7547aca75f82e199bd628d1e2107dbc3727075988509e87253ca8b0dd8194fc4a54c313ff3c0f705e5688b4911df4a4c9ca465f8c1652481c0b0e5ecbe1a9d0824f664d24990bd2564504c84311039e94cc0a57576db63be94e5cc90b27e68a452be13a59483dc3024099ff1cff44dcedf0c56301b709d933933209855f11d4fc0d11f3b9d763359230ae3f6e6d2ac5d7849231cfa7f77d0925d3e24d73c3c5d9683bab583bdff08d30ef44a6249064ed9535abfbd9dd8c9526caeb9ffc404d105b3245eb89f6b9ca71e0985b2152b4582e8bb829e9f3dd642464f7ffac7c20e347f0a2382e856c6ac0e196180949658d505bce0fcdfbe9b27fe9d06111b5cd476150e5461d10e7ecb7043bc81d32e0c63c7b255070abc7bffae699c494c97d78430b8d9c10ddb9d60c8aa9d352c465bd2121a9cb50960d120f91d18aad306bfd336d7dbb7c12dbd1c5215e25f5846d858ddbe2a6bf525bbae1beff39d1cb15fd430ca4136b50c2cc76cb1cd50959fe2c25287ebd471720e6b367a38048b5aca12d5d0a5e1f5606ecb99864cf01b4a3d2706e770b6870a6110fa6255c664c2ebf072f588daf62ecd2117aea2be82c730b936bf79ef46d5d6d0d1e80611d5d184e31c329c4cd607c9869d597f5ea5e9ba550b98dd4f7ef9e838ee343dc8b10c07f60f938db45010dd2e4c62b857ec73f29eb13e6cc4ca42141ed0906ada0e0b0321f477477d0412933c7e75160de87455f77119b38899ca7afc65e093f6b2b88d909f497b1f51e85259214abf1c43dbaae985cb00f3fd206974fa9159fab43f5b77226382c5b67bd05aa18cfb8264a79e94b20db7e7f46a890910eabde838767792dff79103a5d363a506bcdec7c06d086c43636a5a155296da11f16f94b337a73639bd688c2b638c35b6afd5b751f3574bd00d27563f160cfd61ed5c1fcf718242463c49a9333a71d20bdea4e336f31eda8051249da860a14a0d9d68d93b22c8e4114d70fce85633b20728c9b2befe0f32c0cd98f74227e9966fbda60da3c62046ee1a824c7b63104511b85ce000d4a7a29343309fd99a361be6829efa15f5a642a5d52e82de3a778d838ef37458e433c15bc4ca3101d22a4a23cccbfba06f4a7681e0ed313a3aa185f0e32f03c5f1cd4787655e2d7d1fccd0f3177d902c694961d24e7470e0081b0cb15cdfeb517d79f39e71aa0787fdef6c9c8f04b29702d760a7580fc2b4dd689a978fa1a8a7daf7a0912c321324b301a1528e3698fde1bb36503369745641f0cd48c7e57aa07b4e43c69967ecf2e5bdd3e12f66e7d1f4c7c6da9e405ca27e2b9e0a516cc621b11c5a8bcc91940028a9e8c47bd788ce88251283b448e098d19c408ea95f41d624e0db17165e01f5475c3859e1f34119770bb6c193960ffba7c1afb98812f176a4fe6fca2b763be18c5729081fcfe3dd7e2aa45ded3cb9a10d8b1f79f5cbe7d978074c1e93878b3be355db5d2bc7c3ba02fea5fb7ca294c249d6cf37d073ad85f004751d0d82f7306cb072ff061f84d8f87e7069217c6e5761701e498f71631d366de905e73a7d25584a6fab8e67aeb20eab82a98fb2d50dbb823a61bbd8946dedc88d67c9f4808708bf5f3e1c7e7a92e88481fd307c42c0fe9f3d9dbd586f0aa13a66fe0b949e6d3a6faf2fb05dbc088e32a34f4ddacba0f10e931460826409961eb3e5579764d69abb52cc1b63829a4152c23d4cc8dc1ecd4b712a930e69386166aa63692c8d7bdc1607fb2837844dcc18148afcc70d65dbf29e9ce79556a04b4bcd694ed290494502ef5bb1c2298590bbf1be19c7334ca4498a9779a5659f7b31819e1bc29946dd1420580e2462c22c4f542b1feee02aeb4b1cc5a4b0c6a3beae10765f006c5d481a69601aad2b34377b1256b25e5c74ea273ce326e96cd1317e511d5eeef23cee979a17fc0ab532e00ba688a83ff4be12114a54bf11a9ebe10d3ab76553ae4aeca20351ddc25716b3ffe0e02e39c61e16776fce8b593731107df7b370f5155bb9a2a4c445ea5238c2e5e640cf7a915d314ec59271dc80d61f6d20bc2fda0907d7972ff771435b19f090a155456974e545b99bd5d4776da03953df743f80f40f493f1652a47e584d0ecd16f821a996c202e0fba591b52e4bf168f8282f579ad0914eaa3581f4ca95256ce2937aff148ee4fad681a8ee2e366b39016c0c91cbc5528b4d6a0078f7cd1c1e238655d97cf4c9dd73df4825565525604d95a83f61067db9068544e230011f7921290f0d54f70a477d7df561ecdb12a6cc2ac7d35aeb767b7cdaf0373406494f5574a2d1896a2b717caf694275beb23005b8a267b2a3206664b3487aa6bb870761cd075a7715cc8511c091afaa2e3b54ef308c6871a50e38a7855a2eaa2582669326edfaff1c7dc65d9b81e347e64ea1dc4b62f33b01df86cbbeafc20bcdc8744636c49599464d71642c1207acb14ed5a887284a2a6360c839d0fbb800c89d74955036f7562c0dc5be08bfca3d462a05e7109ded5f99222da32d4168cb9ff53e80e440e89069d2eb5168c6a1bc1c6a15a5f7a434102292b47c194f8fb62f2188d6786c46bd8e11f72fbcf60cf10c45795ca7a89230a7117c8f8069dbd1eebe7211f1aa089714d773cda08ab43477567b4c86d3f37c0752ff67e8a75d7187fe8a74e3906dddd7e97422266f7a6aeae3ad1ea5f368a84bf999f34f1213d305784ef3347535570eb166cc90647b309d6abc7cf8611a72cd64dd2ac123efb0cb4257541494b32690dc23ba4eaa9069cbacc6416bd971e4c9b19f4996542004b4f2413100e40bfe761247f7ac5c5674e137f72676504c04398154a2b2b860cf8e3ce0b6c9b4df6e7537c854157bd4c6a534014f418d9d66be7b2236c643d4a23a2d4b738f3ee5f5bfacedb8996137a8476bf8113047ed4b893839b1479a207ae660fa222cb3f866109e6294b7665517fd15cad4080a7cdef37feb0c1860615c91f45ce558514f708f40d7ba952c33e2d9ea8ab90dac2d43479f6a491cdc18e0c154d425fa145b777f4d1d6a2ee3bf200bd1bd4c4053fee72e751eff03a4660be058bdf63fef2c8f4f4c451b3a89f1b4ba50aaa519240db9edc2e9d516b6e3288c4916a0534b4645b043aa066d0aaf3b10de2ec88333c32bcee37932b7809f3bdf8d6d126578e03e3b3ae6dd3a351c44c0378cb21f8996c636c10e54c14b78079d8adb306a599ddd698b04c82bf01d3d592c55fcdd1f70aec28db9ed8eab599f4626dc32d98a82226a0fb44476af577a1ab225dfdafc22bf9907d5f601ef6f5771c077d5f0e6c5826a726f7fa60719e4ed94a08f31bdf2269ef5367885d94e6c2ce4856298358e8ed551359c2fa0c05e716cf3018afc51e87b4349dbee953e5e809f69a14d1a089f371a1285d46d15e377a32beb355fc56db67c3d6a4c744743b71c8c92363f4b0508f077bec7945a56399fd4ec808b715c77dd1253ddde3728b6c4d4f56eaf278783e70a6246937050e4dfdf3c92d9f2a3cd421e55efc782e73a45652112bfa105f0c9a3cf67c5ecaae2b2ba1c9346e2b1064a2ac782d282ae89e545f6c727433d6440f657e74e76bd409d78db97410a3503315767c5c685e1275b47a35733d6bd0faf0dce44c87826c262d74ede394c461569e77814e2b95aefa18533a53c809e9b3f0d1e009f73ac3cfab256d518404c220d2586b19e2a9ba173fed0e43cd64252031df1befb896d2880a352ff09d8e79d36544eaf8bdc7b39a5c68021843f2b53664a25f209fa8af7896da82c398ba2faa54b96abdfae42958138a53da118f92265355c800fc9aeabc1ded07bb5ce440b8452297a13d583e16571c32db8b6386300437ffd60ada9d0c79dd4bd55d4bf20fc9531346cdd16fd453a7501771a4cfbd93694f2e7e83ad3b80d5de1f8be213b3a92f8669f76fb8d740ba4389c41931f8f5c3ae4e9928924790740233de8d12cae8cbfc68460133566da6102eb10354d9a598995b4175d88c912d2dfd6da9bc34132fb06c0a29b20836794bc874ff2b615d880bb0a259f34b141bc372e230e3e8fc807a7809edec94d85734c65a15ea1c15d9fe2bad21efe5ca3f36fd61c2eb96c1f495365d283775e3783397c6a806e3258e02f8b496864168ee066fb8f7d5cb2dcad93afc657d94a80863ccce8c7a08d5ffe7e496349bb874b836b44f55db5aace94e7e9307103b3e8004076ea036d5a22893a865fdb7c0b53709bdc48baf09a8aeb8a6ca4d7de468853505f9904c5dfdcb6a6eb46fe4de5c6753e1b65dd2310bbbce3be11dc339274b58ba17df237319ef1516547a32c0d01561b4297fab67640f29e49943cfe17c942a9a567838b83c939f43dae049e76995442aec8d99cbe2f50bcefd76c06661f00235daa3e8b85a924708ed8b5a22bf7f526da28dc90d89c1ae4957da588e51d26d816f691b8c3a6e7aba0c417b15e3c5ccd482679bafaef838de3d3ca5187e2d1514cdca5140903137c85f6b5ace28396c676ea5c246ef8402abef5d57333b6b42653085a29f9d91826ef0403af69f96c056a99177bb3fef335e9f423f3c04323f700b7bfff3191cabf8531287d99367bba777753a59c4b3d4bd579b113f213618f09eea2dcb63742d6293daf7b316b70ff4ae782cd09cfb9179cc00fca0dc5f0663f606816865a0c0e3b75ef047d80981c73572e3f1b8b42930854f663fc54236573ef11f579681c73175b9613b3574d9e8a0d89a137da0a55d346349e91bf545ee690b9d8ea8910c131302d6712426bcf01eb79018e0ef601374f53f35270326615ad0a728ffb809f58a4275fddeb13a7e928a8ba580f1e4a63484acfa39f872a4df3523789b14b23952bd25ffd7b86ee8e6ad35fa244eb4d4ec177149bd286806107442278f9d537d8888e5192a5f53c425ee706c907df9f0795cd2fe876415d2a55a91b8ca99c38935e213b4390e91f8183414c29c1b3f421364d6d9fd0c0b354370349e09b984712b57a1aa9be39617adc5241c82f1a1ad5da7ea0154f6138819ff9bcd4c7f32fddcabf7de3d2e3a99b935662c2b5305efef5e206b8df5119ae3b0aab6c1e24129ce50174265a3d664a4901dce4889ceaa422e0f7ed66d11e9260d7196d042e77e80d7de96c628e9b269adaad0ff0a53c29931f26fe9a2025058c47bd510780fb9a0e616c4b25bac0a2a4abbc59193e2f3764ebc7cc6d96c9c7b75d663405a0c4b1da9e886ca3a7a2da2ba02097cc8fb1d6172046a2c00378f3708bae1e389f22bd941e3e06c66c720720535934376716e6e56d5e588880a63001edaae9809d821461c86cd35048acac40bf12ed972146293dc23003a1c388dd86903f3f81529f06a07ed8e14dbdd2e5aedf0fdbedbf1006c4e6d0b87bd2c02a5fe058083ca3891196e783cf9859aa781033f527658dc3c100ef936942c56cd1ce61551cad7d7af8cc258b5789df230ce8063f92ab79d23317e2769ffd54e21a0f895897b3b81bed81cd80fcab88413273f33561d7eaace1a209c1e8c2a5ac3641dd00b77be56c3ff5c9d0b1d20896de18208c343a50e10e9ed5e26275f6818527f5dc85ad785c460ae3be77da3a012b6abef05c3160b15a99eded4daf70c42e02c35b1277a7342a076e952bed8d41f9d355e0b6ee0ae25ebb8aee027bb42d16017342928999dc39a2575f74c1f8714ce2976c3f78acd6f0f5c5980690b519e0bfd9f12b8443af85ff428438ef31988c6b87c8d063fd91021e7b1d5515d53c84bd9664afb3886422b1e90fb5a4b872067ca091da609f09ee1fcebc38b34664d9b14b8995800da4728f567915d0e348ba705a107168fc1d391dbe1b7789017b24842dbe82c390a52a7cb46ac85e78f7d4add58ccd4314216330aa2e02da943e173206da4d21bed1e0cc3a11edbb69a944399144c83058be0bab0cbca064d132d0613eb39055c6a2d444e32b1c716c0d96c0847c44d9207114b2c2cf47183081bcc94d3fb702356bbc7280ade3a6bcc34e858c1905af444673f52384eae4b366b0f4f2fdcd0a9c773823f1f3595c5ca79bd31135a1cb725f712742bff97bd651737cc4ebd8fefc4f794d5c0465abe22eefdf9070a52b9bba8ff0dad6608e34f92ceb1d7f905c5c4380f211cba096ad201770b72819db0d9abdc97e96419184c6fd9da8bc76ffb10957a4270c994aa647f07bcb0c6ffce59e929c3bfe929dd58c3547eb583da9b87053f99ba650796572902af9f7cef71c9ed0758bfda825f3ed6779697c11ffaea046ea6e1ee260b576c2df076c7b2e636c0472d2e7d3c6437816ea121111ad34add82e6a9985e65088e58ceb4d17051ab62f1cfbce077940eef49e32d5eaf4cf8315fe714f4ac3d6a2fb9aa0fb7780383984665d37b2bfa58675b21ddf2225b0bae10a447b23d312a28f60463142cfc82fe9bc9377ebc2037d825fa3ec1acbc3739f62eb418046909eed48fcd3beb3da5989fd0788ef39c60c9e0f78a9f855912bc0ebdf6a3992c8449ed8fdd175fec333d41db863d0862435871001c1d1646f3ab6d3806d540e7465575be2cda9fe159668e30c746e42d5198f9422f7fff2756be08f421ef3bec7444bc0d5b6732baa3b49eca10bb48a402654c1f498ef4afede6b92ec0a73d5aab9a656854673f224206f2086b14236f103d0c659419708898ea2a3efe45b8063fe831c244563b9c18f622d8e92c28275eb0a63797ff4cdcbf2d55a20219e2e56bd12ed2c79aee24f4c867aead08b4086be5b5930b94dfa9d1aae74ef915d668a12241ab0f85962b6932878a91d8958d4cc9edf76e64a27546cb4b426cf1c4c0b176335c88e637d1de1431fdffbc287c6cd816ab0fca321ab4f34990cbb8f931123857b1783aac4d02c2ff958989967526c807a20a7bca7bddfa66984cbea1f872b25f1f8c01db518658672c6cc9f31101fee9037438454e71d9b06d3ec46a4129bfb583d272bfbe6c898b9b0565b35b4386f741512b8aa84905f7e4a58020aa84f4e412f2fc681d926a66ab89562fda4f627efca73e42d9396c70de3da37a32e9886f5fa15f7ab78614d8a0431c16d240180049d56e0a67de83a3d738f0b44aa6b587076e58876406ae6cb288ccb7ce3f1120ad3dfb14fc8359677c35be37623b569aa4f325d54cb664f60bcc51dbf81d29f455ef28e1a71c70abbda6f66b7d030609dc0fb463f812d070112b1ff5a848bdb9bf387a617536b35c7721258a5c5e98b36c3dbf823c68861fc0fe4426b94f18eb7e93e481278da857f81543f6248c778803d14db93a6b7734d068895c12186a6b31da496998e4ce666700a4b470905dbca3dff75ae6d0ed28a1934d7a090d5093f5db5cf725c12c322b54dbee672284f6ea0215b0f2936a28d9b1d89552641e087bae04f7f3a8ec9b81aab445b24553183ede8826f30776ad46dcdc0fe4383046c3786cbb9904c5454c960e1f47254d85e02bb952fe01ab742f52ae49636dfbf7de5bb35174d09af135de8f0683bf948ad3a71648dbd3bb1ffe6ff6ee7c5ec1db3ff8d2db72bb7885dc50dff5af31d476bb59fb6c20856102029608639c53c9a7bda9e554687840a79337386d5e86dac6ea180c1941ffb1fd3c73b5dcfb4b77528b0252ee7182a52bf814da483683e874279ab3d4efa374d3c01540efe0254cc59f28989d5ed9f331034a61e024717312d95285057e0aa6397a1e7e2cc34ff017c82939ea2f8ef75c7384906e1f974b77d05e180cbb6436b5cc14098aec3a58e1348f9b35946a099eb25926b08ba5f43a3d1fe70df640dca8e954a96c095e8c4ea892e1ec139b297cb0a22d27ea045ecfdd3a201a3f927654125c40c5351e0ee5cdec74258fe55715b999b34312429e737945a43c4e2ef25b3045b5b1523bb97cf43470d4bf1f8c0811d01ee7577de53636b3475e184b0fe1575764b8422daccd3a8f8da4b1e7df52e6a2bfb12cd804bdbdcb458137c492243eca03a9b01a16c5353de4b4f2f08edced8d2e7ee3b7051e505928632b6c87ec8133f5e18f37561e8c8ab9d5f86d9cf091c44169088bb3a7885ddaeb4349411993597cd1fc43b75fab10ee7194de43cbc22df586f181e29aaa2677faa60a4ce4e9a44c82c86065c867f6b9a35492fff2f067059ad6c5979d40c386e737737b5601827854ac519b33d11e7cd6958631a25fd5cc3eb956a134d35566dfa14d5ab056c9ada8e820de7d6af24d387b9604c2e8c2438b0202acc35d6cfc7f0472eb0163021fdaa92377ff18deddfa6cf740712f19b003961d475af304da964386bdaec8a36de91421d72590bcba62f61d7ba389ad2028330588d6ad0a36f4135c3028dab67f2bfa6b01378949807d7a13415e344973904b3ec4dd6eb8d456951841eb57d174a1932d0ba1728ea517eae9d6f58484216094814c3237ef5dad1e4af207488061c00338e88912f9194be664d2b70def06c3a969631c3ae7a7843576e2c77abcdd96512e41a59caea5264379e311344b16d7002e3a6b3e418e83938601a79fff77213057135382ff86abeed504686935eb6ddc38f7c6ca736e715d9dbc53fb66e796e161cd22f1a539e05f0d0bd1307547a9cb7687b4ae744670a820d86c5963b0c7b91bc4fb83569c2a2408609a50a6a5f750b68f25d5d7913cba710533aefc9cb9eb62f8c8b18efa3791e45ac6a06df0c4cdc136666f3360643e6cb3239bcfd2fe21439492ca767968a15bb130f994738424ba73c22c375a7eeef5bd447d6e7c72855ff56ae6a7db214c5d702e3f87e3649cd918dc6b868ebc3c0a6b091c1938bd4048c2f9689de93b9b2d930acfe9b9b1f0b3bfe4ba914c40520de7837df1eef0a908c0b9643fe08e931a99b93055725088a3863ba1411ab2c8e6a157c4d7e4e59b6a6e92e15e24a000ebd209c873ec13c8a8daa1154fdeb6f96127b995fd0de10d561e6eb0564c21dbe876f93b35e581421d1b91f2cae553f8279bc00b4672668125f12485cd0997288948efc419afb30e1ec03b2816ec4d50cdb46bd9e5d7846e455221e3a3a1c69c8506d355b51d22a339c45be3057addcc9e8c76abe43425e26aaa37e8301aea401a6ab675e3c859a92480341093cd061fb20b078b4470a2224a25800db2a82f8e7702ac97a13d78aa82339be5b4db60f89a47354b7b4c62304894487c043408f551b5308ba2d41b4c2c9009d3ecd6d03cbd322e0a39c25d56363c104adefadc25e3c7dc5b4932be6bec3679ec8e21d6e255e1962b4afb0d7b5b0a02380af41d244a43b73111836c2f3b1e61916d80f57a6c892861c673fa6558c481a9cdbcfe5c177fa6932cb343765abc3781ff51cd3eb6abaaba2bb2454f2c798c797b31302c95f2dcdbf811bbcc94a58181b795990c4904c56679a0a80d5cd25878243f0dffd39dc89f6abc45f9e9035dd96622c466d06a910d91b6a21021a659c0d0777cc96a1c68dd9cb637ab41908af13212b80ded21b9ddab41d62b35750091a4a12285c35c503f29c2028786bdc6262b0ab717860432e0c14cc2a5395caeca0e72f124e10685155514456b26740ac9215315d078d765349955ce0b00c179a6c4a48ce079edf48a9a4d7a17a8a19111946c31bb364addf83c4f73f1121e93be2efbbc66a82124d16fbce58d61c85d02e0412d234bf9509d68009c2230b81ae67f5b764e9101add6f15f396bbb8ed3ba7745f73e17375dd66f0d998013d446d5a5b1f07f66eaad295893e741d9c629887289aa51025074620aef921130e650b10a26d38aca1d0e3bb71de2a4644f08ec4ecb97a13c643aa21f98056b3ef7ad8c28e0af04a683c013bbf78ce7b6cb6963e16e260cb9bfc5bcf76cd7bb0ce5578c52a519887d5030d32ed50ab3994cc7f5fc3304827b83e436e3cd27aa4767e0a7967809922504f5f00b230dd070b134b08aee302155a5fa4b30d2217e2ab8b3244834cf72e14722cf12c9e6e1476ba5b96d388dff02c38962230ad9f26b085352522a77e71f28a6510a5d1b4733a06b9cd5ae2f964221943e1b61b88d43267299b5613a3fc77d25a72b85dab702b15479b5f3491aa24a13ab5ddabab76ce5a58ca302a02557751d70828cf9968ff74951d1492650bf780011919d15145d7ca66415de3c7ab5766b7eaa5eafc81858a55cd8800728485cb27a96097462466982e467a0254560cd0ffce3c7c982577b8b7e74fc9d160e89c06933214c8e28b9c5eccc3e4a1b1867bc67f7d9a7c420fe77bb1383d2d20e3299d7c27703f749b41740033ce2049ecde8a0180f5611ad97509e35a0fc873a45b48e7a7b54092a4f2c38ea8a3f4c0637b40327af4902a9e870ec6565e5c73991c7fd862f6a4c1ec13de9161307fb6c7e8f976384fdc65a0aa2d83472dc6f9b3cdfbf44e6585d3ef6ea6190ce0aba3f186a72b7c949af504b7af9ac2dd417901276d6f7f18c0c6bace3a47430ff5345b5fd1088810c93430d3b0d3a0aef443cdd23695aeef44b286bbd47841a2fda41375d21f993ebbb68fbace93974001a145cce2f03004ea054fdd9ab8fb64324fde3b92cce6c7a3dadb491562098c120c92ad145664bd3e807d88bd9d268f611628c26880574d1b94b0095fe3d4231fcea420d923c4ce32853a976522d191f691912e28f2758ed75e8673e59860495aff7478d6006e6f1075c03a49d67b19dcbd6b7729541f33b7973d793bcbf6f77aaa2609197c07f5f424653cb01195694588eabc75fb26cce349968fcc8785c5a5153d28d5973c639e96c9b8b0791df95035383e2a68b7fb286fb8bc7a2c44ca805c8ee3738c75daefd57135de1193866e42cc73aa63ee8af732a7ac42a8dde9117c49d8693a1ebe4816069b54f415e9a85dcf5c6e25f68a99397152d2fbbdca0517758ca05f014eb306f396ca5373ac03256ab5a02dc556f32a4df553c0b8d8a79fc493f1160000606109306558ff01f9279890f1fbf1e052e7af0c3d7ac5d81da0e03621fd454574b52bc59606aa6470787c119cfcbccf1994d6cb055b366629be4f0e4a48ef86ef1626d6f8433aa711d82d89c01ed6b2aa2644ac1e81154c0dde5df6e48155d586d52f2ea5a3316628678b91971dae676da2992e71d3cb0aef16aad3867b8513a9f6e3a5598de0bf8ec1cca16edeaedaddd84b82a97dd4519862fe27ebf738655725a1c51be9280316ebb95f6091a5e30c87214e505a1b9f940752ccf0065e06c270aa90964ca50283c4d2e570d7a4825120e83796dbca340ba671207759bc4c3b66fa304aad74e3db1d7ebc8fe08cfac356c2c6508490a459f4b43ab1f6daec9f25529b525801830f99cd866c4a87d9f98202c1de7c771a77fdf3da4fd10004d2869b8ec0e8b3567e9997d3e95884d7f21d19545e4ea4ebd12e56335c45f1658363257b0d47f07e78c81e6d5cfe09c0da500b5166931f8d72a61cbcf9db85ad56d7118c50db7a3254ad216dd0fea5fc8b90fe194c7a0be97a57bcd0c042ac37a9421cafa8ab150c8878833f1411a8a5956f02a8fee06cf0499d2bfdb77b6a4ccf8419458a518b7880718174ca4aabef09244a872f9e8d9cba2b9678df726fcc26630332e6c15982070171d93bc37c727249e42b361a65e796500834e6299feec8c3709ea02f6200b390aa01cba9d9def17098433995cf54f64d7a9007f7cc9dfd4423cebfe23c00c9aa4b1e0fbfde52278bb53d00e0de1438d8b17f1a4519a45b0427f15c53a9d1293ba7105385ae131686868e19d005eb23887f0c618db868e306a370cf8bd179d893b6a00cf459d4c21d9d380d5fcac65d9e0c797998d5cfe1e472f649470d1cde181c097b770c9ab5a9e029bb5ec0183602c4fe793835294ce1f6c51345d7bac8a8145ab6a0cec1ca6a1dab7c914917b383dd8aa7c4deb968665a78643c29bfc59511541469457412cd5fb9a474f6b163fbb25097a0b77e4bd4afc488ef3d93f405e1836274cd161a17606737be5259df5b6de6fd2d79af2d84654f41f2fb3250b42cd9842a5f02e147f800043e3442ac3df15ea269b6549634070ae0c3dc3993c8486372196f9b45d079d8b3101152194bfb3f6fe8eb68b536790ae8bdf0e3bb54372f3ba7cac67844fe9843e8230256d1260b25b366394865aa3c21ec0a311b43d56f477a94a024faeca2888b69325abe3d708a6a5d356093d553a37c739286dd42d39c889e12e1936c143d18ef675dbaef49bcfbf44d5c81c51a51dc0a0de267314aabd6f37599008b6b6e32a83d98a17b387d2079718653732747586bfdedde426a025822b1fbe9b495a3ca1c544c9f9c4669f9fba29d5436ad58b8c5c1620a7016fa48a0e45284b1f3a91e0e99a850f57021d01838f184ee3a21ec0882122177a8db7519f0c07e60279a31556ce473d00c0c1a70088f2349f64888f66228ec8b8f0fed09ac508c24bd1eff53b5f94e5b7770bd371cecc2faeeac6bdac8087d02e47823f66198e98d93975dc49eb82a4a7ab08f09c9af14a79bafda95b92b9d154a286a33333d5120b0dcd2da333f87fb8d391f342a3f05dbc82d38d821909eb07860e1ac32600aaa1740112f2e6258bda1ced7adf842d28db0884d78ae75a9707bf0aed1b746daf3a892c999b5fec27f4f3d715dce3756351a4c2dc9a0605e43026a170439aa8b80509f037fbe7490abfc39a8baef9b1328bab71b0ab0b45acf7b78cd0b4bc51060b660a3dd79d0efa18462a21a2848a257488d2ac5138db83874373d9ba6a1953a77fca5185c1232985261a2eb7b69f17aa5aab3e02e533aa4309355b4427494cb494827122ecc822dde3729ae2651a283845891c9d51f70c94d9fd66de6fba5c5a0f98893eddb93d08e8013a2b564f612f2bc257dbbdffcc589f693a33e1b8ce4e076b15180084bc111d9fe7c2c2bf1fb18d05076686d1245e0340753a87161c21518687724bb3444e445a658f29135d5b0316b652bc4a9c5abc4fc5b5b64969894744ad4a986ec0462f8c0d2ca10e734a3ef4e3be484fd3e934b0042a0cf98344cbdbdd2fd1cc9434f5bc5271d68400baff115d7df50dc8a17ae1d40343fac246dcc0ebf0abc5233fde1d714a7f8a529ed0f33637d1779700e6d22ca1a4de4811e1a2dddf833c1d2d18e78033846b55c2d54d4fe4e03c3af8ef3bd691e8811544b126a0dffbffe491db66275e207e6255bc7e4912473218328cd034b4b3e767cf647ca2900ac823122c0fbbfaef886fcef6f4faff904e92fe569047b5a0ae4abc0403c3ffea0ff25b036b1bd431af41a163dd08b4dc54d03b9093e11d095025f82d721af9164e2151d035b8933feb2e4ec40fdb8359398b6d4ab3053fd22ae305f3e12577fb07e5c11aa2498b5678685da8ed8ae253ea94a3d27c0d40153260d3c72faae738ef5bcf1f9c82bde64c3e5aa314a071a921c0a01568a2d2eb4538ddae582ccbf0ca41b61d5690b77562276025bbfdb9f8a2b929a97f6ca917c456a3affc4ebdb628043a16765d872dec0796b472354de9dcdf306ba30d000621c48257de39c838add59996ff6255e43161ece94b53ba6a04cc63605eab4d98f19a43140f282b90d6eca11d4e3af2cc441d2e7c6609b73887809a82b5e683e56d2b0b148faa570b82878e75ca54d18ab305b971890ab93a75f782bd09a3abb4480f16d7a278bba38441f33d7f1820065066175a975fdf02f5003e5a9dd7fd060e4bbb1751b143046514379d627d04d6b41d53923bd2328401a21b8c6c2434e78d4eb4b1a78b14a53c8ff3cf731a3771f65c6d4dbec3f3a3e5ad9ecc8c5ee3ee151322df21b2d813af007851b37162536d19df584bc485f9a7af2e7bc773c4b15fd65f60d87edf66952070d626babcda186534772e8ae758be528e3fe4fdddb6b91db7b265034ff9ff684fe746aab18f383ce7a733a4566a45c62ad5ca35e5099148f98db0c7dcbc0222abb1861e9ff1d66d489296bd3437defd4ca2d46a05089a3c348db5625d719842b2f8e470fe92fc16225100a56db8364136070e78cce3483acc149723953fbc1974fbdae18fa55d1fa1ae07c8e6f6d95b8ff572216181a6e743a0498267ed92daa9d72a73dbae1f07ec13b469099221231693c99f1d50298369a8331239dbb6dd604575e83ebe402387255765be03e3ea05c3073c88c8de7d28a6481f9b85898e4bdeff63deab8164c8b02d6e3f639866e2fd88fccf43138269fcfca0233cf1a34546b66ac85ada2b6e92ac1252af4a165494ddb746f06966fa74b9bab7bf95734d54732c78139d2aaee6a1750dfe3b987eedbc75058c538de1b298277b58e67cf747d6ab233ce41729d0e88aed0d57d3df0ccd8daf1e379758c65c5c758241a1c81ad2d707bda102f7a8ebdf3aedf1eca54708f56c7bcb728306aeb9b19658413bc6ede7d353a02b60f67f705d3c4c3308b51280dec38d24706884c130eb05e4dfc79b4269acb10776f6fd6747a7800c699384230c0633b4828288ba0f75faabd0a6b23b9b17bf0bb82e69b5a26a46f1a7ce7c3df25816fd63a443b8b71813584e9d65646829665b6ce82333940803c1a810dcc6274693ad0e320519b99eab3333f268fd398ffd4f43877b2f1bd9e90bf02f15e0c5228bccc8a0d602909c6d0c38fa53f3a1648f369887ddf836e4476395d9c4b60f846bf311aa9cb7bb5cd8d8a345d7ef6b3dcb5ddf54c7e0c1cc4836b17aca2be9671f43d9dfce9bdebbfc2c5f5d2b92f44474499395c2c9a12709fe9d206fec263289ab01d5bf5c7ec6d2eedbec8e0d3840a4263bd6071efdaad8ad9945bb951b2b37e86f15d3a0fa7113d323f2fc6ddcdf8034bb3664084ecb23592d4c73253b707ad7d3a682367f8d45e4fa12ae7b0fc36f13dec26e77c06989e33611823631f7d7bb2098fbc1badde856bfc93c35c0e2dd01d926e7dbfe5e5e86367b4abab4117e860de7723ba7cae74449ef5bbe81b0da2ec2c18edd94abddeefb9c15dbcad66ee7bc5dbc286f93952a463374e2370b6047b257757205f1ca188092ff3439893f87d8dab840d01229627df68ce0dc589e70fe6dfc94b5f76dacdf4810fe6771cda0212aa7503ee8180fae541cdbaf2fe71f64e20c52045173a7f243ab8406bced86e1c25e604fe702c2702413c772538b685cb817284d7b75a39796c690e4b6d7736676d64d548c34cd5b9337158702cc3f6dcd43a71da0b79857a3d2b4adc75572865a939ca7c2c9c29a68e1d04ae7f30d80affbc627d06a51955d4afbffe302a6a3f094a68050c14a18316ce83945d429ab884ac703797720c3f539dddd95b74bf44bacb5add033247c220cc500761562fa7c8d4b08436e362595f053aff8501f9b2763011ed2a5fe31da735439019344f6570667d9a2d6b3ae681a6c169b84535c94c994d78c1af8e9a7801141ef5d29c60ca28e1bc6f27c1882245e748b3a712fb2abda51cf660fc1ddd2e8fbb8581f36a039905422d5afc192cf1c2e0b87a1e1a09f8c8f6bfaf7e67a975cd5543c63fc4465302b9aaada57dabd106a3bc3646e8de8dfb6af200a67d162cb199e8160dc02ea2f21b00c751a295807a731daf9fbb790ccccc532e48e8adf3a8b0e643a13dd85590e4df0d456da49cb05997dfe6fca5b2f1a9c3420ec709443b0f54d0dd48f073f8d78227b95926c5887f2a6de302c8bf76ea6ccbcc1b5bfae3fabb743455a084592fe5f65a87c922e13d7eae87946b0bb0f168635aaaafaa65cde4a30e8ea7e0a7579f1c00f7428bf7b5075bbe04c50f4753ab181b8834c2c5d525d3f0d0ecf74e7c6f2b3b54236db63158651d6fb306275b13724aadd3f29e6f94a7012f91b3eeec20d97a8bbdafdd54dfdbccd1e926c365cf47e61f93b021ba905672b20fd99bcc3554f31f111dd41070f0dd25088687ea6a3eabe865ae110e61c13f4c3021dcc00d9a691bd914c7037e4a03fafc3a30992aef158e0b81cd0e3f62da936c23777d7775fa0a28a3b6190e2611b102967b75eecdfbbb589ac492d2a256cbb3b85026dbf538e31028df1cb66c8001d412665ec24a9e3a1331c3ed234716ff12467a0793f9c5c6fef366b3b00426d3aea09514e0b873927413dcb30321e816b94572218bab3447f89251888bfbbef577b77a2d87d9d5b2663e3ea19013f18a642f0e19a133c1688951bc63861a1c42b35e7a60b3181b50d2c53a9186d47c73873df18e4abe45e592fd4757250fab82c9c148948be7b755d8bbeeaf7044e423a3052c24167674849eaf379cf7f1b15de23d7a3d22b13fb28b98d7da9ac13c15dfa9ab0857b393e1f4c5932c6871c754378b355c3682a52f759b0238495378964fe6c6be3bc58d8caf9e0029bf11df03a2e16c3210012b9f36dac74627c45dd15bd5382ef36591d338acf666b9799ad3b4cc04b1d31f695b72c644dcac9308b0834be78637b8b3e6c0151aa0fc61179d34113e2e92a021ed0056ef712da3c44e6566f2b8b0e868d54214d95e1afa9faff5ad96d28eef6d8ac628e4d599c47c4e346f44284a483d4633d41399ff385c79936dd4579a874f70a5f332eaeeb30b2568f4c29ef02418be98db9c5bc4b5fce55b58899f7c3420157108aebbef04f1d8f5e10c916aaa47489037ad284eb526589f8491edd430eb884a552f91d032f9f1e7230e5d2edba894323a92920a8b62ffbb71e9f6ebe0cf562967b3f5875344c365618b51af9e1b97b5a98b128ed575390511e8bf4d4ca2f7446a9a3bae03efef2aa8c6f19c743165364f62966fa706d5699c31502a1b880c8377e41783e1ba875df81bcfde8196b20000f50783c82d0e8e25fb6d645dfa135b0ae42e4fb2a8b4d0617afb455f7c8e922010b04e400c5908d7d32c1fac52126ce6d855fb51ba76d4624ea9fd030eab7a1b1e647091a59c86dbe455c66d042fdc444598c116c2c6f3f895a4957922ecf7d9d57a37df503c99e5b91bb08517563f46533cca64d674cafbfa59f12146d5ae4305a43ca4ff3069dfd2317f78a393fe52e9d8111064b9bf5f303b5867bc25d794ec4f11f4405a69ec23b987cba8ff36a252de8ae5f67376cd4a61b2241fb01f6d71b5c2cd6f3a9c243bff34464e7b7fdfecb95d960f0db1811baf78c43d5fc4be6b70c459ef581b2305c8d703375a5c6e1ab5f3462af43e550d6ee6679ed542fd98cbe3dfec99abe7f7c258297ee5070c4d9c46a502e0f4df2a9a59f18d14f1ae531de04a129ad756aa3309a60053c93568d5259093e8a7b4bb842b5823bdf43b39d396efb376a0aacb0659b6e072153a7abad0738de2ad41636524a6f2d7960bf35b2055d3fe8db44286c36ced7455775c86a504efddd6f99c373a29eb090358be0b2144e07c66645d0732291e41303f84888251034111fe422d4c3815d8f2b703527de1f94460dea6ea11913532c40dddd8f5f2831821187ab1f06266609e24529308d91504accd8a795fcab363ef5a57d9f1c011cd02f8d97f7167563bf93c59876a29611896c675570471c2b1d593cc5d6ee488e2f39d00cca63ff6c18f28f86a6e18e9ad55e872f004d33b1c31644a5c54a0f76f78a979956e411ce7e3089ff3e2cdc0d92b9eb7cd20a619aa640aa4a29f82c92e773606072cc8ebcdff76ede1704e3623b3cc1a676f69b9c7a2446e0180c4c6aaade52f51c48102d36f0fd4200181331717f5b983ddae2d2361c96ed8d283e5ce8c710377cbc543f735905278ad052d2f3900fe13d65e1482c69b83520db3c23bee305d1384feceef52fec91ab68c42f1985b338ee0d2bd7a43de552258931c99d9ecf460badb15cce1c22fcfc553019b8ba8407b04cc1bddaac23d2cc6670a9dfc2d41baf61e85a720f3dfcb41e657971356c90142a6805088bbf36fa4e6f7635f43739e4c795bc7c9d2722d0505ccb79a38d1aa71411ec546e8733c1e1388ccd4a60c778e2e4f53f45aa1ee0149a8bfa576df36548b72823f61a2200ea5d44a6f97263da99fd72e23096ef6cdbbcff601e5c872d4311e51f1bfa0cd99c645767ff004853d373ebe59d7d857393f7b905cbaf5dce858185747f31f5f6cc1c8f7a7e9aa6dbfd9b9d42cf5a61b05effc1c98191795e3076220bec76dd738f3a4c49275175b6fbaed05d7f530d09dac676426222bb68ceab277aaf5792b2a329420d3319d9f2a2523bbd0b274dca0b7456556e2d8013e3ebff04ce7c09bcb7435db85f0c28c209141ebc30bf857bfc95ceb945f69f9c502f1b9dd3de366c005a55c24def16d1b23e4ae3350103466d2bc579f2578960a44564596505efb1512dcfaf481e5f86bb68f1bc252e54144528612d9cb393eb1cfaf2349794d676e933054be13c6d3aa5aa926835080e099ea9aedbfd2e935e32d4dc34f64b4c0ac9419d6199d78210384c2c24bcc65a3cd6e7a2a592bc95d9b94c56ab9d5a0b72b9055b9b4078620f87bf39494743016dacf5fd42f57a939b97c93133056cf2875f1caa92becffee834b5aaafb6495f4645433d7bd46448dcade143828cb4b626c95229d5be99e5fa2321a9dcd48a9d07b0d918f99858fc898e28f780eef935946458b0df9babc461960e4c08f74c4f22a6be5fc2575e8ba0d07dcf19e0e6a22bea3e9ea028c31ab801cacab9d7841fa0876f77d7bd419bc95440c2c7ea537670525a4c20de6eefe6a4d9ea511924ba8c7a6cabeb3ab67b754deb643efc8ed0a50155090b294897d8291f0f901e4ab84743d5e6e5b59ef6e7b337f9ef8ce115e2180a0ad7fefaf9641829623814d7ea1cb2c45833ee16932c0f15696e9456b22e0f18c5e9b398d86106119aa9d1300b55726e61e24f24d2092df51474363a8a64ca2636701295e992bf9f22681215b0d19351800cc2e88ed593852911e6c544a5733913b8b08748822fb3738c53855ae2c2725d7e4884e42083f766402b2d0cc3fd5d1368ab53fbeddc6dcdcefba02507e9676a928d5398bb717c98ed53fa1765d722b77db7d1bca21066c59fdff47bba33fb749ba2dd8212224496cc9d968da9ed5c8e08e9ac555965f6796fe67e52114a5811f2297e93ebfc17800f9b993bac5816b9f53b2fd91d793493690d00021a5271e1e5ddb4bfeb0ec876fc39df7dd83351d4a653117dc07b801976c866680ce8c74096014921636c14a33a16ffacb4a8491ec68d5f62227fdfbc722a8fcd0180cacc77b895a9933a04bd3f117201aa38632d0d0221a29d83c55fd20215b77eb928c09e89a6fd07aeca63f0bbb25338c25c3496b0b841e87b1e64579008671e3be59bc4372b86ad886c5f2d5ae879c76d00bc98adf12d927c91d811ab52b9197ff6cff66252f309672dafb9f3ad145cd2531b662114d48d6484ad9921db35aafd04ce5ac1dd2dc9a07defe95b56ae914b6576cf86f64be77643653d60e0e5ebe1571d37cc0c71c0ad8db1b51776e666711e42b8ac2b44b2c3dc6f2c5d9df3c0496d8d8b2a27e6917660745995b2bf35143d03ccc0617629f61e42211bf669b7654f136e093a2ac109ae4171604cfc49761bd68314395d5d1c663e4d40c87654413c39a0a9531a3fc7d1c7d111db99c25d5abdea7052622dc7dc3d08603f66f9f7b6e693abd206478f33cacd4ed5fe7e4a6c8e65944262fc8d222920a3582d807709e2fe45065a887146a6ef98d3ed5ba851c6f4c8c88116cb14fcfb6269345c06ed5873b47c9b6b0ba7c4d4ca909f2a8216470962684deb2a5d85d62a1df1909db7a6b4988f459411240ac490b0b257035545bc81b36d8185e8ff09dc859591a18c07f1a4d6f3b41eb97ddce17ee6cac809b13d475f40b2511bca8b2ec62f341792755285437b8ad61fcdc6692a0dc5021dd269042765af9f7820dafcf6982946ecd9546303d831d2ff879daf809ea414698e6d7115ad23ecefefe3c5483e5c10359558637e43b697831e5753e47013076b109c796d3023dda924e51adb1b80423391ece0d8517a6b1a9eaf108c26a0a61faee00f8c53d74f8e3420c40bded234a056f773f88dd3a924ac2e0320b8aead673cb649b8857eecf8b915825f80a742c944bebaea4e68ecf91718520beae9934a4aafa5b838418b8cac505c738012234297d8280665eec6bba4b20abd66295243e0c60c76b05527158f75f0a1d77cc271b5ed1554ad400e8a6404d00070810c143b463d9c3820ab9fb079b8cb9bf632f2c9d35e36325e3ff7ba66ba88c7ea2924e15a4afa48485f06e5ce65b78b4a54aa098019abe31d9d598c2af978834fe66a3a8703f31a096ee5517fec62c0ab4e18e74e6534ea58a4bfcbec2355578ca772af73991361d049412eaf7d45eb5c848da8439ccd9f4b5eb8b52d438496e1a158daf3e20f9081b2629c02f9c23f8cb820fd1d26814eaf61ee3a1560d4790e463cd38946bb1cca39a5910947ef8fbfed47ec70715901a8404c5f827ed2a17d062520a0f308583f482a00dca280ff1995b4d33fe28c81584975ffa0e582bd2f90230fbcc08524e89ffd8f77dca03da6d3c009943d76d89703b9f2e2a285b995ed0722f326d64f90a8f049cff566c99517a4b0ffb748058a709d3aded0439ba1e2369731b31906321aba12fa9a06a6625bc616a35940c2d2075b2020a2e4b78e04134f60c22f90fb3a06dd6f7fdb91f636f1dc43e88b3c013a450b582d71ad8f351d61ec9ceec34946d5f6d8ba3523beac6dfcaa84c4411ddfb3c8627551c59c475099edfceaac71b8a751c36fda81900650e8b4cbe4ff89b1ed60f72f2c3c8e0f53b6e9d2453c52366d36488ee37b660fda174bca4ccb92109047f847f3cbb1a844708c0ec434a22736b684e4aeaaee419cb38b9c248ec34f153755a1614df3bd3b9b2c38699eea2498fda77c6520d1edd3c980f1d615a9cd41f44b383d55dd6b36705d0aa821d01b81ca40bd8454944e68ee559f8d5ef411c948962f36969d706900986a55fc702e502d50cd4f7e4f4833b93d2550a5bc6bc8842a6c6be936bb257aeb1ac1fde19d875ead749d74a462d37bd76dfe9aac898882d69f7451524b2ebd7a40cd3d0787fe4c3a54303522cb0c5013ff14325f8892ade1a5593649a827f16b16e2c69d24b4f6afbec50c375a330d3540ee0153a9e49def49e1b6c68b20066c7ecb67d88d9fbde6ea0ee811eab6c8ddeabd54e0a62993d51de814562d52aa37ad4f0f440aee1064e0429ea82c7f4684965c75fe8de0f9afb76b3b58b945b4c051d73aa8ac76dcd358d8e4bb7274d5783fabd4595913725ec73f15a2cb1587d3f8bbb9da1f6b2d2be83707fbb767a84b4f7eaa1035f8a937f5b4e92cd64c564e43b693a125a79f1ebf2d5432a38775695d7764baedb04bb817b79c8f691eea0378f98b64282387e9f09df2562e183cc2de9f451e3b456bc319ff51e9e40d72af4b6ea34f2cb06fd735580ef55731f95b156f548bb3cd494c291fd2f91b2f39be34235d627a6910f069996d48c497fccf0e80fcaa4dffe7942780bc53cb2023a4e6c972c6702a4e20c4cf32857bf9ecb7b8da16dd0d25d1a8f4726d772b32381637a76a6b6ee63c035a59403043f120f15fe84c32b074246f61b08d5cfa14631ff201d8027559cfa6086572ce26c72ad2eee19c81e2f7d995d64ef48a10110fc5efe304cfe4bafe8aab69abbb1e49d127920e406d121bd51b62ca40008d1ff1b6e10dde8d1a241a6afed3f2fc6da0e9361c27a7c73cda923d03f77a48ed650e2eacf42b24e511fb97f99897d0e4032db5188aabb18291b9de4f1b4a9f1f71df4d972cad2c2b18d882a0a785e227520d8d1db20166acd960e9dffc139ffb037a726233df49fff2395d1dc210b8725e3e300c40b2db539767709b84c24b213bccf159c3b93ee9a8f6c374e94afe0f3cba6a2eda252a36a4866ee9b17eceb245c233ff9cd8d0fab38f2c76e0c9a4cea8fc18c52e0ffc925a07b13e485b060b7254f28beaa4bc519276c7e25ae3d4f05859c5f71d9c8ce86be04a052e23641651a854ef216dbcc7953ba9bccc20ff29f576dc530d58c1ff811994ce389116412ae97c64c45058d9df73326cba5b62fdbec9c47e64d9299faf11c45a028bef415c49b354b9c81f2335e82e87da94db6e9bc0a636c90b331199eb159e9af7c3343ebd83e6aa8d03eb3ec7c4c68fb4fa0b2e41a561ba768ab6827c5363fe6bac381f13b83b637a28b556f4c348e13e63ff95fab50dc825e94c5f42f7842836a9365e796d4c3c5c4a228bded42990b7dbe211f85beb70ffca167f16e3d5011094f967df3b0b67392198ddb88ab780816c0ce4bfb98dcfd5ee5e9b232c416bce02b64055c23c48baff9ebfe5d5689f47da5acad9a7c34943d13a5109da7aded758c50048043fab77974f147103dfccb36aaa17883c98cb453428244aa938c1a18bc22590eb42ea6355ee3ee2f7dadca81dd39cb26e37a7a065731d1f0255679fff784e02b128bd18420346905dab8d9f5634b5b3d7aa07b9284222036d139bb2e21364a8a12502b5487addb560849c9cecdcf018913f2270c09b32f0542d46234b3b4305ed7386e2f48070b5597d5400439be288f824d32d376d65f2b523cdb3ac1a2087d3d0d549adcf22dd73705755c729ae7ad0b3c2e33db17dbd2950b9b2fcfa45c80f23517b63b2011e60939724b8470d01470508e1d5b6f5cead25c192050c5591a7cb2b065226972bf1e25687eb09765d484db50e486b6cdd9bab0207209866bcff7f6e7d83bfee5cb146f5e66a0de740ae01f226a36a12f1c627935013d14b2b7df86ebda78eaea6b4d04d669f7796f3717f7b82edb457ea060e40c4e85536b4bcd194d68c6da9bd8171c029ba7908a5a47cb95be3f0d9ac6907f18633c9354f1bc81d3aa997e0448c96ecd16289304647b01144795a1ec87c45a27d30fe3f9962a19f1c9e1a0a6d44758607c6194abc56c6be5ea811025ef40461dabde13bdfb3c9f3ab5ad10bb574ea5b63f2ac16c6100713b0c0ae9af01064d66419a12767a9922e7ca91ad43ee162e82c9ae7c5ea0d51b648ef4ebce9313754b63f280e973cf03d58e972f2d2f3169da424e77c60dd4a555f77059ac438fd8ad5954a514870862e3e564228eba3cc1ab1f116f21adfe394862288fa3ed2dd439996a12f310deaf4f0dbf3df41504e887188eb801d7f98038494bced29fb912b7a105637b7815add3aa33740bab682e3678d33064fade86f5501a3aa2e265464c8d8c02309a62afcd20d86e0b093a6d87c0f095417dbaf2447eee994fe777801d534113da8de2be9dad2917445861f8265315775973a6e85205807fb7fa9ac4dc21da35ea5ae836c6469462c6b7e5bf11a69a6015b73eafe95c84b78f4637fa97437b6018f622a9d0073021895d6eea4955f73cd50326d5b9c59e72e191a66be7e7366479bd502051460cae6a91de73e2e9b9235d1366e9ec7df43948bd5b25cdb708040a3613485de2d34ba89fd4b8bb66cc6f78a709be8754cf1b0b893e50c673a2b61cc0b13d6e551c576ed343feeb7c8514d4eb5c82c0ab53ea0e27b61ee1b27726c9c85697860dbc7f21b1e7cbfc3cb5c4ab8575fcca79313e416cb9ecd57dc510eb49ebbebe1914291ba4167a5409389b5d11f9c4367aaa96d0640257b0450ff4c2e54c3ef42ecff7967a301fbf66dac76083915fc0e793b4b549802ad100b0753df48d221492d4de66cbbe952f7feb198a03e4c6d160cd041feaeba8c5ae7f025d71697202d82425ed3d6b283974956236ae9e6d0665a567fbe1cdb95f600a768319b6ac8255214a1d26a2902356a4f25a223a5050d59c058b91f2827d2c8ce09a74a9f8a3ab2587b9a752cf0ef3532fc39dd81ce478542a5395bd14929aca22f963c6c2b402eb604cbc1a90b1c3cf9958e12e2ef29b8104de08372b899e93024d696a910dc34219df36a80562e1711c1fc465856af066fb09d329f483c55fe3b5179f77e229bdf420fd17ca91bfbbd4122f6b5eb5018ee64c4a6389a9a062003b8e739deadb77652097187975a4a919786e9002c0e058dbadea7447f8d9e880eb558b0c08debc3b359da9fe696653ebc9541ad29c117cd271d99564669cb815009a6de08308304dededb891f202ab5c9683797081529ed8b11f8bacfa0cdd45a9b3a0e8ed1622e3134f3a7fb4c89519a8299f268c0905772a2cb3cc7bd042e0e4bb4e3dae5afeba2c4d096de1041338cbe5fcbc183330e639703c4ee65c2fa0867d40bb480e278076311ce1b215d180befc6d6fff8b5208f1ec4f6c16ca4bf19243066230c1c6954cd9a56a4c1538e55e9bab818aa9114427ff60e9edba46f171b1823a2330546aa5aaedeaf398e3519d3dde5f126c8e796e7a5b1b77e0d6a64b22adcbbc967ee76ae64d6da26c8fab0a265e98268000e1ba15185ba32e4f7660ae1e2c7ccba8fae7db9f7bddfe0b8f27166759dae34b51b02ad1e4d11f2f9016d3dd49c24514a7cbcce02c6a24be90ccc611db010d737183558addfafeb2a6b136d30360a8203fb5d4ca4ed49a07930ad65f9a3e98b8910d5a1cd55c6351335ec5afd56ef75a40041f9729258456f4c6c5368b1a5652860922a2b96cbc88a19951d2825c728bf631937a9afb487f32803e9e3d549e7dd19689f4f9c3d0a2911ebf059fcb17093a7628433d7000e6df6976d2323cafb3ced10927b4d35af4602394e85153699a8bcb3b09cc5c70b4c35bf0c2e5f457fac9620756c27c1991312085a9f7c2bd79e6587983fde65ef37e5ca38da1d8ede42478cb3e429ed48a7568f7a9e0c954d522da7c8cd0b40578ebe1a823cf3bce7cf9f7768401d452af52a44a9a3de859b7f5b19545b7ef8f14ae8fd39473fec6d1b7f2e1c052a089ef91cb2d057bf5505244c025cf30aa83f02b8a3febfc40e243a681f3018d3a7d938536d85796b3892f2e843c5f607c23468b4a9e0cc0982e75420ef35827428b9a85c302eb10cf5c197e186d376eeed38e6ffe2e6c0235678db09c2a6f398c23401572af0d7e063bd180937fb298eacd5cdfe9079a958f0a641a232a331f15ad3c45bff38b2120d903060e2eadf21d0b0ffdc31b93cbf2affb3fda8836a1b8092af5388a1e9aae19c642ff9a281e4412a1493211605e2e43eb29c4a8ec52158ec4a42929d790cfb36db263aec6597f89ff2239c8b90a9d27a6695a7ce01816cdbd02adca33d2fbce97affac95513c64985663200e294442aabbc077b75827e1f1bc0984abf7cd5224bc873b7dbf8f4e038c63ac01e18ba3954745736f06a7586bdc316d101b4f7613830e68eb79660995f8b64097d7ff33d00d9da9dc926fdf0b9b6cacd0da6a19d3b2cc08a014a02ad065098b8dbf058607de44ce9a1afccd629f62e518b9eb880f2d7ffb0856e04be63bf4a2bf4853fa443d80212b0447b3f4fe5ee0a74900ff6a333dcbef7366c8e69c5161076690e6ce4860ddab066e4b6f7764fe131a0b9f4ac189b6f0ec42d7acad6c1d0ba08c8bfa2fd2648cd3e7a4cb6a6e485a824a8bd0d927eeea928a16d29ccc5c9e1b54a489f5ad4da341f1d5a1350c33bd83e177ebf9ce4aee8ba3696a9052f34f0f548e0768305e96fea6386e6c6256fba4926ef6e2e8a49b742aeeeb2efacf93de5aad7da876cf66162d42cd1d03bc5cace89a359e8a6f18c4843a31daa3b09df55d624af2c41230b4029cc321237842f3f82850710627ef1cf68c3e3e1d961241d7dada7bafe6faefb84c0392715e26ccf963fcf8e76eb73238f7699baf9b46f8a44a275472810fee4e91bb882ca014a4b9b61cf975a6189353cc04db2e072b7e4675d763745bafaefd8c64085f8f46cc2d29a62da4dffac0155908da2102bebc42f8a8699d6ec3739066958365fed7d5377bb453c1056ca3b1c2c4063fedd5445b4b00585db7e84270e49d4e3a5276079ea0f13f6e8f2b9830a10478a6490366ddfc872f673378162720240610e76d72239917677f6cf72400abfcc4afbd898dcc2cf129fbe2958e698daed62baed6daed9a9758e3f1658a2dbe7f17694ce9bcd5c43cb31f1df79cf0c010102990fe6a1d4c96d63f9a9631d59012596f16862d580990bea0c4dc7aa746f13c44e9b3a35228bdd19f17e8b0765ccf784d7e1a9e08ab4b4c4d02d34053fdea7b14d7894c4be32b9e2c7c710085f5d5851ba9b98bcea1f4003c8d3a382219ed115c02bf76a23e5ce368ef66ded465e34ee81526942725ff37f872353c2af4f0fe2bb464f22dada130fd99cb90da6d7191e13efeb31579b4abc56b887e21a238535ed850930036d6ac56d41d1d68d506c519a554d275abb4c5a71bfe03a1e70d3e55dc92f183401478a8b72e579cc2d3a1a131bed80da4898ee5e57441e1a464235a6de84336f1a541e68e96600045df4de22af56347f640f0bd770024a4c4023b28d553a366583628f9005d613d8d1fc50a85fe1ea9d2272542ad6557c7ab3ab24f6b58116bd4ae378684280d919fddb352cfc3f08aa745190b459b24076ddbbe3277199a75efd0db42626af4a965c4b997c4086aad6165a904291114ee4090761392d4f08ad3daf97c345a5f4f79d5c027bcd400a870b01e674802c3815a91216a52fabe3f260b5fffa14e50b219506f917d8179cc76d6b86d4624b63074eabf626f02245b528ce3e1ce8bb499e7e62044663a812f6f58fd14bf7eda00101e9d571011584c5dd6a196057312e5bccb25bc112dbfe60506dfd605a9fde42c6c2162ce317bfe2c142817a304d953ce4cf038e3f32835cf6dbb272d3d8d2ebc87ef115ccb86eedf3666dd9a1278d61a5bdf083d9c59671e170993eb5d0316dd12db698ee2df4c012accfe498f24f8857c2e12d10352f5e6edef33b3a295cc5ca5076ae878efe0e30294a831484863b77ccf8532abbeded03c8f4f9b5e01127de8dc077f28ee527ae47f1e99152580c4dc6079427cbc938cb586a64196e1a2f3bc691638108dbd61fdd34358763b6a09236c15c48ce3e225cb295700e9532555b7a4179385f524d365983dcbd90d66c795d01c30ae150557671871f2c916b4a09ef504188e43d5e79b46b23cf35acfb398011dc1f7eb4d66e70c336f3efb9c1366d9568221e7b023d7aa6e2979a11768a6005fb42aaa65361914927e01b7a67f35976a307c0eaf83cd230ea7df8d2ccffac6d96f071b9c40e74d5b0837401043946935e21db639b6a6d73b4fc7bbd1911f036b62d96440e859e0a115a42b7f3b507f14a9789158f637f3e1422d1829688dc02630ee04a398a114183a637d926193df5f55777c1ad3dcebaab1956b719e344ec7fe1e4f1d4c24fcfd1b5496ab513df4644c2ab1d04d9b430739e7889d381c96bf2546037172a8620c8fa86d158c54515101de971dcf2c21e7a1addf4efbdb955396a584b84640650190ba32e06bf9821ed6c3644838152825e27fd334a5e2027399b8f50e2ce901eecbb799c36a60243d9799ec2a21af319919fac8f4a38105704650d78c7359f41319eab3e188530609e8a69491df91871688ad1f6b71d6f19ba9ffe426f22a77319649345441a0b217262aced48b364540c9aedbe7651f6fe2a91fde58a68abbbd3162401d21978f47dbc2612dcbbb3280f0c2020f8ba0e2521f72e891d34dd64101a6a2674314ca522e9ca1f4068287cdb2d873d198bb7c8298597869129f1aa58fa3f4701cf4986c946e95011c6556b1cde3deaa830328b1e5ac99b9ad976a408b505395deadfa54edab8595be14e57c61160dddf91a3631ef35bd12a2e67f6f0ba2a6cafefbd90d930abf79d35e57764e52b2aed1f73fd32042a2bc3d3bea7b2ab83b2edca2ed5831b0e62b7a5becaf846a5dc1105af3c359ef1ed7b830c9a6af97be8303cc5049115c44113cbff3df0bbdbd000271fd95edb14917409da65deb67c1fa2a013625be3f077675343758b0c302b9c86082f60e59fa9193a1927885a82e95897e460b8e7326cfd88bf2616001747d331f697c06ea702f385dc3db24201535c1656c2af590325708c0b4fd576f5178fb58007f674a7159ad9d8b44778a8bc4afae7246a168339114882673e801734d28a819411d4f533f1d0b9fff2eec97cdbbe7c044669c973231fdbb0c9f8d7ed586d7029f3b45814450209c1ea67f0067c2bfa57188e117d8ca0c69b8e5fb59059413ef866689deddd109d8b1970041362313405559600afe2b23eb500ec12d7b5f0645e311e17958d18d2e5d8c941531ee36c8390af33bc1b02234a024fc669dcc48cc2e04ba3844ee59ea6adf05d0ce5c2d0a1f18d57ae98d450d85f40ea7a70e2557e12556af7de8166240c01f20401d2a3c2daf32a3c2781720387c8a57193aa6540b03fd669ff7b3cbb8740233dd9f5f3c17051e34db2ae77cb2aba263da7accd4b4a8190793651cdb5583e79b38b6463d5dbf045bee7ffe48231476279d827ca63fa8a52d13a51a65b57cf4cf5286233a0d561c04e162094e49724abf212a9eccc7ca1d7807d46673bf5f75032a3868384dea16c7e76b96ec08e994c3bb0ed67aef7ad9f795481b6b1b007858668593f80f7776593e9ac33958fb9eff9720cd1a3f1131f3230b7fbdadde6ea15961b47a6468ed6af5e47ff47bee4ea61dcdb7cbbfe111c455c20852561efbf53078b30c305b3dd0304e529c6b16b02762ec3d4707286a153068830298542534721bd105076ab5ac9654c0d6ddde5aaba47f33904c7cf723e23f469cddba88de33c59be14f3037d2da59f167e292251081b82799d01fca676bc968f9286cd0ff6c8c783457e56d2d74dae21e780aa51a52e695486ce1e6bbe7934b9d0e991332a67b9a754221da58fd2918a2f76b47347697445d0f0adbb5527b9adb0656eb755c46963433ee20b13e2f5823d82c94578561e4dc756cfb442a4425557637a7e40b1fcb8a6e89897375cf49288bbf9a4b03f1dae8f0534c42eae7b4e6f5239b8502177acf6c29640e823d022f23eb5137e70738d5d60f71fc2a707c6b0cb59942c34370b00f4756fffd381c321d5deaf0ddb25d5304b3113b9d4ce5fe102d15728ccf57aa3d1661fd3bde00fa2a7af4a500336ce3f63225c308490f34ff17cacb745a419d551ea8618e7f6c22fe50d687a05a0c72260a20afc12c73621270b7e8f2e1747f1f270790aec4f43335d5d3cda24e40db7ff477b0e53a1fc8486820d72e72eca055e2ce58156b02718e332d7291b5525009788de1de7344a32d6dd962f014c1ac170e7e19815486f8ef0a4dfdba87d59c5a9c81cd56578a56fcd147ad61e156660ff69df098f23e2002f3182fc54fd7efa118899512308a771ed10e26eef136d51adf7e40f9de3a3989f8fb7b610f7aff5ce755602f40046345dd67770fd8b26a321b9abb3065a464a9950dcf217ddb28988d028c4ba84206628ec3f647baffce52578a390088d9c51bcbac0e4493d9edf52742fa91483807a86a349d9bd6f3274ae0b8f2e86cd0d642ec3f8e141c3a1d2a02df24bc0a2cc9ecad06a7eadd6a7b5f96d21672a3038594f794a6edf44c8e2f97215f310763c66b2b2de7bb5b32a771b92144d4c27926373a68df1934a164bd4c1854d60bc28cefb6f544b3a145ca213fa757f4c61b056afc3895e891c6399e5d1fdad6869a8e2cc06e1759e35ba91697bcecb51edfa2311fff1a276313744aecc0069e36db4be331674be062080a0a3b6ca53e7cc1a2853f4f8d694afe23dbffe0ae978300f82e8bc68d27e799752ef7e3b34df44737cd8e274cf8ad5bde68f59ab745cb5feee629ad353a0c7504290626a10cb704fdf5d651a0bd54f4a3c7d7a7c4cc07329d6f2e9905fff6cdf636f35c9bdbcc36d2097f4d78105b3fbe0969e81d37d8af24210c0ff8b0f3cb245c30858bc858fb2c8d8e30fd24a7448ae4e00a59ca9b5378da0f36669d0fc15918b72e8c2a13ca6b23afc305ae8733c5d9e0901875b049587c3477cbd88ad91c157e9a4a5768484fdb635cab61aaffdffd9c8d37f9e79c9e0b9f7431a1d71a974e12d6b45afeaf26092a3d634b8671370f75bd2fdbf1b86549f716789ee81954537768231167403810764d4e3d2c6739ec68ee4f2a7c9130ae3db3f4e92677c46cb03e27d0043dfa565d5112fcfaf4b44110d3301feefdb95adeefc81fe7db193f263ca25db6b1ef5cfb5d721c19b5b7de06e3d2648f7b1a373f15bb3288471d07eef86489c5b21858795e106b390e86e05a6739c7f243413bab13c6251159130b8a08c20f030f0d5911757cb0708f6d56abaab288cb9e80a120de2e947e425d63306c832248a185e0e41c0571f0bcccbcb9a6d0cc92c722b69db4546d284d0d58b8d084bf76277b711a63622b5e283a99b5ba40d02ce6c00888679099fd2f4838416df432a3313ca7653ce8c9a656634d454978877322540d485eed08a642f41c7bfadf917987d97de8436009e7b7256806ff97efc7024f4b17d94b91671b2e1e71b500497888ce3205d384242e4ec6afa4a180f5b304c263bc69d51c454b37047a123a3d9941382275b85ed2792beff18136255532ffa7c894fc3a0ba61b1b2c7ace12358e6f7bc897c90a50a3f57504f9e4dad1c115d22d95f8e4a9bf2484012808938bfede50c00b670c0af17e9ab15efabe6cf62885f1f0fc2eb9c6ed8373a3a2392569ad87035bb1951642d100111e57f3619d80e5f2d2b727a86b259f2c37415ec2079a7011adbaabe606e59cb516c45925b0acbeeeb93d2a588d72d66baae4dd094d7dc2b76b4303c7eb537f8d16a00432778220fcb7fda1fba326e24d7a369102b1356b25e24b785ff4ecc9107dfcd791f9d508213a79b52b2c5f4e4405dc1ae1ef1181778ee10af27f73a54f1b590343cb2404312804484e867145038efd63b5703c1ef7ee91b9e3eeb187e079bf5239095526326c84f64631f3ef49bf997cea0f798646aaad648d22e45ed2994bcfe100b4e40bd495243222a12a51414348ff219055babb63508488769e42ae13ba63848266df8469fb361d8c9a2ef2a8634ee2a83172d481d647ac563c8d1e4b6fff8101c26cdbcc9f1c85a36d15e43f6f43ae17498c0bb95fc8892bee85f3845a73425105f66989c195c7575109d7daa1ef67577c04da7e27b33468508d9a16abc82d9a11cfd9fa8b172fb5d3044532de00554efd12b3c49173d18d48f822799f2f2f831d130addd0f33e906e55577df04efd7d271a7e287082c0aec7674ec1f4217f4b61510c7ba844f95a0f6b5b70930b53f3a0d2cbcd70733dca94e384bec94fefed8ea5093d86ca9912dc099edebf0d199170da14ebaeb525d23a0be4a4d97a56aeb3885067f4e6a9f19e47d8e95ddc309f1ccb865e72af7890a594e402ae96dcd0d229fcbc037561ffa62436ecc2a3a736868d6dc310503a2a8f21126879a1bdad7d2bcdfa67520d70e26ce5c2bae6ec13603d1b7a8e9b613142ecbc46a789bf5bdbdaeaa160bf23842001a45927646d092b75aa5d9a2293ae6212b8912c92d011028c817264356d1a9a1f905aba2a4ea27c10e3c19e61a6498c3e62bef27bd78d1a5cbaf2decb786ae02d03deb26ff6191050c97f4c531829bed9843cf46772f382835cf6227f41e9b93cc5e42cefb71d1b18bf656572ca222c3b15ddc2d91de0c6592d7af971fa754c0f6d72cff97c4e50298c4c615601bdc7b3c200fbaaab1846bc09d5e35769820b43767f24f79cb806604c83f31e020476f6e119fb87fa10ad7b845cbd2cab08ee2511fca13187b5d9b6e456792fb53b6c417c75aa8a5d928a679812fe1f13240148d3d7d9d3df9a518a0b890117234f51246e2989251669f6d196143fcc0cce6904d40668a2f68c5c4d9f0dc50507de1f16a3576720ac779addfe01019467ff2d7215fcbc0c0980024b49cab81cb7d60077502becaaa98256678f33a9e5474f6a3892ec8ceb8c046f4ce2bf6709d8924da3516f4ef9e9584668a53eb62ba98f8a0b465efd266f8ffa4eeed01c8c540b12fa9d2de4b40e9da8156a4e607bac170616f48a24fa9ae8305d18e68f69b19304f269ad458a100ddf411b05cbdb7543d324380bc198f1ff76b36160bfaa048d1d4d0796b6b8e0f774ddad3ccb97a0515c4e2fc9864a657dd4712008366caec64d22143f27af1a7fd02f98abe9c2a8536f5c468a2cc74eb26b933175473752f3bc380ae2f57e4b0545f6f785ad5149a74e619553d208b169c90cbb77aaae8ad458ea83ada7ed44e167618dc9e57ed2da10364e4e1d246b49328a686c387dc682cbfc2a5d81fcf101472d2b79137f5ebe9d440f286dfd08c350450e1d2457554a2b7cb1509c74d254824f0a73a0e81508e60e1e70e90ded8fb212d3970fc4dba6e4d292142534ba06880b270e4d86095b5771c1929f07b64ea78bd589cb3f8a559ab7d55841e45cb33ed5c2713e0a8ab52e4dab4be4fd5d90abee3021188f60ee3795ee6526d6b005f77b5aa49a285fc92f308a92e7f9f55a61d3c4029e3d478fc12555f2eb81c11eb6d3b8ed10677bf0a6290585f2aa474a1a48cf13d00eeda98b72e6979b165e19d1bb80c647f03d8d535a2bedcdc425baf879be8ba06d06311ee4fb1a935d1b25a365a7905e4d1c942a665eebc0433bdd8f359a93fb7ceecac4d613a4e9e7e3483b957640706aa088216b681235e9116d1fbdf994b00bcfb2e7e23339f8e816faa0d457846a188343ab1191ddeec62bb5e2d4445ba641982159496ac6fdc74409936a7464edfe8c206033b884cdb0c5f84592b4b87785aea118dcf2da13fd937a4ef171afb26719391c01738c11d457187ec7e93394b39d90e04ae6e66227c062cd2fadac63556c652846e11b5e3025d051c22ebb294d14c927b70baa1bcfcb67ed514acd1ff76c7546152e858e6a7a3320e8a209b130444ef83d826f23b3305e29ee79e383794e027b32d3726ce5134611856c6e6c80c5ad63bd18d773d3eb7860e11fe75cdcf77ed55379f0668377edd4419506c78b86194595d92bbbf04f027f8d456d7595232fa30522bef470d8cc10bf185c5e1583e3fa2b5552760dd6c181b8f1bcab35acf6a50ba577ae4d790c4803683bd0b4c5c31d2eae08b3626a0d9018ae8db6ea0fa43f6b291328c02f30e57569a83764653b6c60eb306354baf94040f88ace7e038e6f1ca8a4dde7b75def5cee83793d681476d27c92088023fb6bdf198ac490ef74bb4febb43880c0675e06c37b63742f9ecf85120516df7b30d6f9a6e3403c9af97e024ba7837ba8e1db821c1cf2ae062a63cc79a7db78fac4b40df8e2d2b7abb25d6ffa764d15215e26c33c18fa0c184673fd1a58f869a3f4aacc1dd63225c368b9043aa3c7866e4f0ab415d14fc148ada00dd95e43d0da163ee93ef87e706910a1202560ea1e14e8147a8f9600ad6199e0406aa5a4461c633c184f5882589336064efae83992c806d22d8e9e65f7ad1d2462b2760f017b36b74af6c38247ffeb2c112a84e8dac58e43f2750d39b0153b6fceb8514055e2a9ed0b51cf02b7e804042ff496a9ff6a7f790b02816404465989f34b742f3e84e4a65e6d8fede9037b526f43941219a0d667c165b9503bf88da2f5e1196da7f86c4b2f63712ecd5ee2b750b22544bc9d03a32e2ba403882f27f44443dd1ca1c773e6492e288dc003652634187d57bc5616de98c0d0c3a4355e8ac6c57e1d2319ffaf1164a7b8fdaa642b13e354584873b9409f44494a81b5b2d71374cb3d908cb5613a4ab679303109db18538f2dc8bf9d3df22509f2ad40cdfd3f8652973f70b1f2fa12737df6fb875c4081219446ba5225232f8dd12ed02a71e96186e7e6129536dda47dc71e4c479a17bb39eac463ed05e3ba2d724f2f1016e221db6e6c18611fe7fcb1ffbe47797331b8783a720a80d032799cb86d5aae36dd8e1c1898e8acbb247c639c377d0afa0bece7b866cf8ad5091f0379972f369e3e8a38e99109b89bc86be78dc8a2c17f0215dea45dd0802bb3e3a531fcac5c84755d782a68848fead8cfdc58285360d16392db0a7360023ddd75f742a020d3436d0926e5081774cb12d7d43e91ce8517597f168964eba4792160fc405e465e6a42db43e1c8207c72c63c5882b0943df34e6a0b4e650e90347eb64b98dcc46c25b6415343db3714aa6caecbcd208b6b11474392bb097738003a83f869c2dc0383f7588685bc3ab4e51fd149e2c6d5681d0ed08abd8c3a36356c2b616dbaefd5cf4da5251f431ed1575e9623afcf05a1961c1862f9e196c675f2f99e8f56eb102cc4b2ede8f905e50ad58a552176382487164d9dbdaf0bb3f32697907ae173a6fbd0998c43fa2ba2025c810536d28262923f523b54e0b3a3ec891c1e113f3f8c0aeef75f34a54433d9631eb5353571620b06ee84e25ee12d3677a42ed45b18727c82d7cbe8b2092ee45d3692f5695aa9ad2b2032103b6a060e94f47f1b3bba0670d0df1347fa4631c113b101ce3cbf5fd13bafc5ce1262fc6f5e37c3e481dd816febd77f2e513c117bdcdc15b0f11b87323fca9dc97ebc2e68b5d06a57009b4940a9fde092b0656fa90398afd152ebd3010be9973c96548eb1b01ddec58b8aa2cf949e1e19229ff2010e33205b7e9ad800385b2a8bbea74dbcfe9e5f507108b1066cb59336b1b5c9772c2b5578245f333d73e46d5307daf9c35b2fefd963f04f13071b8af59b35049e711d9caa3d2c72890be980e5c111d0f2f59d40f57a5de0cc7026d9468f25a715b6185296075c85330a038b60a27fb166763a7209f58fa1cb5867574bf4cde844565246197fa188f31a8a9bd550965753502bc95dff462675834088cb982c46316dc7d9abd6a5038b5df1f4cc080fbe4cd3f5e1568a3599b1807292722f633a7528975156a7a3eff4295a1bb344610027c78ce6aa35ae5d9287338b0108d9e9bfd5bfec9717fffc7e4ceda0f2a61eb4d78cfbe442ba49c8c67fb65867216bae9d811d8d526bfd6ba9082dc5281a4bb67b8a6911f748a58271d8e0023090b3d34829e4cd56114b97fe4afde5018ac7e63e50342d0c113481e3313c2b723933af76e88c0aa4b92e96dd26bef2a8bdb90c3b9c03af4fd6387cdbe7ba6881743bd02d6ce2f24bd0d37b7d1c21717888810b9e764907641170b5194176be4918cf35cede4ff10d2e2b9fc4096830a8280c18edd039d3ed3c6a8a86f7883c10372c9f5dd9fc5d5a07b471356d696da3b162d2df2f9ce36b63f73674b56df39e8348645e25ce020f913646e227670dfab97abf1cfef039c4bbd91cbbc8623bd09a3b6f6865dcd606c1b4d85641787ce7a9e0fe20e88316fc4906ce5bdaff9e0bc755ac58b3e7b16d20ce2f8e73158d6b9c1bc0bba8a4b445e1f8e6ec6621a68a22de9d5a093ec344b7de4b4d300d2a1e8148b3c3a37fb8a4d95640fcb19792efd7338622b7fd45dfd9933b1d128d1623532d35c71ed7a904dc06e903426bdb722d2bd0f30b1d8c61b4ad9608452cfc935aaa73ffa6afdba4f0704edb2d42c38fd167f6a22ced8b4fc6d9ee594271536abd9247154f6f45cf743e1c0d5c4014c62cf95d40540e8d615e059390482a7724c518f7479c1e96d503fdd09e7d481e4068df454ec918329c74d10e850953b7e86283ec874cb69f5f504d13897c3d7dc2f153b997e2b5f061ae1953434e32de9dc11aebb632c9da9084cc46f99faed00dca4fd56cc1118e21b4b21e623b77c36dd5fbdbbf84745555fe3589a288c23856dc65ce4c7b84385a64e521b12193b4ceee1784050dca7cd1ed8ecd7dce597eb36631224ccb736bcd68afe4da2f6cda4d375b0ae8e9771e5430efcda9e02c1ce101d453ada341ef9837c24d0b0803306e708dadc9b64032241de5dc73ba03271793464773e76277d006f239ef459649491f5dfdf19744aa547fabe05fd62434525e91e6b3eb0a465d02dd6ea923cf37772d97b2501d0bef070567a8f9becb795a9acb718afeea112da12006135b6631102f85e92671e7daf5538e1c8691193be27934c2febf839d2c2326206a58c4a01ca76210801f4f94277e1ec24a669bc35f3be6e45a44cc04dd3f51bdaf18e983b2c89745442845a9a781c0724133b9ca34cb2d95fc0af8f1534766402d34461a1842b2e0006fcf5f7b779cdb72a460901241f8d44b625e138785fd3ed706f0c9af56c8af693e2af61598c5f80f64f0867214431bc6939368e9d3d030371e4169ccb26644d50abc2df67b2120a1c12615b05d53d03a385071c68569c20c590d964da7488eb0ef0886431de926e61fa7cbabf9aa0c691c2abace70369d12164bb08ef6221e5e602d28d4ed6071578edaab1840d8bd086f2a7193242b57cd1b0bc3b0b272d87152a89fd20c1c7d627dfd4d3eaef3289cf37b617f0593cb5a2280fa05e2e5a0df0498da446d57a5045d838aebe924452cbcad5b544f168ec650993e0dfebcd3231c6c0188a881d2509000572b51bfc65255dd5b65496067190498f1d1126ea33588f8973963e050f6538eeba5871769e8bd4a075314e6f9475877d45247d42bcdac63fb99fbaad0d9a0eaf1f355c55244ad5d33f3227573cf1b8e7a5cfd196d4b3ad8ac4a494a3c80f96ddd5b6229a88de0afbc017b5503c12b740412102603139ebffb073f700517b0d215c2740f833daaa744dd05dedc822e3b4e08146531d29f5b1015d2d353947bbffdc7684809d00117e06af659cf7d1e6cee177c41785e1fc8f6805f70ea2a85303129769a22a3fff2eba55506969b05cd5843d03915d238b189d4f16ec11e8ec5fb85cac4a62bc516e324914ae25c4b4af329bf9618d4e35ff25493bc669334c0ed90a3990c597026a67783d90aef4533aed624a95a6ddf9fb04aa5b455c896640b151b26002ee934c85a2b4d5d4a647726ff5eabcd08c855eefa14aa8c7dea97d7bf16129baf4c8451940558e18e4ae4e7f7e3b5dc67c8f515b07169d21fa64750c395523e5f2050dec4fe7637233f141950a1a92feeeedaa6137ea9cc7efe481ae08cbb6b54bf4f79b26dc876032ddcbe1dcc5c1a9aecf8610fdb38545c3bc2070d8ec3d9f5bd5d5075eccfa30a587aa032de35f86c664f8da56f11bfe336397cd234fb9aec5e061cf31726c8f94ab173a9da6a5e633d9435049248cf566aa17681d0d77ee86631aeb1b772aa1e6ad10e00dc67cb244e40a565dcc830398f10df6fbb7f161d8793a938ca2b571c3f6a76a4aeb3add80b6e9fe2c76ad96860dfae7a43f3d58ce98f984bce129ca608084eab32d4fbc99bc24587c97e3c6b2a491a9244653a5c7b431a843789294f4a24688769ebe741283e64dbd39dae9487a12383f3ada880201af86127e7ee7260be767565cb3b136ccd056c7a81d2414ea8e0edef8d9761665df08f65a6ee824673ca55ea1eccabe2db68d82320aa2a7ba11f5a6b9c2f674ead484b63d7dc313da229af009c630c48bfc9e8f6c3055e81c89fdc40f65ec003731c61b0877c5a63ecb00a673088e789c69e988d6642f120f5264ea1716aa12474a5cdc79c98913f6fe3cfbb631ee0eeb37f74638771a8746968400d778c717e4ded0ef6ec8571679655d6994aebf0847aef9ac91b6664c3604cd699c1dd66ebfefd554c48e23f1b7b8dfdd1812427128f3d46f71e89e0880e0bd9cf56de3ce72c9d63f46f9a6c3e8a0587391332e2b73da7718522d5e13195f3d7fe5306c2c017e991967e13b50f043276549abbf6fe1b9066a90af0e65cbdecba6bd32b6783fa989e150fa32649443c7f984d96ceeff4b2c99e49ca0c0ba7ef41cc210b358f1f24686d489bf61c967b8d6599e99f93f85810138e8781cafcfc72f0a6582611db57074d2680c226f81d63e29c984463af4e553cad1dc80d82df52238b1c225ebd3cbfc5b3d83a6d1875388a3dc39e2cb35186a5c3f725456c8c51392004ef311727642769a6e70f9f03283fda74322142010d2a3262f189bf9a3ae4667285e5354cc1360964fd05f464fc7e8f3fa65ad3f28cad1b66973fcde3742e53589252f3a7349c6125cd1dfa3a36864a56a5ca358b50963ae1b37e59fe47597ad9d485fe5675f0bf078715000ec0567795e3f8b2e055223c27b4d55bc83bb19af012fd9b837373e09a4a68bb0b05277234ced6c18f863b58421b939d054138dbef87b633d5bf5d4504720bfe1d91de9de9522098a4fef8f958b0264cc4a312b602aa886fe03a333f95a11d2916229c8c06117790bb3e7d13585596bb4c5db765a0a0da6c84233fd2dd004530438d266e0b793324f5744ec4ee9735b7351efce9e68cafc47161e0c3a6c072bb68de4f2a1a9dbb83f6a8556b6e74643f02361785a53a1d7599d65de875e63ef7ff693370d7de76c5fbfbd1a14222b0c5b16ff5681b4a15bfe3df630be22732992acd66b52ab60089f6ccf8e67735b9a049a3c18dca9ac14adf9bb9eae85549ce32307c960be2e987bf2199f8aaaf752e3b89c22af8af84c08faa850d1a385a95046258472ddc4aa2d883f687aa0da4db4eeb43b65c58094c42578f43478b71890385d759341b12b6b6a7f76f3711860413139ea9fb846daee910704aa7870f2fd5327fe89e17586dc85dc6cf7fe715450bc0a5296110de7905bca4b7851c2da4bc33d2c8eb859bfa1a7e2497ab8dc10d3ab13a99fc77d1f02c8846de4a28480827d89d09df482917cfe659b08c7d4087385cc0c6e9de5b42663a8ff72ed160245e3122c9ae367b30ff50708563ed1de68f636295a0e1a5482538160669be58c682a1fd9d7c0aa475a413dea2e209ab047fd20ac1d7a3121e8003980ec73d3ea04fc94d3c1cfad48df688a9632896cf95822967445fb6cb7c4722f96e85dc620fa9ed9f1a1e5022b95512ba27e0240ba16b7341cb191cd61457e157f025af336eca1a18b84c34581ace46f4b5f00dd37f9b35da4ea34f6eb28bbbb8269368545de3d2f9e6983f8431a680e639b183b7146569c4f58413a0736f1ba9c09b403a64c08f6c1b6d6043ba2dab0456390a43a513371a822d078d61146b04db7b7e3938f45a57761e21b8b85a1e4e932721bded90bd3532c2c6afb317cd21cf573f0595545ac4dab2009994a1da1c7fa79df5403614b8bffc3cbc1737565637237e6a3b74e9f301aa369eab7a93f53d54d00d017d5b5404338f364b03dc7159d1025d075874d28858737d106d80344335d242a0409d64228dc71c6933b8dd50d2593716df089b4d90ca85918222317bbe1bafb2310a1705e0f13800e522f98b381e4020b03c3a9d533b628c9b2d2d7ae171b1857bdb6a9d9c656ce599d11430ad7e0d581d0e698693a4deb66af757357a1c14cf6b99432981cb3e076f4a7ddc23bd4cb8f27a50017ffaf5cc051cf00b32ba7ffbe9429c8a3c0ddf937515b7c78e99099c724f72cee61593845a470935f8e20a5be43545bff584e7f6d34591a656290023c13768f99a8e084b4f2c1d65e4ce1cefc4e97626c2e8ecc654fc5dc3db4c12307b6d38a75c7b3bfeb15d4adaba7a55f21a15075ca530acdd49f648042ed22618239b411e34928e3e9de2730ebec5fbb7ca5df7c6571552554e8e9e5d1d6249e27cd84613578de9836345e61da295b0c0d6ece572f9fc3a7f4307fb9811266d1d09fd39a6abca98176d0de3c6dfed5be3dd30300fcac7a712962398cb9fd80c9b5505a7bbb644cade3b1a2ed55a4e10ca38019da57dc877192a9160094f7652ea66d50cceb4f3929a7aa6bb5be11c8b53cf0629fc29059577ef61255db35e01d8e14e73bf5c09122e9f627e76a9e7f6c759646f6223a24a698c414473876a0e8d97f2dccb473f2bd25dae8a828ba6dd892f46f3faf511ccaa780c1f90105a44ef9990018348b58ca80bf9f692ba1633a9119743d0ae63db76c77c28a44ba14bb47174ecf4ebb2cb5c062678236fa27dfe046d5bf8386d6f781ab43c14fe2a3f7630081362b46e3f2e6d6c4ce15024dbfeba287c73bba4998150d297d5468304348dbc7529e176d26d852c31a63302b4c1c73ab2b889d9a7300be1c618adff425bc48531a22d322b6f0f3953b9312023f01486e103a81e2ff9ac01ca6b76fe3e492b3f4f874b41dae07eb3b26afad246f5c69652cce086a3831fe2e36c72bf3d704497c48719014e2c1c0696740c50d63a2a87c45cc79bda3c68c6426277303a08c5efca0b56b828271675e147f255cec598529934718119fba9f689c0603417f1c8bc2b27220a3cb874b5c417d78d389019fc9131b72caa5ff33a79a201245bb4d15e28736767fb9ba5186cbecd0153a2ae206207d916affe33a57342bcff6dbf28edb59cc81025e56cbe1c9b5a9ee372d129b3fd0d91008938fffed08a2718a935f1128ea2780daa211750c69c5cfcb8cb3994ca8ebed4909b521e02a32840698d90e09821a636089349884e225d2fee2042c209601912ac03ca76f650505192fc4efd9c14c1824d2b56b74e7df733df741e13c4c0a140fdecedb6c642f52d14eb43882153144c967dcf803c4e88be2cdd7dde63b83b30bdb1ad1b4f30e72ee3d4e77089646a535431460d6789402ce777b3cc28619129678472ff037fce5c96fa049fcac4e4d640081a0752bb2471bc9832a23b70a932c765f2c81e34983c2b6a35e66a47d8c14f94a4c8c7bc9acdddfc2e1285c1bdf94639588af2d45432996f309e43d26e653f6fff9125e638302f53e13af67c7b97d56784094f4d0b5c0de6f38bc8340baebe2b6fe4b4aee0f0b2016bbdfefc00812c65e7fbe6f90507cce1bdec7da3220259dfe09e91574e2878166d7ef6223b56f085600b21d17a3d963b9a43014cc027f177290e0880cb506d0bfb584276c9762a0f2c114097a74857d951d2619c5de85206b2a9b6acdfe2f1a8716a08365776846ccfb6e1d9634633194a230d5873a5519f22601c4bb209ff47c1f9c3a9d26ef04b3d9c6771432f395b6d9ea245ad26399001cc3b7a598e0e497fcb516a765c55e0315da68cc939607e05e7dd370129c5b721ae739c21884f56abff0242de89d415ea59248bc344730415ec2839047d40a10ccbc6308f960f6852e21b861200e828e7931004a8468593c2efb6f9ea636ed7fbcc9143c42a39c8f76a6b6e63c528c53a3e9d5fe25017c0f866f70c5092b8b514653c497962cbf737ad640ba587580437fadea3eb30905c8e62ac4ce45d15bb804e0026cba1fcb3b6fbd7593ca4853b5cc94293252e6a1d799c4e1233759f207bdcc8b6fd9067262f96d692999694cafd755dc9cb2b7caa1a6c839abc3e06e4ed31b06500d5e4bbd695bf2be285aeb0b5ec844acb11c4f4245b5fb134320737cba3218b6fb9b6ec42da450ce4245468a194d3529f29faba8ddcc44b8275c381641527d957e01575e4ea3a5fe7d4a778aea9cc63b36f68bdbb3b26205df43982f8e6ba6589c0fe58bdf2375dd1a6f52088af53d9173ee4f685ae1158f53b73abe4523cf8550ef47f08fbc254b2bd7a50c6f1807e96714256a0a1cc19dde6a6f48adf715da534bded53b0f537444d837cd7d728f9ce3ab3677d943d10a78349576de0f11de9bd13739b8bee163dc2a3cd1f2bdab58a00599cb59e651cc05117bbafc6dbb67bab02d6fcc9c29919a8ee96dc97cf51fa34fbe36b753232998c8e7795f3ef6b6a898528b76015fd0aaefaa046f14ca04c2bf0bd1a9617653f1eb78caa0a172575d45dbbb13ef7f1bd1efc8dccfa1f7279731388a3893a7ad1969bc0b76797cdacf3247dbe77b4ea199b227b4615dac21a3ad5146e8722ff4033c2c90f99e589abd874c219571d7c293ec1c252658d9a3f50fb646e2d1849606f438cc5055b394cbc347a52f6bf64554b76e05c93ce27dd7085e40514fc844cede2d1a6679dece35da145f8eb9a97e7e2c83f7e93857277682c46354ed0d01dd919242aa4ede16c1235172d3520979e8cb48b757657820940c8937c12bf538877c7af5c50feca4c56d55a3f1a7141dd2ff859ad4aa2ffe7593409c5d6bf9fbf4a48a5c7d1439bece57816bd813cdfc85420310ee3b96f3d265fb760955614bd4fcd1d138ee1d371f9bc8998206fede9939e8de674b0a4d73b74a74d36110b035d5de4449d4b9e51240d4673e75617756e9dab7c0244837f59a1e3538ed86eaba5aef70afabece95f8994ffe0df45fd0ba2385d79b3e075a0704044c1a6f53ddfe571e22d4bfdc5db6270d44165a3b172aeb8f9ae56748e56992c07e289d9f0891068f50f8129dba0cb46d3b283e3c86baec28344545fb7e15ae6556b9717faa6e3b2408fa721c82985d5c5c0360f3bbbc051d5eea2f741f6ab2d325644ee67a5604b06f465603d1bacdd42e535c1c8849d7fb3ee93780821b1a59352a54faf72027b843b0a64f630da98ff33b0609c26bc5ec70697dc63c13da4334ec512e4d750844d826c2fc0bfeff5a1fe702ea5bef77fdfe2beddabe70d79eb7258ab09695d76b8b066f2528ea12b1e597c6a767249e639ece8d3959526e4d3d9ad6669f16879c704f921b219028530e333816d9bd56ec895494ae24417eeed5e3727e371164299e5f6979dedb78ceb91cb8adeb36da446edbd102f246fc38d9868537779d9d9a52670c48e33144d0284c0a0ce9137b0d455b4d6ae5462da725d52981b741d1c3b445983dd62ce0fb362e560e0bd4eee0fb23a77e186e21fc7c5a5d67d155578ff004611b2fb474083e56bd1ddb5bcf4362d0a7f4b118029ba914d9737c8236acfc3175a11706948fdcc824d94650774029849c22fb09252f300f1311d6fd4d8a0e1a9a55a5b5368bc01b8406f88f04f596452878515c4c8f986f5221196f98b1f2c1202b6aef5d19e60d1a2080d3f5aa3328d3cd125ac932dc99215c0f326c048a3d353e775ae45860b47c209d3031d825a206be4d410bec151c66e96d2e20e5a1df367d0bb81bf6dec88f18b6a5469480a71eb5545aced9862a39eb10b52255cd597d9a9f483fde97a2efa08b3a840d1f44a6e2353b78254538eea3a406e9fa234219174b03a524fbd0560020e8bcbbff9831a4fa04d23ea74ad639ab5a9de1aa474f2bdb9a9049fd88e9973c85d3d04696e6527d3ea4b5db45feacc6f1115cb0fae895277e2db8ba9681230abe6f72ec380b315b01aaa4d4ea1e639bf419ac35149e9c53b0e4202664d44b66724ce2fb2ced68cf35bfe1091f6005fbb6c4fd9ac76187f19861ad03b4754ea8d714220c4ee6fd4117665f01857f4af89eb1ff582582396974e774b02dfb6acd2e7e9d9f021080b42184dbddaaada8e4060eb9d05ae416220f4fb343c1c0cff525ceb8047e3e36342e13398d83cc9e09a846300069ee3163e35d939d8be46cb6d819f56d2715ef941a861208ed7eb99681fc15341d93f3efd2351e08342dfcb343171576ab168997405813984d5a3f887702c1371927bc104f68e33e72ae7ac42a007e78943b1c5e33fa60b67033455769827010dfba573646323decf29221a754809c19d2fb86d3235f04212e40a2053556fd374f595f2f112070fb7803dfcf944d5d0a7152f26812ebc1c3a59db9e1b2745b058c6376ed03d4ea06ed2aade0b6dc75d24dfa9b0e0e32cd774b4058c7e7851251d4d2caebf7c7177dca91ce4682b5f4d52b3f46ba1d96d7034a968169fa7bef5b163338be02e407df80876166ff80df050420d7dc2e883f4011e71dddf3e26e2c8a06344dcced6fcbefb1b7275f921768ac89dbad2bb4b9461fd6dcacfbbd45072adafbf5cd59fea8296dcc457a247549f03875af2e6e515df01686601b78a0242822600deb461f36fcb2b0db3c32f7fbd601cc9a00bd94efa45ce983772cce88318cca919315b7fd39fe908470d3ea0e16be55f836573de5279e3765e8317056c4381401a44642947112e8614a69df3ebcaebf7e363ad80ed01205b5ca84deb9674e8702dee1d769c8d17ccfbc05b204cf7033020d7bc1aad57f2d02d950b9a1cf5fd9f270adfc745313e6df94e80343890d485936336141becb48c64e52f71b81495fc49f26077b5fc2c6e59e6fbe504c6132451bdeaf7ebac9ab161c216633c38953b076fe9152cbee1c84850162cb152788f90a3b5dfbb5ed08f34368e902cf5735c8780b85174f7b7c5e62d7dd158e5b8c9f4a0c88f6ea4f99ddeac1515110563e83d2418f84df092c585f6bafa3713a0d37006a0ca8bb453514d52adb0ab40a7268d95c0fd8173985374a845857da6a11b26e226ade6e2cc649923acfa71bcc4c851b879134fc60c47cd04353b6b0e20db24ade952a9c7c6a64153f29e8ad5fcdc8121c19c7c19b62639e4f8021b2e69e4f65838cc2489a3450fc5f99421dd2e405cde2cb4fdc3e498ddced937ef8cf8d7237f64d53e360ca40338870d6c0d947ccbfcd195674433c06531bdb3276d7db7a5ade3db60538be9031afa7377539529c21c25bc72da11c96a8c4eefc4c05e6382302ca03f9c1f79f3025bb87d2283e10f9763211d33561169c8eefebb9368b1c34233c8d4d56b322aff694545267156d0fe751ef58467fd9dad1264222a14c1b4740a96ca4dad225296ed595726f72ec01a07df6d89c6f7016abe717560ca100438e889a93cf6ffc273cefd5a54ec821e3d9b0fd282241ba0ff77ec28ea41e9d4f847e90d993121559ee3e537fdcbec1a14b7b06704710b192c67df827df93331e6122ee85ebd5ab68848ab03f1059b401fe08186185793b4ef91d82670ee85d071e63d76ecf9e2b1c3cee9d42dc4ab1904ca672515887ef9e0e73add56245ba146326b89740ffb5d4669d48ebd40b6fd27c5d5cb017b22b0eb675eb71f775140be9f44732ec4dd31b96bf73a811ec601ffd89bcd4ee4b8795fed6e9472c78794a6f4f2c50a3edb9fc46e45bf03dd051471c09fbb989ce22dc440658b2360367636daa70738a75850cdfbed19608801878db6d03c45eafaa11c37ccea991779338911397e85388dee63b475a382f629a6cd1f2dcda5ba8c50c832d8b8d5834ae31eb5b0af45c3577176637c0f92e188ba9a0fdc75a9c0606cbc08b70172c3af54b99db0c1cc2f7310ffbffa83d38cbffc87fd285acd9dbe3f4b4347c1c68ac730b81036346595d95032b88146f66a4349e8b82511a5e6d7ae6022e70eff788e76162cdae456cae81cde30eb2795c387d0af950ecbef530e343e76a764b666e8c365b1d744f5ae5f6147a7d7ef65a601d2bf97046307945e0f70487b987f816432586043203953991c33c24063ee1eff6f84596299a729c83c1b08655090976e903456a952ab4e6aa07e0fe5cc732347f57bcbf03de614217fc719f56e0e8ba580fc4b66536692168378c33e11740a41a94c75b40c0e5d2ba19cc963fb8dcd74972ba92a436c835f3ca7608c19e5213d47e61387b8e6b6ddb32173e1bad23fc640664ba98cce41cc7dfda8875305025a4f98181256175c72f123b116d8361cbe83a86e8cbe91a59ef34a413f63b7b0823f5250535e6d7637fcda46126a0a27ed4200311071be5f5d2a57a780ca8a2778fa4158bda10c0b4ed356274776a0803fb714cf877d06fdf3b0532749b23fcf9ec1b11ca70431a606279f0322e66080828413395007ae6b013bf513075f67c3fbe4275655beeb3a87beea4be1f2f392cbf5a8db2c34ab15e50b79a1fcf5119a8286e78f444f268675f2a5b9b5c2d24eb1152222520173a8fd33ddc7d9d871de247582df5e6ce0816eb34d0bdbe8d81687b608f458bfadd3eea1fc4f8568111850c7230e4d9f7a0fc9ea88f4c677fa118233bddf492f2aafce44571123cfbdf89edc42801e7b3bf80ec26546ef8c31e2fca1ee41e29355bc2592a9f7dc46a6372a9bdc4f8f0acca79ed634a85f9fd0ecb8937d9f73f557c7a4a50b5a54df30f6e680d5fdc04892056b09be7a79ae55569c115c76394fd38de15ee6d4288b5ed0b4c3249920213826af442c6af695bf58ab96a9c69526f823c0b66b2fa9514ab34d6f47c7cb4e5a5f364cc43a3ca307b825e5aaf7ee04fc50feb95efafed374322643300c909c6428036527fbd48fa4c64af4399d738af6bf9312747397860e970770875e6c8e77ccb462b562110c347da765fe73b2a9663d825dd137cf7eaf1974f29b3921bb616e53bcb868a3e8d7c09051fd542e7577ee182fd4ebea21b392666c8798003ec06dd3f8fd770e47db9fcc62613402ac5b2c82e72b344bc708fe98d5e15ea21dd475937eee9f65d33dede310eaa4c5c27272e3388dfb443060a4d1739cb3c711bb2dc23a7c0d9b0f282ea255ced584bbc9cb45ace3b72d4c730c33e0fb0e8419cc2dac39c827b403c36351816e81a283e79d9c0193914060c9334feba5112a8b6b45a7578e4ecc5ffbe544c3a4f3df38046f30fdbc79e084f67f3589f9fa058535a3a041cb0619278a74575166d421cf1b05bff10d11e785955156939f04a186c4b11ae28a902aa86e797a6619d024ca5fe7f2692946967b66dcfd959f22c34ce7bc052814fb4be3937e1ea61fe0026a20b1813aca9dd15b9b7f08b867a2c5e348534e370eba83d2d4337893116ed6b42e2198d0ce7425fe8099aecc66dd57ec754ee5b6593e06f7f569d2956b9b6da2e85a4800b3079af9f3619d6791c002d5135ad83ba0eecf868eab939299c1558c514f1710c216628bc1090bf12ab9805ea26dda342d5f9814307cec113a7b52e67d2a37e9ee38d7f9a357a87ab4a6a6446957a2be19a6eb4b0530839507c4bd9f8d300e8a4ef1946a1178ce915291d9e9e7a533ef17f562d81b927f637fef52b432691240f53918182019daa4d0d210a680a5d4fce325bfcbc432d091471272e403207f685f4da2d8eec214018f348b3d338ddbda0cf5c79f5d90aac33ffd228f4ff5c86b44f0efd2343f910ca8f99a9f106fb9919327168bacd52b020bd222808e1de5b2f6c8d3712f07fe2870eb4bc2d845eecd7fa72a34cc4617d214303fa2a2f9478ec52bdb07abacddad27a10a73e54673f257b4ccda155f48111975d338184158d613e76d5369d739197cde73bfdd94db71e86b81bfbc675a01fa027b559eb3fe7da004aab8c932d1c33c1901623e145da304d84640a6cf2b00f5c4a79b509e61b2688df926e58bb9db84a61575eb40ba9d35a0e290b0e805606f253481eae9bafb31c99f983aa0fe67aa0495d34c84d15cdda9f0e7cba357142a9690eadd1ef397664291e1f532a95412ad9283f3c39f5b4085e31fd8a20c818939cb7e8dbbae0cc4b7d555d7b22d8352ca4fc914a657d876ea9c3a75a86315e6ffa3b21cd8f146ea2db398be5b415f7ab8328b2ae9a35ef4d734d390381cd3c421bd9ba6a4a777e8965a1f461b17a8c95ce3e119811eb95a26c0a91fcf5fe085281abb82f74a78206c75297c1954f228ea15d85b0fe3d805fa1382670aaaa54d930a33e2f210d607a44da9beb13c094e884c431fa3326825de842e59049fe31754ab88a78e2ccf852d082b997b0821c1c34739ee73183cd8358997009640f6f8adaf66898cac5efc1095c86c05b67e62b5e4aec437aeecb83ac2f555b940c24df08a5cc6e91ad2852018b49426d0f0ad28aa82c69c3f16725d7538a6cd91d318668fefef0c52d762eb60b300eb6975874737c5b9c86a7cd4d4002201025976c2ecff8ef2ce084704b496e425a39470a33f24a37d9913b6f776cd2f4a61a650da810cbcb1a68bcc52500ca4b34cf4ae8d0ece54564030e3f414803de99e89d2e39eb8583867a09822f9b091b03c03df87099d446f44c7d08ef0ff6b7ed344501c2086045c27054782982dd1d78a17d2270b2ef204b31165239fc7d6b35faa86694c8d5fa76697d2762ee996d0a1c3432decfd2d41e17654fef5822e608e05c220fb7f9ae754103abd7f2582c33a82ae3175c7df552cdcc7d8e92e09d104afa5e88e30501f4fe73c2eebdbac9ff58e529e246dc4d3f73329b1d4188649d1a3b466d6b75410f8974c2a71dac381a5b95409c7ceb9d2ff31e1d6fe148958ba8b30ce2ae11bfff33eb1f312dce4d693009bb51d68d21440b36e5c8b7fd42123ffe6ca58da49913cb6cd77bfe9162c7c9a977badae840592bd221f3208778d419f511823b4591ea7679ee891db69497ee3cf28dc175d7f3b485b94766b26a0f3c503567e956faff63eeba5eed557dbd5f365068dae695719c88268cf889bb2d9b79369e4be87381476ee510e4954b30e04d2a9d9cb8964ce1e807795caa9ce4be961ee35f63804b18bad425fa1888369d1ad30d58ca9471110d920ab1bee9f0df900b3f78fdd19c2970ee61a0adc81deb98dd481e5c4a90f101154680f4953e187e2d782243347216d91abe3dd8c0025a69b334ba9eec6efefb0272b20e224dd5075fc04dbe5445308ee2ae2b46a99d22e3f6cdfabea38d6b1102c89f9d6418d88245c42df21a1e9046cc999f4c2f80343f3ecc1c9d225282090654f95e9db7a873630d9b97bc038aa1dc63ecf11661c18ea8c67b0bc1f09625b93f876c771035e61786c47f2395bc5c970a8dbe6f461374fbf6af9ee1c8965bb895f9e9e3b26ddd3b8ef1de17429b8d8538d9be51080ede45af913be310870af1fdb0dec69c6ad82a6e81ec4926348389ee4074159ea5aedc5a28bd6e522726f1a9a094ebf8ce816ac477c9e9b414763a21b0093a7dee64a124ae4525d6307371f94646ced41d80266e15a5f6ce88dbf22b5290123d3c7eaf9a9dba1ccde420d52e583326b8b6c42e791096f7e87928954ecb1472bebfc3ff7f08216ab3a89754fd85b7d7c748781a019261aa2d1ef30620f6807d0cae62460cfb192620e13fcad6cc184dab9ede3bdf0501b6b6dfc74dea6e9c43abc68565ffa1f8b7775cb5584650de3fe231f63b6c36e1a9d6719cbead0bde4820a54a5263f84a3b90547ccf98ef8729afc7a46eb79a80eb4bb5e7446b646416d78043ae04a360bb26f3fb40f77acfddd22b9d3a34316c3974e10be8a8c490183f2bcaaa6b655ed834370a6ef846f6c550cd8b4c2efff1e710978dc154de04ba2342968bb22d639aff97e48afa38e54175aa4115daf75325a2a295f8259d44128401da4f5742565c51738603ade077a03394fe4805611ca82972af5c5b0aca978f091113fd5a3a9429bb26428fd5740319ffbe0a54cffe7b67278ffb780fbe1e5959a93216827a0b6ad96c458d1460e5c17277562335b36d51280c3adff47ec6641411a7fefc9bce90c6337cfa63b3a381b0bb5f648e55ff9ecc17231c01e53125e16d9944c0a72d0fb81b0936b018183a801459b522559e12794ab0ae345c78361d46bd040a1c37df32f42eeabf2d4af7800d40bcf0f5e3a5209de77c3573ef0e533d62367244d013fc1b3a23c122ec7344ce6da3454ab17eb6bfcbaad42c73a9f1078d1c59c13a0d4bd285d08eb59ee5b5852106b562e3c5f5aa79e9fbd7355e982e5f3ffa31f248ab5dee106b35675dfcc271bd3012e6e843ff41a7dd9489d62703bd9fcee61c21e809805cce9218c7aefe52c6bba90ecec08b639eb5f7cd89c4c9cff4efb537054b8a689c9fd4ba0e85e566befb268328bf679b2638fcfb5ba02dc03bc7604738e937ea59ecb2f24fde3e0668d98729b655f457cde1a3bffe0f7a3f0252ec147b28c0862351c3ee57687935500cd79fe3c3bf9fb8b1d269b208fd9b8b17c69597a75f684011bc413d5e701a98b969b3a62f26c01fb8be640cbd7b2b4c4ecade3092bac3a1e6d0dbee2099715f8d5dcae3c70f971646f4007e680a6bc1e89c6caffdf40af85c400738032425917ec795551bfb81b4fb5239be1bd8248adb61278d31d885d33653c1fc9d9970ce53e4d4d600167c885ff37e78ce0e8a9a80dc794d713a41ce6d8b7e7ec3ba63d893758ad568c519871ff067268e7250eb9e42f1a5f71c5cfcf8cf39b8db225ae35f6dced3281d516223912187058267e412817d87eef2f7bb2b44782317f01a26145fce3f057ca09e3204e16aefa8d78777354d395a5fa7278309092f05776331d4912c4e1dda629d1b6c5ef7f3b9d0754ea0ad973dced65629e26578b41442cbe447192d030280699c861e094e137269945c9102427b233060e4fb1209d7665e36ae79e4cd37c9da59146db86be0a19487f5a9c61f6e0a3078ddeb36690f116763c3886f22e33f941dd4a3840134213264f82993c5021c9a311f054f9368450689b5d96daa286123ced4ad6c7c3c1f12bbebd4b32306d29082beeaec64bc13ebf8b9115b3f90f34e4a066be3d12e1cbb798a6b4b3fecf920fae28d20279b4fca5eca1ac204ec2023d3339d0d53ca29b2d5a6c23e7ef671897876deed954141057688165fe7b35a32e545472266354da10d4f817a9114986b7a11b5c465c21400a866e77dc15cc9bfd22bcf3075c1f70bf2c737e0e28da4e3b7d29885db1108479ec61a37726cf371705ea39dd2beabce7bce1c5af9ac6ffef95d366b1da7233e912837ac1084a86f21df06642c987cb3628c5aa3c3cc37b12fbf9df91df31b9fde91e7b41bcd842677cd4f42976a4e0d554098b5273e5a48377ebed03e4671967ce51c31a5a699695dd604334a060a94e8f23f8c80d8de124a63d3282afd47c87410b2186974de40028c5718968814cf3e8583dc83c5da694e1c8b6158423ebdaf62c3340576a8c5dc1b81c46e5afa7a4135a477d59f4b4674e32aa26d751ed3443ff4c566433c99560d12ef78f1abc58da244bd5cbf0db047d3bc46fa9041181c4ec1ec32a6d6804fd59c133c2230833a7c046b6dba629a8f23b3e110db0a80202eda61d13eacca8a6fdf7a09169cc05a8daf49f78fad49d6cb7e14356430115ae2ef74345d4fc10ea6b9adea1a87b42ac688606cbcc62825ca30ef6b2f71c31564823e61e6b146d185760ac76ba3577e23842d5308abf26e1b475d0c01672d2d01dfe1b5f5cb3c098ae82960be27635e7523b3b98b0b08055565066207606e710e93d3c68570ea96fbbee6b2954a5c56898388d0e7c0b158c9155eef2b5c87c8d86ed8a755efa7c041a753f9ab35b4883900dfce21e0721fbc2001043b33b841f884490a741232d766de9b733f30f41052c52ebed33ef082f4227bca2e6f598205df10719af7e3315305aa43b66f4dd314244d0d37b2ab6c299925aa7567009a6e372a7f13a330f19884445b9d80127b19efe717325afdec0cbc7627396b68e5ea4c9f8ed985e2400fe5c866d968e3c2727911e6f530ef4886d1eaf66131e712797338a11f7487881ff19e91f1beac2d842e55c211235f33520b72730318eca9b78b23ff75ec67c6019740b88d47cfa8d78a785c37505f60a72ae80adf69312826060f423037e6bc464ef7c98d20132cecd05747051be2d039f0d1867625e065cca77dc780adf239731e41d874db742ec30a230c00e30fbf1841f865135130f0577b0cb15582dd2c9b8210b015f0052894bbe158326c2e0e25c7322171565d67c3bb975b4c957c7fe4ea9043578ac160a8366f330bf1b2f4a3e0312c2a7062db46b514aa671730b0f836fd4529d20ed3f6ed5ab9874440e38ab56c959e0255aa7d5c31a7f1b8442446f03ae7e01e7f3c3fc1df047d630fd41dd268a8140e239d785001ed78709f3dbb0c03cc9eabf652c54e2970374b29f7849ad88d5553ce4a9041a070e054a75ce3deb310cf9e27fed4eddf2c80ba9e66e6c47bf87a1c8bdb5e8fa60835ab12839d92117eb89e7a8f78205ccbd1dcba0490d8296b1f14f705b2fc8d95ba598718bdf9785bedaed12b2e74fcd89de0a162f883e9ca186abc65296cadfbdc06139bdb4742ba8595048b88e424836c8dc8e6f49d2bdd10595977f8d4d4ab658fddb2bcafc18dfda4a177c55cfc556ff54765a7f90756315748b3de52676b4f0b25259fd7293aa73a09e5f10586c14e3b8e155d4a1fc1ca4e663b4d3cd1bb1ca86fa92998a8f1d3e506ccdc6767594b918ee312f74dad303952f48b3559d3e7847d694f734a3ea21eaad82df776dc3614ebf6f40e7265e82d5cd13af6481d83f670d0ee73bcd5666cb6f551c0ccf9be49f64e64a46b4c5c4696d1c31fb83742f960526aeb5d0c98edcd3d26a73086dcb027bc59af9961790611f46f0a44c372bf1a1ddd2a9cfbf80feacf347d6f868f81f6088c91c68c7e12ef1da285b89661db9e3d956647227342fcc319040809bae1e0f387720e12d40a31be8b0fdc371ab753a1b1d6f05242e85576f9e84b2c274560b29ab1a10a6b6b9a2cffca5fc74ea76364efabac604d1d695add2723cca2dd90404b0cfc030a3a51bf9d221cf363f0f4a5857ee5f0f4896b1cd03d21e0d36b1d213726cd04c811958e7fa68a8ffd8fbacdb370cd9094274e498956a3c2efdc5d20731b3ffcbe316fb08426d1709e0c1eb4be1d63e59b33c9f64333184114b0f2e966381630da6fdcccf90b3008c16c2c88ecfc5d4605553e9b8e95bac7bb167b1fa7ce3d609672b925e13ce9de6198ba07bc4e1b4e7445e590e053cfd2c46b9fffaa2a02809b1a0083503c4097dc8601132772294638a45322939b4694001ce7ae1b6c98a2833a6ac911c1220d1f6453d11ec1593f2bafb4d381f061a5f2fa94bc4c31ae6f080bc233b6fd760da96df4e510420a2bd03177d70378171d541928093ccfa6300f5eaefccb717ed53bcd46c20e4cd6f82ad46d06aa0c7df2be5570f90ffd0b0751151988d93f83ca969dd4bea60a2b9392b5fd473f67415577cf0a259eca23661e01687053692e89d39bbba682075d2d1c1c26f6cfecf3ca700271be98e01212b026994f2d394da9e740ea8aa398ed9482cece85cc32067aa5aeefeade2cfec2bc147603e61bdd4c8f27d7ad6df7132ab896cc89e14a3b20af23dc1a0aa038237c236a9f986ab4f87927abe2bba649020e755af94809bf980c9d88b8535f8473a12678eed8f0a6af63ddddfbd4951ef12bfed0e25be184d06f1f1e9e73d853c3a344d68a38c0ae6cdcba8a74c085cd8419af7798177b032e5c51639c97da398d13061e4621a02bd0228536a5ca26bcce3e5c8c0914850d74324f3381b34049ba061174cb90fa59719b86d8e288ae6ebd28c896348be649e414b691fdd173cc3fa04d4947389a73d1cd4b4e7e41f8955442375520b373d36ce3806da191d44c2c8a1d4fac08bd25abd1986d6071e5f538e3c75620e23237824eeea0221791a250c91ea7d76cfbec56aa29e928e300114b9a4154c7432f156829b5e422a53daacd7b3d20c0616a5c3dec2461248c75bc0d31d71afeb6228d62d0e9b23776ec3f5ae96dd4c5b0e07a762c67bc6ca7978eb918f1c1d2b2517109c0bf2257118822587d1a8ad277b1d98a818f78df6b17e00ff72820af618c50294921b03f0b22c2e6bf46e3933da2430a1b0d87f00aa95855b75712709bbced91db93c0a31eb2f71ae342611eaa528912c7b509b643e73b9e74b25c52bbe136043f3d174159360974db45ecacf59d32b52c68cb394bf25bcab7e33dc77d670cf203eaf20db762fc76f36f686feb8938c91b2ad36e8438983454e1e75e6e100d5f1768c7e83955e76a7c64844d6f997d6acd154cecb04abf25a5a42cce3a9ffbfca456ebf46434bd089f44bfa48fbf856bca2c18d0d8285e1cb107e72cb55ece39445c2b553ed60bc44b2f8f78671055f5d4896c01bfbd4f7bb11b02acef67f385c42a8582b22665a0511b6eaa431d14931d2c08f15616612449f3516f3d063844e4ecf94262daee6f913f7f74947fda74ed38a400227945d2c4fef0adb3d03aaa9af2a788eb318d547b499a620ce02786b4679d972e159e4ccdaaa107a90859985fb0c1a6749d0e1f676d00717203b5c861790ba999dbfba71f7557093f64afe58ea9dca825aa120d13234f07bde08099279c22e216b28cc823ba1762957cbb0cb1a8c2dcc12cf4258b47753d3f784a2889816a2dae7c55ad4c3bf0a1f5403dd9f515111430e14c16e546b1807e5830fed4364a7b80595b150be4780f7db6a2d20e0f1b68cabc35e0d31445016050e3e9fc874b576f621c69bec9c5981d650fce3fde5eb19070d175613d5a9085f9620f2c0fee85c98e47c17da567c9b3bd9d37a6dfaddc07f658eff54437ee42bfac620699cf2fb97110208e30c656728d70c993dcfdd795937931f956eafab7793b816082077ebd0f6d6e3bbc18195f9c05acc4979ee1c68b23d50a315990ed4276b9e95da5c9bcf433ee30222a969f2b70278507739913f963c2b42d7e8e6daf6b7f7f5f01fd716d812608ab1faaa16d04604d1f27ad246b578e4e0d5e4897f94c732092446f37b7460b92931feeb9eb9912b1d1d0f20ada4b42012f35392c1f3ef41abdd12721404036228c15e2894c2220d3e9adfacb6d82c2c000b623bdc8293dc8165978bdfda925c4098f6cd5c332276e06757cba7d68b1503c7d9c8c62a6db0a299a520d0b18258344fc1032a0c467f8fb2513fa10e649ff3f4a67cfaf03b8affab124459e73c7cbaf23acbc235132d4371521e680d053846a9dd7c0a92988a3b751a759e5ef121b24d8704902f048a4c2ba7441f472b3f639eb09a11c14931c2ed783d2e9eb45dd1bf220e4d6a37b086382005993016fd60974be7300cace96b50a64448cae291280365acbdb90c0c101f88d1038b9dab232c81d42dee4c5168dfada7d1967ab5b62ec4998b4f7b1deb36de3eb5b7b1d3a5b3af2c1d31de2e60cb1575360abceccd9aa07c71de362fdfd6daf72be2d967e4cbe01914d20d4569b12b9dab8689bdf5aae2e80ab4360cf664fb5107ee7553050a9f72f71531d4e9f4f2150b528aa286145073ba9adccd8a1de0e8a33dbef690e003612814c67f2e923caa8230905193bc58cfd52bb8b1f5d086b9c88b264cd43d2cfe4fd2fbab87b7bddada6701da3990a5af6a2bd8386d1174c41566ff41265e826e442c355864c44fb02a1fb22a784bf57dad051fc1be4573fd71171f94d12fa1df1b6960a8dc45a5266ac05493c58b33965d57b1cd827fd53d8942ad75207cc810bce39d4ed08775f27ec174e558df38d7174a482353a3b8e0ff0d952ab6f7774a43cdcb74edd3e7b5a0cd3f211e1ef2b3a4052daac11503a251b76550b10ff044b7f22fd4d1a0322f6dad21eb98b8527ca5357e4c769deaf92e3ee548a01314f25b893151ef52f2126cb11b637af32b34e0482af0011287b8696d0b2a93a7f1ff18f3e442c10257cb009c9adbdafbc3b7e45a79e4df88ab8deb58b3d5423fb3f83ec58a3a6cf9275d6fbed578a88b9aa3a33275abe164c34a607b38f9b61391bbdb7312772107e7c231be0b2026355b3b5b0c7f4abcb803e655331c378121fc90db01f5b2786a895695f4085573a8d774845c8b199c6a82c1bfcab7aa516579287ad559a3f91769dc72cd8e2f10fb8d7a5088eda60ad5a357cc6594b530ae2d2cde95568e6d3bf81c51680946f05313f424e1c4b0c778b1d307bb4979f73ce5eef92753a58f028653c7e4dcecb033d8eb8dc9d70ef9bc81322f29ece982ee47e5ca8f209c535f4e16239abb3191eba2a0785a418e99a9f9a7386a2c01d2f89560805ba8b1ddfe91c42d8189c96da74e00eb09aba960b168ab09a411b915a9ec8366d275db70534bfdae437a459d60ecefc31a1fdf060af8a338cf49ff96f01ddbf8da6a507f6db9294d48a832606604395fbe21fc4364bd1e420b99132573346403d0e481506d17a5775f968dc0e969ebe2f7d1413d95d8af9ffc93a5637fbe6698512dce4d66e5754167a85b74f25224f778b3fc2d5f6f277e8f2f83b0d6d744dd8cd05507d27ea8c2eddf9919bb12d06ddf3db07c9a975153c4776c56bb0666f231d006a8d1392ce88948fd6e00c207079a0904d2143afe80a6f937322e9f447a35836750da7b3d06c2030ff0b47885050d9c4c2fb77007bb9ac25354cc70c192dbbee1eb7cfa72f9a0b9d24ad56fa7e4bdb42ecc6f6aacac696f0c34a40809dcdda70fdfdea6de7c7810f9999f0d71989eb4de64a508f382acded35246f80bd57b334dac92c256163081c9c79cb279d6b02af857e2f1f8c3481c36e44ef8682164e873dd6118dce2b2d99b8a0ca2d1084ec105162a064ff0c37840a1a7b274e799a705792e5403f18e671f21089d2180dc31d4fa78a9aa3a26d21745a4921bedb6f9d7b0e357d35591731dda2211b98d77ca14d0abfb1e813524b973075b9745aee61ffa22f5e653d0a1cce9431a1a8ba51a243886492da89182b576258bc33a98b30ce99acdfe6401beda8a3d340ba502a9c9288c109de1a976cf02861de2014eac913a506d7ca19f23652cd7bc1b4881e16c004c7fcd6f1a4415c042d6cebadfa92b66c019c200a1f595e939c10236606c9263bd9fed950c5b2fd252eb09e784fab05c359631485c19527939208b535d73054aa4c63cdcc4012c87225058241a85d446d55bb1ab7bae0b03e3fc32b26299ff7ce12379f89c3b110632e8440b108ed5a78f58c5c1e4143cffff87afab7f8cc0dbe2d9667959657a965041c59bbca08500506d4115e1152fdcbb667ad693f6045ffaf0d9b0d8350e41c2dabd119c7b24b7ec2eb6374c5f4667d4c0588c11b72fd956d1d7ba7b7578a155490c159e1873929d44b87b0f4af6d8a185be4790ff79aa5e756320b6ffeeb0d76ae93892227b4fd58edf4a1e0f848041e511d2f34c12d04404eccd150dcc256bf1c319cfc14da97bca953d5fb85e5a65e7ade68d84f7e69e75d6f254b000115a960d1a9f5f8c6e12538b3a37d6c8366cefacc16eeb8566a9c3452623beef23372e3ee46148a0f043e8bb63f5a4d61b4839cd59ffe2307fdf20b0e897cd526728edf690a5f754a876a27f0a323858b8f4cddf1c447cf426895c6d20d025db0372f37c34c7e3f3c471d879d70115af1d96d128467fd7f48fe80d34ae0d816854764a3146f4e11e6470ba0f9fb016b4451ceb389451477e0609933827b2d43b766d08d8e0ca0c5f4f5bbe9f2fce8b20669cbc612c466c65c5787851931baa60c2121d460b01d9ab4aa09bb75dc65b6cf7e90e95adc4a4d31e556e31e582e8c6a72d25f417caff682f5ec046e1471a336ea451c4b2352bed1a0097b99528692ff41ac0469f11cbdbd5279c4ba9e8b362d902229861a3d8fa716335f64d4192a8e4e961385b55ab18a2fccf82631a4f0ce35bbba8781b40da4771a0da6bfc9e018636ffc91782a8b871173c9af4eba838c4c0390dec34827bd8828a1122ba358001e8c299f70b1c69a72a6d87f6fe1bbd57f4a8a11abb08bbfe1b1a1595f1224da48f9932b6eb431889ad2d9de3f2b408fd68498ac05727a56b6aea42e8da1ae9c5090f0a635f1eca66129da612fbbc7d9206186db3387149a310a69dc5902060d44834b9ae5dfe55ef7a5da15528081cc5c283a20b924907412d041590585e37f482a957eb942ed3811b5f7ff11358b1413aa5a234527899a742abe369bc3c729280ecf6b68c199c9b813cba982465b450c945e90aad32b7b4291a24fc4e5d2d64fb58c280284e4b6e8f4d675fe0e11b6eba179124911750460f8e0e13c8ebcbd67d273a0dc2bdcf9104fff4ec0ebe7d9466bbc9034cce6a29bfb51ce273a58982c693b9a3be0350b4f8735dfa37900ca1d22afdd7cf77652031b4e1b553760043d97cfeb49a5830902264e946830b479c464b0bcc185c681bf340f89b5e0fc6846f0afe434eca85536600f193176c32161c384120de40995d3a355a942cd10e718afcd9ae7fec6baf84fc3c2fe161e5241834f43f92d6e7dac7faceb90d712651a12c3910537fd6be7521072335ec119676952259763abebee51516f5fca0f4544915728deb532300176aa2b97272132bd20d4a686bcf6a9f25c1761eaf22df524499bb19ddfec574387d9f873abe8188d289dd7ca538d6fd9bd0cbfdbb9eb9849266d0f1ecc7ac44a3d602ff7874e38d29a82ad0d6b56c0a5ca9fa133afddf009af35cd001a81f2022b2cd6431d24606a0671ed2d7d6ed020825365a2f3fc3052597c7dd802bcc9d69b0dee9c83f174f2b62b606faf72134688520ac6f8d94c5c970fbad3b52ef53724f85fb6713b665c22e04b4e6d2016f7568bd0fdfa432a020d97b35a857d074d35532a0697ab3d46cdd11f35cd47c34a16f59911d0cd5c68cb1c8b5e5ee5f13717da62c52f461e4956f8d9ff73e971009353ae89072d77242ae866439cd33e0d055c5d46eacb85e7edef0a4a49a98185c7d7f5c88bd99942275cb9fb852914c3d8c8794a48c423f0f8fdde316e801bad7fa11bd12d99e8728526718cd4cc9254292d38a87ff58aa0a3be0f585ad29d5ddd13f1569e632b279d48c33404d2bb212a73e54a8ee55cfd3eaaae553327fe3d1bb6566efb493915ca5549b09810b7b0a161504da90f1de6b146d5595ee4b966c395f22e12c66c03b173edd3739ee1e4e6295ee027099e910d92642ae4c6aabbb17d11b3a8729c4956ee66c5a72e23cd9bbd74c9872d08edceed1af00455ca3f75117a14a33d9637d65a00bdb7d472eb1e6970f133c24b337c67222182d306c1802fda868db46550ac2335ad63cd3667aadfb6640cf6e4eea6847df4ab16126469f99b27ee3c3455460441bcf7d2e26c9136c3be1cd9d4b2445decf7235423079a30a44ee0d8ab21a747734d2877090cd9cbc6c0989bbc82513b0e40d4af6f9481c09cced9edfa2efb8175c44ea4de96845e2e35c5e4a5bd90945014a867e1a4564fdac9265e82052b57b4a27c1f9bd8ca7d8d6c70fa698a17835b2f934ed7a9bbb4ce9e2ea328692f90f62d10eee9bd50e1053277a67ccc5a661b61c53af268557c8024518cf25373da52d1f90b724a4bf40e74b55eb23aadf1977125a110a536a47b0bb712476208a2e798ac3f1ee2a4da13afc001c21eceb075c9cf23fe330c02c088d3b0b95f4ce47e806233e2a3f5ec9486b379adba4b3889a3deed17e684fff75eaf11090f1a26049f7eb9c2c6e9e7a0ddf9f44588d5ca3c8d360231df3d07f1201f3ffe65760ac583203830a5cae34d455877f94e76c7152a7f077a17f28146c15b52c853421bbe56956ac1d18bb51bdb6cd8970bd7abca1c78f1bc07085327d59b18492e4254a9d17e56cf944bd31b79464650f6254e03ccfc7f9b9177ba30c8974f3800f073eac4169a2bdf8510e05719ed0aa5831cd46c2b52eaf63df1884e9922f35676a63a605eed5999f849fdfdde98f815de7f6cfc05c0d1e82525c817098d5019aebac4b686be5ca14701a2608047cc5b4fe3f0d61003081399ca6b3ac865c05c988729d66850a4fc52f0f4078f67cc978c30d6a016afcc85ac930a421c2ca556a9f6ecad966fe76bd48390a13cba58fa73b840b6a66221e9a99dcdcb6cdc430f50fa22843951915951138b7557ab6755a0a6b46063d8f26f8172535732eb03354ebd0a516a05f618527acd4df0aa02332b8710eb03dfb094dd4aa75b7d17310f3e71153316db216da917191b4ed637584f942e2de2e8b1e20bf5512b6cd76a2938526c4678d40aaaacdc2b41f4ac6b624c7d44990f723550ee7997ce3e56ae6d14e68111f201c151e0981a6036a41df56fa76c53ae84c5c73bbd4edffedaf31c7e1f1dcd967fd07ffb0abe6de8dc6f121c8b1880aa041445588d35590b5ee20f09fcb713966f8c3c47ecfe6e9757710f3fe0eb659077ad84371b6f194932235b7042bf024f7c6d8dea00d7f7d44758389a855ef605186893c1a5054220da440e9fea6cf584d8f17d17fb9266126456a37fe8a861a8246667c5b7b1c0fd899c3a6ce114eac5c57c9489e047be5cc7cd7ce3b8bd3066ed2e9597461fcb95d8cd368a278c2c26dd9fac36ef5112d00992f5ef279fe12bbfe6e341b8a5d4b5cd7b2884aca95d45ce37b985459bfdb54f696fb81f02ccc2c103dafb45da378416c4358c751c617e03de9704e908d68d46dc09258098d66efd2c2101c9c3c3310c0a47902291863b831d0975da1192be7c2b7aa01a66fcc31d938992f93a8a3b21596c891b64e318bd6603278730f60d97bd2547bf147bab8ac9d82ee1edc37c84965f1875bcaa9c3b4b928356631ba43baefa1fe999df62957cee72b72b2d92775758ccd5b1637cfdb0b5120ecfb1d3c20b0061cd91e397e7d561103b5f1546c65b2a335dda27db074d899a5a0a94a9eb425b0c040e2ed2bf4ea158b9619f0c68d459ae96f8e866ec653e7df0e4d1c8a8c6e8b6feade665d06aaa3a1c9abece6e4a61101f2a270f3eb4b9e7f89afd431ac7002251808bfc7d52e38868c2b04d555343aab127187c2ebb9f65c2f3fc1da7d7fdc66b26ecba51bf4d884246ebac56dd073cb06137dc12380b36f60adc3bc431a3ab948848dec35af0e0d76ee4c975df5b5f2ac5dbf4ac42d4d2fc2a0574a23cd7609683409540a0f316ad4dbd05f3443ce764b6540842cc669773cf8d40907b481babfcc40e20c576b50fd40b3d8dfe4f317c16874a0661707932ecbe6638ae131bbbc8c7c5c65ade95c2583a2a122a6479082e59ffdc7eac6fc1b8283524d6c22f9dd877cd48993b374497760da57f4d6a4feeb5761c30c619fd65f368c705c7a0ee06e07948cfae775b714d6beb8f2a6ffbf2164d00c719be2f61af8fbfacfdee7abf975eaf839a2dd60c6fc346ac46a38a0e8e7afe8a7e70cf23166453002a3b41bac37806f40b3bde36b3b3014e4839cf412eb96dc58546ed732157568749ef6f5b8cfdcb98b77cde07907de9e3189cab7a09efa920a65deecd2bb9037d07c62afbda6eca0cc64ca72a014bf9a23586df1cfda84af7f87eec4bd1dfcf36a9ecf5b1ae90cf230c8bae9b64b308c6550952ce55cbcae107f783fff0ae66ead50139dd79c4ae029c53a7f52e56ab4859125a32d9a04bc41c22333e54aa70aafaad8a28724b10340f7f6dae4121341c08c15fab9bea89d0a3a6744d35878431b15527339a72b7c4fe6791ae0c149c0118890548c3019216536ed044bda5dfe0de0ccb88fd2ce30f55b8560cef213ccd2d01ef92f5eb4fa2f876c91fdf323487a70d41dc0258069a312bd13431e88f8b4df3ee2deed7694c0504af557a9b74521159947601112cb2931e430eb59cf2324f80521eb0d31cae6a70496f855be2830e82ca4836707039b2a547ff1ec41ef6448ab5f91a5a0f840dbfccb93c5e8ab8e293efe410d0bfd2af51639401d994a55fc580ea1f6a50b473ec0a6c7b3ee3a6c8d77e3fb474d3aad1116160edd6720a459a1e4009fc1d7740255ca177c924a72ca05942f736912b630c2cf71cae65296327b82a1d2aa7ffe461e44e00bba8499d10d23c24e1e04460efdea42d82a9c564bd27f98095b4bc0de5fa7a7b665f486aaa4fb4d446bdf5b6420af9445bd4dc3236c988fca008ece729670ae7318c4832a7da2190b68fac0587219886b30ab129a01a263b7f4dad054203cd3f0df6abc7dc953e0c35da8af25b1f433c60b08841b7dd0d27ece0d4c60d2a1a919db02af2905ac4168dc824600b03cd084c89ca62b5dd982964f51239a9efff7a0f93899c696d33c38bed6bbd34f4e920a6b369343ba7a75818f1c65627e2942e99289af8e900da7fc65f655aa00ec5edb615be6a572641d21c70cff69e8d2369406c33496bb13e12d535d0d742706b805558464dfb71e42d03078f5804f97c4efea496942cd3edad665d28fa8367478f2bdcee04cb77476c8489977ddbf60d02c3f9b8123d5333d3a59e721371890e0f813775421388dd298afe427ccfd810288c341425b34f5dccfbaf26c7ac5ee1112d0d6a468faa1601b4e433237617eb6849997220186ebb661ca0ac00b6e380802fc4d957ea92c3cb640029b781b5cf0d9a7ee6a4b11a1e5ec685373008595fd57d2b9c002783be96195509f3d0d8f6a791a846d20980b071a7d9f64436d369b6c69127375ad24bb47f15244930dd86ebbef6d9067e90f5f11f6b5024515a2fedfcff7f3cb7a2ae064edbcb582c4d6831b02b988c9b7a9e1572843eb2abe726cdbe03002f2b50042e6a6b6dfa6f3a4c128bd79767fa0862896532c705581de4a3a5dbe747dae08c21f1baf66723adb1b554b396468bc1fbe552ea79e71bba716a4c34d96d4bab1521196ab2e78c9241569969fa5c6cc7629dc5f7e1d5be8a0f2b7f62bb1472334947b931b889625b32af37d21ad382f69bb536b10cdb1b23956b70d83c1be203b94471d0eafeb2ffe7f0d05a58567808359cb02a138111243251601a196a9369ea0e661f2660b98b2a57124842001f769bf5e6009e93aeaa557b6a462b189293f8727bfda3da8b348ad7433af8c157a9ad35cd6c1a278b63c7a278861b2a0386ab17c42c5e2535904efea932495a33d32f29a9cfa5b155a286ebe0cd136f25a72d573bae68589519087b2b458177e73f7cc16b27b3a67fa385c4f0fa311260c41cc93e762fa1c75e9df4bd72c705de1e804874f614dbbfe729332dac0b0e3dae59b41c61b97cc88c4e2c811c41a62e86cdb965c24bf2a123516c2f4a7555988577ccb3c80ea2520a04579aa4e1373b3d75c5b26ff55aa5270d1228d45e61f413a02e792428da80db4d05dacf2f6729f2b82873414c2beda219d7f8e2831facb66758d458cdf54fb6f02c6516b3756082b40c5348e6520d9359817894ee410f49ea4b75482174ca8fcf190e33fa2497b3f1eb11dfeb93bf1f94a8e58d49524da1813f6f8d1fd42d31b89cba70ae01ede67251d3cbec27ef406eaf3a798900affe6dee74d093839416f996b0b16608b3e1dbb42bddfd89c7b25a690702fc3c7779683b78491164bba1112d023e49ae9e6bec2c08fcc3fa2d5cf7dce992a60c6ef15e2dac6063a864bda4fb905b9847e1cab6f3cbcb70cc1fb5f5ace1bcdc24c73bcba0585bffbb91cc70605d9889eef09bd1ab9517dfca45a3410a9959874a3fdf6b74f997917aa16b84de47647a8c708353f2a45c301680af81e803c5fe6af27886d6159f79fe23f72d4a65e7a4e8fd6be84552ba0537814e2e757ce336338db7a98fa38052c2fc7e9ff5f2ea5fa2fccce25ecacf303b818904b03de4a452022fc56ba92bb368937f1f84ff0eca84c714d6f6893caf82e776322f89455c3c4122600568af71ac5f997682d40e92f3f7fe92344ceeae0a351703ddd32d0c08b99240d6933f888dcde9725b72298923433fde1cf0f5264b751f2bfc6114e6477ed3802b4f739c879dedb8da244e99c93a5ccf5090eb7017461bfb290304163c6348d26cf45d3f1744c4c2ead849b5380657e535897694b01d7d406010fe46341173e3763b1e14bd13c64fbc86b107ce329464a31a499b10ce017c75e8a67246abe808ead63d3b5b50a42c490850c697e313a94f025ee1245f03ef684926413f32cccb42322a60e86188357248fe859249e904eadd6c0bfa52a478c1e5d7197170c5c28ff742e5a0fff67919634e1801ad72f07bfaf394560d9469da66a8805852ee15b37bd46ff83ee3276f1a39faf4539769b63f52f5084c69cf085941fa6c1974ec5e42338e85545334fb7d80cfce9cd4fd5d1536d3eec0a5d9899b0d504415e86fa1272ad1f09a333305fa6e80a6029b00ba058e8c3b131d9ca56fe53b0c1000f4ab00eca7b3c5b5514669babf5d3b109271d165442ae064149ba1791540103e9fb42d4c785be7ef1f3a3533ef0d6fd34075c560174890e57be2b3173a721e947214230711731b077d9a2eadc19ede2abeffd6b17ae9049fce7533ea5133b12f47cabd3fe640b3c9bfa84a17058efa06c0d51711669cd9a57e7add92952f388b132441f9c74df6f06b87aedd99b5031ef2dd7d694c80a65651a5ccc750181bec9d9e35ba492685b694f172aeec398a2fd9ab1c6dc8ce16e744b562cb3b1a84e08dec10a2c1fa764aadaed06cc9fa464ba6045090e487a8fc4404c564fb3681e8dfb070ae2a732f41791e5791dbb01afd9f31f5881d69fff8034ebf932c53d813ab130e7d0a31ebb1c256eedf4cba90c02dbbd808dc9ba4e2de77b9271f0ee41f639be9b43bbe8ed8e1a2785d00d9677409eed83f2258bdd0ee5b73099702958b60d9f9d25d62874b0e6af3d788d3243387d66a7759d41a38180fa654793357026cdbf5b486c1d846a2850c33e2ffbd135ba4d4440e7a1b9dfa5ecbd0c603ecf80f4f7c91884c412d3475bcaedd30a72656e3f73601d8804cc0c7a3b1f74fcba74d9ff916844d53868e9b947d76588475967b55dfe9c8906b8b5a6c48e82673c59d3168d99fd90c6b150a56effb14e1805a9343f9b0141c067068f1747052947f6fc404f648c0d07e21abed2ffb71c70238dbc7f8c36e42058f876f73266d8d41c183054001b11fec6882bc28985ff26e02d0efe88dfe426f0f5d1b3efd60f5ffac3fd9ec22c77f1771bdfb95648b9efa788f280c219ba6cbee22edeef83f4b2575b8ca28f93e31f61b74d8b2800d3c46257baebf29b9a0fea1aad75ec83aba5430df6ff739be4c6c29300a20afe455db87b79aa360cf43a6b4ac6704d0159eb34d3242fef9dfac82b3abbb307ce94d087a33cceef3464324a235a800d726d733ba31f1a685543d9673a69defc29fe4f2b3640d6f39f7a17a79fc48969e36bea23c7d3b235afe71c92b6ed78329c2abf806fbf43cbc94fa06417967c4d729ab268c47d049ad74fa97c7e9f299df636f4f8780012880186dc1a4faff17ed71d213744f9506a721c687cc6167e1dfc0c544122e7728f0493cdbbc1e1f63881e10354d491df4d7d7855082410b4adf73f18a495ae4a30ed1769edc5d5a7f31481365dc42edf5d4775a8ab2b73a9ae9079b899154a1074d587c13f5f336f854442b1e7235464c60269d30da40afdd9a7b74a2f8572996f814270bff51da2d3f4e9132bbbec37cd4963fbbf8d5d5a27a6015d7b994d2ffee8c6908cb61bd82ae68cc7dddbcfc17c066f53244d27c98674a3d58829a653bc7270ac24ead25aff24b8f1dbff758818fb7c4c3292a921211efc0c34623d4a1350b3ccbdbac726179112441625cbca2aed4d23acbbac8455d7ad65417d378b59dd43525cbbe5a48b64c28914bc3ad610469019d3b1251364e8f5eb0596ddcf10ced5ed65d72b301352fb4d7629cdea833fd2e8392d3b85914579f4eaabf453fd694d08e4d6f576a0429724f805ca0ff29734cf14dc7815767522e38fbddac2cba36dd5ea60acab2e3061bd1354b4b9dc05d589310bb55bd1fab44a761bc62cba878adb4e83548b27c65fdfb8f162c2b301ebcee0fc166583ea0eec599a1f80a4833994f57a8728cae65d1b54e7e81aacab07b5bb81cefcc3ae22c0dfcb17b52de0d82afddb092c0756b366eb62b60bee39c969c5052f5acb0d17887253985d65bcf8a4ccf53ba2f502418a2e8d5de2dd756dde7f283a60bac1c3abe87e509cf3bcbb6313c6b282d5c4a3433fe1daa382b1055af986d9132c39ea36830c191249b636bea91cc6b5ede3e334785a01579abedfbd5144b5526a92dff34be5cba9edab59686d8e2eb95490ad9155761e27387089d9e88907e9dd9b2655aa3fd648a9b6ce10f0dacc9c565616f67bc1c4e1d11356cb51ddd8430ed6874be9dbf8951602532a32bf74ba5c355b313beba888765287a5096d760aae1b81e2c66a699b50ef0757c186c0be85336e8fe135e108d26cc64e78dc787921ee62d2fcfa9ae0243e46729b11f3ba0d25253a1d658a1cf216669edd5196a9890db150285a46c38426438d20b0b4d7cdf477756a114c19fca4d8195570db3282fdbec1b8423a9c2b28195e1af47ea42462f247a539b8b561f89ae3c93481c4930380ef6a0828fed863c6ff33d297dff2f267ae828beb1bec74ba3a9d2dc4ab4d224e217e076b6f3eb879e2a1d426e8cf810c9cee8707df3d07ecb47c7f07742e4d5d67e271fd24eb862376670143e2389381ccfa3a503b59a642a06d533b3b4c5061aed0388881d8657b687de54a190c40c8a7b9f78f2148da5d09742eaf42a138482c548f6937175f0522c73c9d5417a00b03d5f501a790ff5999673ec53a82547f88bc02007f29eb3d29296a18adfe9101e0a6c96d0e6b512281ac3307067d9cc295a191f5ef88463b64cf778b8d1aa0ef3a30744af203fbf03f377c6efdedca0bbdd4e9d4ec62ced073285e7c0535e3a7ad1f763d50e7a13dad04f51b184654124ec84605ce4802d8fb362bb2d7b0962baa1b67a3599babddfc28d18b78829746207a90b78be596a40ca5b34f54d4f6a997c4d4ece37eac8ce3bdb9ede5ada1cfcd1e5266cc4a6c63ff6c50122de923ef20e2ab142084cddc8bb077a09137436107ab6ffadb9c05b4abffaf6a767e267c71fbf645a9902ee9796127622e64d9771189cf886afa85040a0658f96415c0284abe3396e345e2b052830cc9c48fbf89e50dbdafa62dec89f3e5ddd9efb01673a537f5115eea7a9cc1be78320d6f3c960c43449cb310c2b2a91b04c74f2b65a98ca051fb6a2f8dead74df8c05862bbc46b1b6c09af5bdb10c7ab1808cbd4e6dc54b4ae106e60daf17726e5676d89627396887d870aae29985dbadcf823fb506ecb0513a3e687e9fbf088fd4da4ea0d1593fae8a332980af12a01eb86c7cdb5bd26be18e4cbc2f40ca0f3ef870d4582eeddd283d41f42689c51504557ea5f16139ed4f3e6b6e7ad2df39bb5e416365699c0f600b04f98e7f8bd396da98665b4a9028d0ddc852a41e82d1152372875b12202e97eaf3840173adab6ecd2223e734c3622262699279ea16e50b2d21417ddfcd47074ba12d7f5db4e6094a3ba41a3c4ea12da6814d972afcceacaf72dd4d1a888f6c556acac0598d07f2786cbe8760cc0594ea650cc13173ea355894764901cb4262daf3249eff440afc74f534556c3c63dcbbcb910f0128a311c18a2af9981fcbd0359c6f3d9c9755496c1df73354a63f2d1aeed1b83cd1d3a31567b59caf1987eef0aa5894888d8673a28f32d422ff797422b857a235965a94e972f8ebb42bdf64dad4d010daf75521b34090a74727dda408b87c0e65fb330dd052638a8ff96fa81d5d6f219b0240256f38197d4a028df4ea2f3e8a2251b382aaf815c89dc8213aa1c8140dc999602e949a31c35b9df3419daa235615375c574d7e02673a36504da486357d7357f16420699e1769f38561db29dbb92aff400ff5616780814d041137f27e506d71932669810c38632de05cc0599bd0de8cb32bd97ca879ed81e08849538e16cb2296ece2d293da3ceded38f9b6cd26b095a7b5d899a5e94e59c7a59746bf017fa4d354625320e70d6e839acdbc4b4593e86b6aefcd8268fe6fb22975a4cd29fd60524ba3e7c091263b42575982f901f431c41f31389beea4e37ead00d177e4556de5e10978554d0b217c166e50659dd5de365ab815a1e4609b1cbbc326db118501e79e2d7dcd47724dfa7515ac0a32f3245c598678bfb2c452106d61d630b8783460d9a4b2dbeb5c275f9c93c214ca25c0672d28699bceeb897c44d85486ad8d89e0ff719e9e4229629fa80ce8fe8abb162cb7061688ec2c09c1f09a093a2c9e08c2398fe0781363b41675526d41bcff1242bbdc6f0182043c8850be7b817cec8dbfcb8138a6f823a4cefc8c326e4ec9d134fe4c071d26e17b0cb3a87d3cc7e7b32a4fdf8b3c76328fef5e84b1461f591e13828e55bb3ee944f261392e070800382cd4e84d37e8453ffb942c687305b5728f403f9391fe2452222827abc88730ecee53aa3a68240e4f486a7b88d192d8c67e3ad1e078a324b93009af1f1cb0f839d28a50774be05e99f7a4ead41334743a71ef706c0887129b4c87ceba1f8624c8ec6bc06b77f2299c6e8203c0857e072127585abbc221982d197c39e63f4e6c1321e59458f39245e8376c3dfd8f5c3f67f8540828c1a346a07488977e8a40d0402a9e42dc748b13e210edd4e7719cd1fd84c59d90b9f510b8fc4f66d14c56f70e3624875c55b3677880cfcdd78e25efed9686e161167f649eb0cc1060f1e5e973a56af656403a132c0318bb2b2af9ffadd04ce45154c6959ea27da7fc45befa8a0ee31ee70866301b4e3d70ecc315a7b091dca2f0cd9fe5655b295f0422307325c461d39767130aefe0301f1d8e72363ba3b72037ef87c540382f61ca6ce548ffbab5ae8d268759cd56c648b0345299b77374ff52963efb5daf10a67e5bf2c45fbc537479b63b6c9c06970bcf7715636bc6363eefe336a5d4b9f99355900b05247ac4ea283ec623181cfc4cbd12086adf60702c17e6aa6d0f8916d925617a94cdbd16128465473133e23fdd09a57e4b24a77e1aa504a1d44f153c0478670fb18c08f3d3a8770f0bd07633374b84fc874524b59ffb3657ce95585de750c4dd983f132cc47a90274ade2ffc9ae7495bc7738a1c3efacb86f6625f10a2947ce54d1fa6c1b26b915636450a7bb61017b0970c38331b3108f4b649174a8b71d00492244650721958d7200ecce21ec485fe49641dce21178c2d66fd5c60555383734f19be5b040be4925a722f64f05ca81d585b282aead28eacb7ebf5a6865fdf5c9dc6f2b2ed6508c15405626e1f95301d815d751e1193cb878d97bf1c238531769653f7a7ef7e0c4d3eca02807aa0a89db972985cd98b7a7c9fb2c88c0131a0edd6f4b84b01a8b39c5da2cd0755af9bab270ddee412f15c5da82226c78eace866225f5916411836d76d843f41176c65afd2c678212bedae9b6629d7095a307645933da0d70d97117e2a32e0d6a5450f8f7034a99e96379a5cc783cd05b0e5ba61da3e2a86751b8b52392817656b9ad43ce328981929503c9716fae2193e4ee9e70766b71aec9605b36a6b02e6171373d88bd069030729559c2bb45e852c7958fbd28afdecc62c6a7802767ead9ca36ff5829361411c202739741f9f75c9991fbfa7dceb58bef7bd882269b6b0214c58fca6536806271abd630b2c187a1e67fb628a35267802dfc5074e1a54c94eeff60685eaffa1ce8276af6f8cf82c83eb8973e5940c1e7da51f0c53bcf6e54b3cca5933c7399e345af0e8d6940193a1ce8e4263293d9017eb1ce63465cc2fadac87c1ddce11b4e373254f2973f1def8ef26f5aa13beb9628eddd24e2f33fcb29664e277eb0ea073914f56c4518b07d41020d6d34e71585913a5e43551b7021de56567852c6c0dfc8396482758568f14c6344c4e2640c96fe4da691969aeb191daed8892ba0cd7244ca51ac05547717833996fa06a162c19e417d947df39dcccfca52bdd06085cc80f46c0da98b38c7f572ae07b84b01734e4a4220c8745961efb9b0f7d40970f90b2855e17d563a1afb4dc6ba4f7345a85329b8e31f2707aa6cc8f4f2caa5cb368456a965b1fb329c42403c079d9d970a671ac9671215049de095bc628fd6a7be249bf1c087ee4b3052cc1c6751e79a3e006ac942b548c242f2de06fcac08ade9dcc4a6da9b263a191c6ff73239b065ffe4e40fa20e24e5d803ba6212560e5e6c67dc82eb6671e8281b217850012447d820094841262f08ece76d203f29a4d23b03ef62e4cf93882f1dc737b5d1a1f4bc9c950406d2b2f821f1123a1f0228e92711802c35b654b9162cd1d7c0ffc4e54465e73ff1706b485a2e4a2aa1dbbd4af3e8f9575d0db7ae1e982f5765360ac3746844f8500de828f02359508c729ec77ed2deec3c00a4078f3fb3f55819162190f616da20cc8e692d8102e86a451ce75c8175e190a14b7a8284020fc67b2e3d1893d2d15695b0339d8efaaaa8af8d04cb6181eb03a27d45fcf70cec0d31d197163c8a3f304dc968a21e9370b331b3e53bf962fdf8dcfae7487008476c7a3380492a5a3c12b875b1d08c31eecf8f3badd62693cf51dd430033fadcf43d6a7a2f6d23a97db0a9d67b3eb3a97732731d00309e886236a586d807d79b219a152d9d576d09da468469d357f59b5cd8ea21c0f5d3d78b3929ba34f1f07e936b97d2a8e154d03bd5d5a10701847847ae3c44f613b7bf46b72c02769929f48c761dae9c4d401ff6c207a392787e0254de5e26d5a11f9dc9b814486c76842dfe1a93cd6aa7a79b45e53912fb71136b912e8edb2cba52e1c6a15fe19582564579ea7b8dca04276664a3a51abea378f15ade3f31b7c51e480cfeb9524b18799b3a9b58ac942f2ca841538e8ef6088ebd156f3647697d08c446c3e1b581f2a2f9b8fac6ccad351122fcd1a7f34be6a5ba9a052dd2ee37ee7278a3903d420e61a33be76dfc96a211d6ddf2f921c5af12371a66280bcac1fede1b255a2547827065669a6aca4f3f26b18dc475bbd110f897e47940f8162bde29e4eda857c181891f8a45f1bd1fc318fe84d2ada9fc26c0cb9d72b34f314215cd5b24fe7e2bc9e7c70bd719e4d7fcf333fa7324738a512c6e5d9c6991ae7ed5a0f6cf0d287189aabf761924b3dd7904f9f0d1269a1d57f4155ad8eb64dbff6639e9c3c6adce65da391c91584a75bb2522200675476590e7292933d4b3ccd709f97279d052b7382735d1e8b229e96eafe1ace4ea1f7b7f02d7c0d891269b25d90b4cdf26ee3f283b6f423df9c38dfb014268e3fc41b0c697ae867eb189aa3b7aa6cd39463f01cc88c3d50cf77370f45d0d7ee090a8b579c09abe00ae063ef87d71327703a66acbb9c00ee0b77fb0cc8776aad2ef2a96205a0978b13eb0fbc1176cac82cc18f506812d8ec757cf18b58130f133512012237760207aae2d479fc41ceead60b3127672456cd3089fbfb5b543dd608d05c257e3fcbcfaee8caf4999c5838b8c490dbf1d228f311cefc1e790eb32523b75d683437d5de5813649998aa6efc75b574ffc8d8d7555a1aac438fa3c64f2e5397878729e7ccf02237eeb6fe4b92886dbc24af614835b25d9c01cc6a7fb04ce168d13d44faa2b87d7a8dd411affa8f1cbdfa0e2545c0600d0591ba8a13784f16dd3087e00a88a18295654563e012a8d631893a933078908935ab5ed9a9036b6663edcc0395f2b3c80ccd06d3f16565ed4e8d80667c0f3e7964b1d5be3c1114f6e441e4c8d254c6fead77c0e0cd7ae09557263b84bc14e307e73a5398a18a01cfd4712c27d8d735be713c55cd6b9d48692d7c95cab25d897df856415a24043ed47eae6ee65ed27964e95fbf4035e2164f13963f52f96339f743335b8fc965631fabafea1295f2d94a5e5faa10513e686e436d12151753257c76e7b63a1c3d37241f20270b7b6990d91befb3005ef749413d5bcb90351bd39bd447e8c22bf57243b29b1e02e0101dff4623241cb4dea1694fa7cb7dbe5aba2c2c4655f5b708552cafe7e7a7185bfb49a4f90f2a21de8713c7ca61e5360ba07ee11998c35d946cf04e1a88dedfa72e0942c8d7720641a8a5aba7eba4af94a060658abea667c932bb201edc94e5704b80decf3fe3c3b93ca72fc615886c0782a2c5fd3afa4aae1ce847e0c7254a2baa526225208e38ab118ace29194a4235123ecff37d9b28e706ca9dfc352de4ca41bff0d72ec05c14d3d9b54e6aa57831aaf8624cfb23dbd21848c84ece72b9b25c1e89f0d4ff7941b07e5fa784d2d853ce47b0c436c5c8996e3893298f4f7f1d13bc1d3d02c7b0ebf473678042c1819e726a491518f4b95b1bffd955d9fe9e428dfbd9b6a3a5d147d858c935f8d07c34b84754e796e050740a5222f5ba784bfd25040e267e5276865acef42e9e9ba75cb66cc191a34c77c90573b3ab896189690f1d2a3422f7f907541b713858617ac5bcf209de164ec31a4394d75097269effbc0bd231ce2a30879b37cee1c349caaedd4fc2c17a151be70b2e2481908329372e0a9d41d804e2d85adb29c56761806b0c411a264cd402c42d6fa74f3e1ba613e1941ddb25aeb21e1960d6952caf30ee81fe7f01c1b2c31630bfcc0cdeb6fd6cbb3f81ee9fccc84ce8ad5b06b8ea6c8108ae0a9f093355b853d18b31e32840c83c8ebf3a7f25cb9437a81dd3fff1ba65d95274b22fd01ace715dd2bfedd4b565450844940abd789abb58d368e508ce83f9d0a05de67c28cc30b94d08fe42e3c1d5435b5384d88d1b1c4c7caeb4f2922be48aae1926eb866216ff842396428a110c958047e197ed64370c9127ae4361b9b459f89fa798e2094aad45114c979f082487ac013eb175b99348098335f6addc369cf959a6372dc11ead55551a00fc1d03b7c66b45f904945567d2ffb27094c0f65c26a7df8db3453157de06a980a0d8a446af06fdae55fae3091a9735ec794ad65ecedf387a99df42d98a1ada1f6205f2a044d325de572e258e05b2f8c496fabfcfbd008136b37f3df417b3c07634c7440cd0fa35df36e91b6254b23af842bc025fa377c09142a149674e76ce9eb3368766db45a22db44b944d015373c584e3ab89f232345682c311c7856b74421aa9c0d4e5c042dce75571576a0d35f5621e17c3be165893c0d428f696bf16f5eff166f062c79d5169cf81e0d8d5ccd0c511e8ee9eaabf7abd6f56e88f478b03d7a99022a2345362b96b5a9dbfc98abfc19a698f3a635d1b955e5eb78e26267e5f225c0c6232a628076ee268faed2ab156befae975da1d09840201ea72a152c72254a005975185736e1436b65eabaa68c345ff7964e946dba98b4cb61acf3a3998623b443ce07e8faaa3969dbe7986860f70cae53044fdd7838a681f9ebdba52bc5b11ca9855f800834e1709ed6d29762badb59b7a6a4c767fca \ No newline at end of file diff --git a/crates/settlement-clients/ethereum/src/test_data/program_output/20468828.txt b/crates/settlement-clients/ethereum/src/test_data/program_output/20468828.txt new file mode 100644 index 00000000..32c67eaf --- /dev/null +++ b/crates/settlement-clients/ethereum/src/test_data/program_output/20468828.txt @@ -0,0 +1,23 @@ +3018624736188101328246297444318644294563497602220136692870273928697775988666 +1151630580362070988738268361000255623588004050079088328197570103143309491006 +666040 +3177057833047671898647187604563418056605025652261842640856515337450823335648 +2590421891839256512113614983194993186457498815986333310670788206383913888162 +1 +1061847063359104755717935913364096764981563580403432547646 +3164098607841814262685584562065384014561507683008279950804 +2163376855567109814935508075637289690251398831877065408270981012600474013295 +98755104937949709628642176073380055704 +104362282196061853877497059290599067159 +0 +10 +1092735609972394726528730534548720965203717757019 +241939744573875736075283046176274470447710245184526611146097095139641614684 +1658854 +774397379524139446221206168840917193112228400237242521560346153613428128537 +5 +726330175714135941764069406682033110407748398240 +107328282983576198777841180606493918532289616812 +827937323922753091213038911432679783215737312167447066598033347469054956751 +146531850000000000000 +0 \ No newline at end of file diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs new file mode 100644 index 00000000..24410963 --- /dev/null +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -0,0 +1,122 @@ +use alloy::{hex, node_bindings::Anvil, primitives::{ U256}, providers::Provider}; +use color_eyre::eyre::eyre; +use rstest::*; +use settlement_client_interface::SettlementClient; +use utils::settings::default::DefaultSettingsProvider; +use std::{fs::{self, File}, io::BufReader, str::FromStr}; +use crate::EthereumSettlementClient; +use alloy::providers::{ext::AnvilApi, ProviderBuilder}; +use alloy_primitives::Address; +use alloy_primitives::FixedBytes; + +use color_eyre::Result; + +use std::io::{BufRead}; + +fn hex_string_to_u8_vec(hex_str: &str) -> color_eyre::Result> { + // Remove any spaces or non-hex characters from the input string + let cleaned_str: String = hex_str.chars().filter(|c| c.is_ascii_hexdigit()).collect(); + + // Convert the cleaned hex string to a Vec + let mut result = Vec::new(); + for chunk in cleaned_str.as_bytes().chunks(2) { + if let Ok(byte_val) = u8::from_str_radix(std::str::from_utf8(chunk)?, 16) { + result.push(byte_val); + } else { + return Err(eyre!("Error parsing hex string: {}", cleaned_str)); + } + } + + Ok(result) +} + +#[rstest] +#[tokio::test] +#[case::basic(20468828)] +async fn update_state_blob_works(#[case] block_no : u64) { + // Only Supports Ethereum Blocks + + dotenvy::from_filename("../.env.test").expect("Could not load .env.test file"); + + // let anvil = Anvil::new().port(3000_u16).fork("https://eth.llamarpc.com").fork_block_number(block_no - 1).try_spawn() + // .expect("Unable to spawn Anvil"); + use url::Url; + + // // https://github.dev/alloy-rs/alloy + let provider = ProviderBuilder::new().on_http(Url::from_str("http://localhost:3000").expect("dskj")); + // // provider.anvil_auto_impersonate_account(false).await.unwrap(); + + // // let gas = U256::from(1337); + // // provider.anvil_set_min_gas_price(gas).await.expect("could not set min gas "); + + // println!("BASE GAS PRICE : {}",provider.get_blob_base_fee().await.expect("could not get base gas price")); + + // // provider.anvil_set_balance(Address::from_str("0x6E9972213BF459853FA33E28Ab7219e9157C8d02").expect("lol"), U256::from(1000)).await.expect("couldn't set balance"); + // provider.anvil_set_balance(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("lol"), U256::from(1000000000)).await.expect("couldn't set balance"); + provider.anvil_impersonate_account(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("sdjkvb")).await.expect("sdcjb"); + println!("Balance : {}", provider.get_balance(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("sdjkvb")).await.expect("could not get balance")); + + + + // println!("Anvil running at `{}`", anvil.endpoint()); + + let settings_provider: DefaultSettingsProvider = DefaultSettingsProvider {}; + let ethereum_settlement_client = EthereumSettlementClient::with_settings(&settings_provider); + + let current_path = std::env::current_dir().unwrap().to_str().unwrap().to_string(); + + // Program Output + let program_output_file_path = + format!("{}{}{}{}", current_path.clone(), "/src/test_data/program_output/", block_no, ".txt"); + println!("{}", program_output_file_path); + + let mut program_output : Vec<[u8;32]> = Vec::new(); + { + let file = File::open(program_output_file_path) + .expect("can't read file"); + let reader = BufReader::new(file); + + for line in reader.lines() { + let line = line + .expect("can't read line"); + let trimmed = line.trim(); + if !trimmed.is_empty() { + let line_u8_32: [u8; 32] = U256::from_str(trimmed).expect("unable to convert line").to_le_bytes(); + program_output.push(line_u8_32); + } + } + } + + // Blob Data + let blob_data_file_path = + format!("{}{}{}{}", current_path.clone(), "/src/test_data/blob_data/", block_no, ".txt"); + println!("{}", blob_data_file_path); + let blob_data = fs::read_to_string(blob_data_file_path).expect("Failed to read the blob data txt file"); + let blob_data_vec = vec![hex_string_to_u8_vec(&blob_data).unwrap()]; + + // Sending transaction + let update_state_result = ethereum_settlement_client + .update_state_with_blobs(program_output,blob_data_vec).await + .expect("update_state_with_blobs failed"); + + println!("{}", update_state_result); + assert!(!update_state_result.is_empty(), "No Transaction Hash"); + let txn = provider.get_transaction_by_hash(FixedBytes::from_str(update_state_result.as_str()).expect("couln't convert")).await.expect("did not get txn from hash"); + + + + if let Some(txxn) = txn { + println!("{:?}",txxn); + + // println!("{}",txxn.hash.to_string()); + // println!("{}",txxn.from.to_string()); + + // let dsd = provider.get_transaction_receipt(FixedBytes::from_str(update_state_result.as_str()).expect("vdf")).await.expect(":vdd"); + // println!(" reciept {:?}",dsd); + + // println!("{:?}",txxn.blob_versioned_hashes); + // println!("Balance : {}", provider.get_balance(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("sdjkvb")).await.expect("could not get balance")); + + + } +} \ No newline at end of file From aa1bef21602d7e811f0a6dac0775d781763adaf3 Mon Sep 17 00:00:00 2001 From: apoorvsadana <95699312+apoorvsadana@users.noreply.github.com> Date: Wed, 7 Aug 2024 20:41:06 +0530 Subject: [PATCH 08/41] update: working test #2 --- crates/settlement-clients/ethereum/src/lib.rs | 37 ++- .../ethereum/src/tests/mod.rs | 242 ++++++++++-------- 2 files changed, 161 insertions(+), 118 deletions(-) diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index b75ea0b3..332a166b 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -1,8 +1,9 @@ pub mod clients; pub mod config; pub mod conversion; -pub mod types; +#[cfg(test)] mod tests; +pub mod types; use alloy::consensus::{ BlobTransactionSidecar, SignableTransaction, TxEip4844, TxEip4844Variant, TxEip4844WithSidecar, TxEnvelope, @@ -29,6 +30,11 @@ use color_eyre::Result; use conversion::{get_txn_input_bytes, prepare_sidecar}; use mockall::{automock, lazy_static, predicate::*}; +use alloy::node_bindings::Anvil; +use alloy::providers::layers::AnvilProvider; +use alloy::providers::RootProvider; +use alloy::transports::http::Http; +use reqwest::Client; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::sync::Arc; @@ -46,15 +52,14 @@ pub const ENV_PRIVATE_KEY: &str = "ETHEREUM_PRIVATE_KEY"; lazy_static! { pub static ref CURRENT_PATH: PathBuf = std::env::current_dir().unwrap(); - pub static ref KZG_SETTINGS: KzgSettings = KzgSettings::load_trusted_setup_file( - CURRENT_PATH.join("src/trusted_setup.txt").as_path() - ) - .expect("Error loading trusted setup file"); + pub static ref KZG_SETTINGS: KzgSettings = + KzgSettings::load_trusted_setup_file(CURRENT_PATH.join("src/trusted_setup.txt").as_path()) + .expect("Error loading trusted setup file"); } #[allow(dead_code)] pub struct EthereumSettlementClient { - provider: Arc, + provider: Arc>, Http>>, core_contract_client: StarknetValidityContractClient, wallet: EthereumWallet, wallet_address: Address, @@ -69,7 +74,14 @@ impl EthereumSettlementClient { let wallet_address = signer.address(); let wallet = EthereumWallet::from(signer); - let provider = Arc::new( + let provider = Arc::new(ProviderBuilder::new().on_anvil_with_config(|_| { + Anvil::new() + .port(3000_u16) + .fork("https://eth.llamarpc.com") + .fork_block_number(20468827) + .arg("--dump-state=/Users/apoorvsadana/Downloads/anvil_state.txt") + })); + let provider2 = Arc::new( ProviderBuilder::new().with_recommended_fillers().wallet(wallet.clone()).on_http(settlement_cfg.rpc_url), ); let core_contract_client = StarknetValidityContractClient::new( @@ -77,7 +89,7 @@ impl EthereumSettlementClient { .expect("Failed to convert the validity contract address.") .0 .into(), - provider.clone(), + provider2.clone(), ); EthereumSettlementClient { provider, core_contract_client, wallet, wallet_address } @@ -145,7 +157,7 @@ impl SettlementClient for EthereumSettlementClient { } async fn update_state_with_blobs(&self, program_output: Vec<[u8; 32]>, state_diff: Vec>) -> Result { - let trusted_setup = KzgSettings::load_trusted_setup_file(Path::new("/Users/dexterhv/Work/Karnot/madara-alliance/madara-orchestrator/crates/settlement-clients/ethereum/src/trusted_setup.txt")) + let trusted_setup = KzgSettings::load_trusted_setup_file(Path::new("/Users/apoorvsadana/Documents/GitHub/madara-orchestrator/crates/settlement-clients/ethereum/src/trusted_setup.txt")) .expect("issue while loading the trusted setup"); let (sidecar_blobs, sidecar_commitments, sidecar_proofs) = prepare_sidecar(&state_diff, &trusted_setup).await?; let sidecar = BlobTransactionSidecar::new(sidecar_blobs, sidecar_commitments, sidecar_proofs); @@ -155,7 +167,7 @@ impl SettlementClient for EthereumSettlementClient { let mut max_fee_per_blob_gas: u128 = self.provider.get_blob_base_fee().await?.to_string().parse()?; // TODO: need to send more than current gas price. - max_fee_per_blob_gas+= 12; + max_fee_per_blob_gas += 12; println!("WALLET ADDRESS : {}", self.wallet_address); println!("Balance : {}", self.provider.get_balance(self.wallet_address).await.expect("could not get balance")); println!("MAX FEE BLOB : {} {}", max_fee_per_blob_gas, eip1559_est.max_fee_per_gas.to_string()); @@ -189,7 +201,7 @@ impl SettlementClient for EthereumSettlementClient { // let mut variant = TxEip4844Variant::from(tx_sidecar); // Sign and submit - let mut txn : TransactionRequest = tx.into(); + let mut txn: TransactionRequest = tx.into(); // txn.set txn = txn.with_from(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("lol")); txn.set_blob_sidecar(tx_sidecar.sidecar); @@ -200,9 +212,8 @@ impl SettlementClient for EthereumSettlementClient { // let tx_envelope: TxEnvelope = tx_signed.into(); // let encoded = tx_envelope.encoded_2718(); - // let pending_tx = self.provider.send_raw_transaction(&encoded).await?; - + let pending_tx = self.provider.send_transaction(txn).await.expect("dsf"); // println!(" pending_tx {:?}", pending_tx ); diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index 24410963..d4044ea4 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -1,122 +1,154 @@ -use alloy::{hex, node_bindings::Anvil, primitives::{ U256}, providers::Provider}; -use color_eyre::eyre::eyre; -use rstest::*; -use settlement_client_interface::SettlementClient; -use utils::settings::default::DefaultSettingsProvider; -use std::{fs::{self, File}, io::BufReader, str::FromStr}; use crate::EthereumSettlementClient; use alloy::providers::{ext::AnvilApi, ProviderBuilder}; +use alloy::{hex, node_bindings::Anvil, primitives::U256, providers::Provider}; use alloy_primitives::Address; use alloy_primitives::FixedBytes; +use color_eyre::eyre::eyre; +use rstest::*; +use settlement_client_interface::SettlementClient; +use std::{ + fs::{self, File}, + io::BufReader, + str::FromStr, +}; +use utils::settings::default::DefaultSettingsProvider; use color_eyre::Result; -use std::io::{BufRead}; +use alloy::eips::{BlockId, BlockNumberOrTag}; +use alloy::network::primitives::BlockTransactionsKind; +use std::io::BufRead; +use std::time::Duration; +use tokio::time::sleep; fn hex_string_to_u8_vec(hex_str: &str) -> color_eyre::Result> { - // Remove any spaces or non-hex characters from the input string - let cleaned_str: String = hex_str.chars().filter(|c| c.is_ascii_hexdigit()).collect(); - - // Convert the cleaned hex string to a Vec - let mut result = Vec::new(); - for chunk in cleaned_str.as_bytes().chunks(2) { - if let Ok(byte_val) = u8::from_str_radix(std::str::from_utf8(chunk)?, 16) { - result.push(byte_val); - } else { - return Err(eyre!("Error parsing hex string: {}", cleaned_str)); - } - } - - Ok(result) + // Remove any spaces or non-hex characters from the input string + let cleaned_str: String = hex_str.chars().filter(|c| c.is_ascii_hexdigit()).collect(); + + // Convert the cleaned hex string to a Vec + let mut result = Vec::new(); + for chunk in cleaned_str.as_bytes().chunks(2) { + if let Ok(byte_val) = u8::from_str_radix(std::str::from_utf8(chunk)?, 16) { + result.push(byte_val); + } else { + return Err(eyre!("Error parsing hex string: {}", cleaned_str)); + } + } + + Ok(result) } #[rstest] #[tokio::test] #[case::basic(20468828)] -async fn update_state_blob_works(#[case] block_no : u64) { - // Only Supports Ethereum Blocks - - dotenvy::from_filename("../.env.test").expect("Could not load .env.test file"); - - // let anvil = Anvil::new().port(3000_u16).fork("https://eth.llamarpc.com").fork_block_number(block_no - 1).try_spawn() - // .expect("Unable to spawn Anvil"); - use url::Url; - - // // https://github.dev/alloy-rs/alloy - let provider = ProviderBuilder::new().on_http(Url::from_str("http://localhost:3000").expect("dskj")); - // // provider.anvil_auto_impersonate_account(false).await.unwrap(); - - // // let gas = U256::from(1337); - // // provider.anvil_set_min_gas_price(gas).await.expect("could not set min gas "); - - // println!("BASE GAS PRICE : {}",provider.get_blob_base_fee().await.expect("could not get base gas price")); - - // // provider.anvil_set_balance(Address::from_str("0x6E9972213BF459853FA33E28Ab7219e9157C8d02").expect("lol"), U256::from(1000)).await.expect("couldn't set balance"); - // provider.anvil_set_balance(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("lol"), U256::from(1000000000)).await.expect("couldn't set balance"); - provider.anvil_impersonate_account(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("sdjkvb")).await.expect("sdcjb"); - println!("Balance : {}", provider.get_balance(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("sdjkvb")).await.expect("could not get balance")); - - - - // println!("Anvil running at `{}`", anvil.endpoint()); - - let settings_provider: DefaultSettingsProvider = DefaultSettingsProvider {}; - let ethereum_settlement_client = EthereumSettlementClient::with_settings(&settings_provider); - - let current_path = std::env::current_dir().unwrap().to_str().unwrap().to_string(); - - // Program Output - let program_output_file_path = - format!("{}{}{}{}", current_path.clone(), "/src/test_data/program_output/", block_no, ".txt"); - println!("{}", program_output_file_path); - - let mut program_output : Vec<[u8;32]> = Vec::new(); - { - let file = File::open(program_output_file_path) - .expect("can't read file"); - let reader = BufReader::new(file); - - for line in reader.lines() { - let line = line - .expect("can't read line"); - let trimmed = line.trim(); - if !trimmed.is_empty() { - let line_u8_32: [u8; 32] = U256::from_str(trimmed).expect("unable to convert line").to_le_bytes(); - program_output.push(line_u8_32); +async fn update_state_blob_works(#[case] block_no: u64) { + // Only Supports Ethereum Blocks + + dotenvy::from_filename("../.env.test").expect("Could not load .env.test file"); + + // let anvil = Anvil::new().port(3000_u16).fork("https://eth.llamarpc.com").fork_block_number(block_no - 1).try_spawn() + // .expect("Unable to spawn Anvil"); + use url::Url; + + // // https://github.dev/alloy-rs/alloy + let provider = ProviderBuilder::new().on_http(Url::from_str("http://localhost:3000").expect("dskj")); + // // provider.anvil_auto_impersonate_account(false).await.unwrap(); + + // // let gas = U256::from(1337); + // // provider.anvil_set_min_gas_price(gas).await.expect("could not set min gas "); + + // println!("BASE GAS PRICE : {}",provider.get_blob_base_fee().await.expect("could not get base gas price")); + + // // provider.anvil_set_balance(Address::from_str("0x6E9972213BF459853FA33E28Ab7219e9157C8d02").expect("lol"), U256::from(1000)).await.expect("couldn't set balance"); + // provider.anvil_set_balance(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("lol"), U256::from(1000000000)).await.expect("couldn't set balance"); + + let settings_provider: DefaultSettingsProvider = DefaultSettingsProvider {}; + let ethereum_settlement_client = EthereumSettlementClient::with_settings(&settings_provider); + provider + .anvil_impersonate_account(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("sdjkvb")) + .await + .expect("sdcjb"); + println!( + "Balance : {}", + provider + .get_balance(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("sdjkvb")) + .await + .expect("could not get balance") + ); + + // println!("Anvil running at `{}`", anvil.endpoint()); + + let current_path = std::env::current_dir().unwrap().to_str().unwrap().to_string(); + + // Program Output + let program_output_file_path = + format!("{}{}{}{}", current_path.clone(), "/src/test_data/program_output/", block_no, ".txt"); + println!("{}", program_output_file_path); + + let mut program_output: Vec<[u8; 32]> = Vec::new(); + { + let file = File::open(program_output_file_path).expect("can't read file"); + let reader = BufReader::new(file); + + for line in reader.lines() { + let line = line.expect("can't read line"); + let trimmed = line.trim(); + if !trimmed.is_empty() { + let line_u8_32: [u8; 32] = U256::from_str(trimmed).expect("unable to convert line").to_le_bytes(); + program_output.push(line_u8_32); + } } } - } - // Blob Data - let blob_data_file_path = - format!("{}{}{}{}", current_path.clone(), "/src/test_data/blob_data/", block_no, ".txt"); - println!("{}", blob_data_file_path); - let blob_data = fs::read_to_string(blob_data_file_path).expect("Failed to read the blob data txt file"); - let blob_data_vec = vec![hex_string_to_u8_vec(&blob_data).unwrap()]; - - // Sending transaction - let update_state_result = ethereum_settlement_client - .update_state_with_blobs(program_output,blob_data_vec).await - .expect("update_state_with_blobs failed"); - - println!("{}", update_state_result); - assert!(!update_state_result.is_empty(), "No Transaction Hash"); - let txn = provider.get_transaction_by_hash(FixedBytes::from_str(update_state_result.as_str()).expect("couln't convert")).await.expect("did not get txn from hash"); - - - - if let Some(txxn) = txn { - println!("{:?}",txxn); - - // println!("{}",txxn.hash.to_string()); - // println!("{}",txxn.from.to_string()); - - // let dsd = provider.get_transaction_receipt(FixedBytes::from_str(update_state_result.as_str()).expect("vdf")).await.expect(":vdd"); - // println!(" reciept {:?}",dsd); - - // println!("{:?}",txxn.blob_versioned_hashes); - // println!("Balance : {}", provider.get_balance(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("sdjkvb")).await.expect("could not get balance")); - - - } -} \ No newline at end of file + // Blob Data + let blob_data_file_path = format!("{}{}{}{}", current_path.clone(), "/src/test_data/blob_data/", block_no, ".txt"); + println!("{}", blob_data_file_path); + let blob_data = fs::read_to_string(blob_data_file_path).expect("Failed to read the blob data txt file"); + let blob_data_vec = vec![hex_string_to_u8_vec(&blob_data).unwrap()]; + + println!( + "Balance : {}", + provider + .get_balance(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("sdjkvb")) + .await + .expect("could not get balance") + ); + + // Sending transaction + let update_state_result = ethereum_settlement_client + .update_state_with_blobs(program_output, blob_data_vec) + .await + .expect("update_state_with_blobs failed"); + + println!("{}", update_state_result); + assert!(!update_state_result.is_empty(), "No Transaction Hash"); + let txn = provider + .get_transaction_by_hash(FixedBytes::from_str(update_state_result.as_str()).expect("couln't convert")) + .await + .expect("did not get txn from hash"); + + // add delay dor 2 seconds + sleep(Duration::from_secs(2)).await; + if let Some(txxn) = txn { + println!("{:?}", txxn); + + println!("{}", txxn.hash.to_string()); + println!("{}", txxn.from.to_string()); + + let dsd = provider + // .get_transaction_receipt(FixedBytes::from_str(update_state_result.as_str()).expect("vdf")) + .get_block(BlockId::Number(BlockNumberOrTag::Pending), BlockTransactionsKind::Full) + .await + .expect(":vdd"); + println!(" reciept {:#?}", dsd); + + println!("{:?}", txxn.blob_versioned_hashes); + println!( + "Balance : {}", + provider + .get_balance(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("sdjkvb")) + .await + .expect("could not get balance") + ); + } +} From 3158c485ad572fc594d5cd6118110ac9076c3438 Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Fri, 9 Aug 2024 10:20:47 +0530 Subject: [PATCH 09/41] update: added cfg test for update_state_with_blobs --- crates/settlement-clients/ethereum/src/lib.rs | 98 ++- .../test_data/ABI/starknet_core_contract.json | 681 ++++++++++++++++++ .../ethereum/src/tests/mod.rs | 100 ++- 3 files changed, 825 insertions(+), 54 deletions(-) create mode 100644 crates/settlement-clients/ethereum/src/test_data/ABI/starknet_core_contract.json diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index 332a166b..e98e0ffc 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -1,71 +1,80 @@ -pub mod clients; -pub mod config; -pub mod conversion; -#[cfg(test)] -mod tests; -pub mod types; +use std::path::{Path, PathBuf}; +use std::str::FromStr; +use std::sync::Arc; +use alloy::{node_bindings::Anvil, providers::ProviderBuilder, sol}; +use alloy::{ + network::EthereumWallet, + primitives::{Address, B256, U256}, + providers::{PendingTransactionConfig, Provider}, + rpc::types::TransactionReceipt, + signers::local::PrivateKeySigner, +}; use alloy::consensus::{ - BlobTransactionSidecar, SignableTransaction, TxEip4844, TxEip4844Variant, TxEip4844WithSidecar, TxEnvelope, + BlobTransactionSidecar, SignableTransaction, TxEip4844, TxEip4844WithSidecar, }; +// use eyre::Result; use alloy::eips::eip2718::Encodable2718; use alloy::eips::eip2930::AccessList; use alloy::eips::eip4844::BYTES_PER_BLOB; use alloy::hex; use alloy::network::TransactionBuilder; use alloy::providers::ext::AnvilApi; +// use alloy::node_bindings::Anvil; +use alloy::providers::layers::AnvilProvider; +use alloy::providers::RootProvider; use alloy::rpc::types::TransactionRequest; -use alloy::{ - network::EthereumWallet, - primitives::{Address, B256, U256}, - providers::{PendingTransactionConfig, Provider, ProviderBuilder}, - rpc::types::TransactionReceipt, - signers::local::PrivateKeySigner, -}; +use alloy::transports::http::Http; use alloy_primitives::Bytes; use async_trait::async_trait; use c_kzg::{Blob, Bytes32, KzgCommitment, KzgProof, KzgSettings}; use color_eyre::eyre::eyre; use color_eyre::Result; -use conversion::{get_txn_input_bytes, prepare_sidecar}; use mockall::{automock, lazy_static, predicate::*}; - -use alloy::node_bindings::Anvil; -use alloy::providers::layers::AnvilProvider; -use alloy::providers::RootProvider; -use alloy::transports::http::Http; use reqwest::Client; -use std::path::{Path, PathBuf}; -use std::str::FromStr; -use std::sync::Arc; -use crate::clients::interfaces::validity_interface::StarknetValidityContractTrait; -use settlement_client_interface::{SettlementClient, SettlementVerificationStatus, SETTLEMENT_SETTINGS_NAME}; +use conversion::prepare_sidecar; +use settlement_client_interface::{SETTLEMENT_SETTINGS_NAME, SettlementClient, SettlementVerificationStatus}; +use types::EthHttpProvider; use utils::{env_utils::get_env_var_or_panic, settings::SettingsProvider}; +use crate::clients::interfaces::validity_interface::StarknetValidityContractTrait; use crate::clients::StarknetValidityContractClient; use crate::config::EthereumSettlementConfig; use crate::conversion::{slice_slice_u8_to_vec_u256, slice_u8_to_u256}; -use crate::types::EthHttpProvider; + +pub mod clients; +pub mod config; +pub mod conversion; +#[cfg(test)] +mod tests; +pub mod types; pub const ENV_PRIVATE_KEY: &str = "ETHEREUM_PRIVATE_KEY"; lazy_static! { pub static ref CURRENT_PATH: PathBuf = std::env::current_dir().unwrap(); pub static ref KZG_SETTINGS: KzgSettings = + // TODO: set more generalized path KzgSettings::load_trusted_setup_file(CURRENT_PATH.join("src/trusted_setup.txt").as_path()) .expect("Error loading trusted setup file"); } + + #[allow(dead_code)] pub struct EthereumSettlementClient { - provider: Arc>, Http>>, core_contract_client: StarknetValidityContractClient, wallet: EthereumWallet, wallet_address: Address, + #[cfg(not(test))] + provider: Arc, + #[cfg(test)] + provider: Arc>, Http>>, } impl EthereumSettlementClient { + #[cfg(not(test))] pub fn with_settings(settings: &impl SettingsProvider) -> Self { let settlement_cfg: EthereumSettlementConfig = settings.get_settings(SETTLEMENT_SETTINGS_NAME).unwrap(); @@ -74,6 +83,27 @@ impl EthereumSettlementClient { let wallet_address = signer.address(); let wallet = EthereumWallet::from(signer); + let provider = Arc::new( + ProviderBuilder::new().with_recommended_fillers().wallet(wallet.clone()).on_http(settlement_cfg.rpc_url), + ); + let core_contract_client = StarknetValidityContractClient::new( + Address::from_str(&settlement_cfg.core_contract_address).unwrap().0.into(), + provider.clone(), + ); + + EthereumSettlementClient { provider, core_contract_client, wallet, wallet_address } + } + + #[cfg(test)] + pub fn with_test_settings(settings: &impl SettingsProvider) -> Self { + let settlement_cfg: EthereumSettlementConfig = settings.get_settings(SETTLEMENT_SETTINGS_NAME).unwrap(); + + let private_key = get_env_var_or_panic(ENV_PRIVATE_KEY); + let signer: PrivateKeySigner = private_key.parse().expect("Failed to parse private key"); + let wallet_address = signer.address(); + let wallet = EthereumWallet::from(signer); + + let config = Anvil::new(); let provider = Arc::new(ProviderBuilder::new().on_anvil_with_config(|_| { Anvil::new() .port(3000_u16) @@ -157,7 +187,7 @@ impl SettlementClient for EthereumSettlementClient { } async fn update_state_with_blobs(&self, program_output: Vec<[u8; 32]>, state_diff: Vec>) -> Result { - let trusted_setup = KzgSettings::load_trusted_setup_file(Path::new("/Users/apoorvsadana/Documents/GitHub/madara-orchestrator/crates/settlement-clients/ethereum/src/trusted_setup.txt")) + let trusted_setup = KzgSettings::load_trusted_setup_file(Path::new("/Users/dexterhv/Work/Karnot/madara-alliance/madara-orchestrator/crates/settlement-clients/ethereum/src/trusted_setup.txt")) .expect("issue while loading the trusted setup"); let (sidecar_blobs, sidecar_commitments, sidecar_proofs) = prepare_sidecar(&state_diff, &trusted_setup).await?; let sidecar = BlobTransactionSidecar::new(sidecar_blobs, sidecar_commitments, sidecar_proofs); @@ -203,9 +233,12 @@ impl SettlementClient for EthereumSettlementClient { // Sign and submit let mut txn: TransactionRequest = tx.into(); // txn.set - txn = txn.with_from(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("lol")); txn.set_blob_sidecar(tx_sidecar.sidecar); txn.set_nonce(666068); + + if cfg!(test) { + txn = txn.with_from(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("lol")); + } // let signature = self.wallet.default_signer().sign_transaction(&mut variant).await?; // let tx_signed = variant.into_signed(signature); @@ -214,9 +247,12 @@ impl SettlementClient for EthereumSettlementClient { // let pending_tx = self.provider.send_raw_transaction(&encoded).await?; - let pending_tx = self.provider.send_transaction(txn).await.expect("dsf"); + let pending_tx = self.provider.send_transaction(txn).await.expect("qwerty"); + + println!(" pending_tx {:?}", pending_tx ); + + // Checking contract state! - // println!(" pending_tx {:?}", pending_tx ); Ok("0x2b3fb5f9a59c0687e6e33ca0fc2fe7c02be013a52e5935d8a7ec19dbac95d081".into()) } diff --git a/crates/settlement-clients/ethereum/src/test_data/ABI/starknet_core_contract.json b/crates/settlement-clients/ethereum/src/test_data/ABI/starknet_core_contract.json new file mode 100644 index 00000000..99362827 --- /dev/null +++ b/crates/settlement-clients/ethereum/src/test_data/ABI/starknet_core_contract.json @@ -0,0 +1,681 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "changedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldConfigHash", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newConfigHash", + "type": "uint256" + } + ], + "name": "ConfigHashChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "fromAddress", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "toAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "payload", + "type": "uint256[]" + } + ], + "name": "ConsumedMessageToL1", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "fromAddress", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "toAddress", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "selector", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "payload", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + } + ], + "name": "ConsumedMessageToL2", + "type": "event" + }, + { "anonymous": false, "inputs": [], "name": "Finalized", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "fromAddress", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "toAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "payload", + "type": "uint256[]" + } + ], + "name": "LogMessageToL1", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "fromAddress", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "toAddress", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "selector", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "payload", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "name": "LogMessageToL2", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "acceptedGovernor", + "type": "address" + } + ], + "name": "LogNewGovernorAccepted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "nominatedGovernor", + "type": "address" + } + ], + "name": "LogNominatedGovernor", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "LogNominationCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "LogOperatorAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "LogOperatorRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "removedGovernor", + "type": "address" + } + ], + "name": "LogRemovedGovernor", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "stateTransitionFact", + "type": "bytes32" + } + ], + "name": "LogStateTransitionFact", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "globalRoot", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "int256", + "name": "blockNumber", + "type": "int256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "blockHash", + "type": "uint256" + } + ], + "name": "LogStateUpdate", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "fromAddress", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "toAddress", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "selector", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "payload", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + } + ], + "name": "MessageToL2Canceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "fromAddress", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "toAddress", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "selector", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "payload", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + } + ], + "name": "MessageToL2CancellationStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "changedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldProgramHash", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newProgramHash", + "type": "uint256" + } + ], + "name": "ProgramHashChanged", + "type": "event" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "toAddress", "type": "uint256" }, + { "internalType": "uint256", "name": "selector", "type": "uint256" }, + { "internalType": "uint256[]", "name": "payload", "type": "uint256[]" }, + { "internalType": "uint256", "name": "nonce", "type": "uint256" } + ], + "name": "cancelL1ToL2Message", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "configHash", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "fromAddress", "type": "uint256" }, + { "internalType": "uint256[]", "name": "payload", "type": "uint256[]" } + ], + "name": "consumeMessageFromL2", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "finalize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getMaxL1MsgFee", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "identify", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes", "name": "data", "type": "bytes" }], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "isFinalized", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isFrozen", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "user", "type": "address" } + ], + "name": "isOperator", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "msgHash", "type": "bytes32" } + ], + "name": "l1ToL2MessageCancellations", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "l1ToL2MessageNonce", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "msgHash", "type": "bytes32" } + ], + "name": "l1ToL2Messages", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "msgHash", "type": "bytes32" } + ], + "name": "l2ToL1Messages", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "messageCancellationDelay", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "programHash", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "newOperator", "type": "address" } + ], + "name": "registerOperator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "toAddress", "type": "uint256" }, + { "internalType": "uint256", "name": "selector", "type": "uint256" }, + { "internalType": "uint256[]", "name": "payload", "type": "uint256[]" } + ], + "name": "sendMessageToL2", + "outputs": [ + { "internalType": "bytes32", "name": "", "type": "bytes32" }, + { "internalType": "uint256", "name": "", "type": "uint256" } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "newConfigHash", "type": "uint256" } + ], + "name": "setConfigHash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "delayInSeconds", "type": "uint256" } + ], + "name": "setMessageCancellationDelay", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "newProgramHash", "type": "uint256" } + ], + "name": "setProgramHash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "starknetAcceptGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "starknetCancelNomination", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "user", "type": "address" } + ], + "name": "starknetIsGovernor", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "newGovernor", "type": "address" } + ], + "name": "starknetNominateNewGovernor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "governorForRemoval", + "type": "address" + } + ], + "name": "starknetRemoveGovernor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "toAddress", "type": "uint256" }, + { "internalType": "uint256", "name": "selector", "type": "uint256" }, + { "internalType": "uint256[]", "name": "payload", "type": "uint256[]" }, + { "internalType": "uint256", "name": "nonce", "type": "uint256" } + ], + "name": "startL1ToL2MessageCancellation", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "stateBlockHash", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "msgHash", "type": "bytes32" } + ], + "name": "l1ToL2Messages", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "stateBlockNumber", + "outputs": [{ "internalType": "int256", "name": "", "type": "int256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "stateRoot", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "removedOperator", + "type": "address" + } + ], + "name": "unregisterOperator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "programOutput", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "onchainDataHash", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "onchainDataSize", + "type": "uint256" + } + ], + "name": "updateState", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "programOutput", + "type": "uint256[]" + }, + { "internalType": "bytes", "name": "kzgProof", "type": "bytes" } + ], + "name": "updateStateKzgDA", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index d4044ea4..dce81df6 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -1,26 +1,24 @@ -use crate::EthereumSettlementClient; -use alloy::providers::{ext::AnvilApi, ProviderBuilder}; -use alloy::{hex, node_bindings::Anvil, primitives::U256, providers::Provider}; -use alloy_primitives::Address; -use alloy_primitives::FixedBytes; -use color_eyre::eyre::eyre; -use rstest::*; -use settlement_client_interface::SettlementClient; use std::{ fs::{self, File}, io::BufReader, str::FromStr, }; -use utils::settings::default::DefaultSettingsProvider; - -use color_eyre::Result; - -use alloy::eips::{BlockId, BlockNumberOrTag}; -use alloy::network::primitives::BlockTransactionsKind; use std::io::BufRead; use std::time::Duration; + +use alloy::{primitives::U256, providers::Provider, sol}; +use alloy::providers::{ext::AnvilApi, ProviderBuilder}; +use alloy_primitives::Address; +use alloy_primitives::FixedBytes; +use color_eyre::eyre::eyre; +use rstest::*; use tokio::time::sleep; +use settlement_client_interface::SettlementClient; +use utils::settings::default::DefaultSettingsProvider; + +use crate::EthereumSettlementClient; + fn hex_string_to_u8_vec(hex_str: &str) -> color_eyre::Result> { // Remove any spaces or non-hex characters from the input string let cleaned_str: String = hex_str.chars().filter(|c| c.is_ascii_hexdigit()).collect(); @@ -38,18 +36,39 @@ fn hex_string_to_u8_vec(hex_str: &str) -> color_eyre::Result> { Ok(result) } + // Codegen from ABI file to interact with the contract. +sol!( + #[allow(missing_docs)] + #[sol(rpc)] + STARKNET_CORE_CONTRACT, + "src/test_data/ABI/starknet_core_contract.json" +); + #[rstest] #[tokio::test] #[case::basic(20468828)] async fn update_state_blob_works(#[case] block_no: u64) { + + // Changes : + // DONE: EthereumSettlementClient : Provider type with cfg test flag + // DONE: EthereumSettlementClient : impl `with_test_settings` with cfg test flag + // DONE: EthereumSettlementClient : `update_state_with_blobs` add `with_from` before transacting + // Send provider to `with_test_settings` from tester. + // Possibly run anvil at the start at PORT 3000 + // Only Supports Ethereum Blocks dotenvy::from_filename("../.env.test").expect("Could not load .env.test file"); + use std::any::Any; + + use alloy::sol; // let anvil = Anvil::new().port(3000_u16).fork("https://eth.llamarpc.com").fork_block_number(block_no - 1).try_spawn() // .expect("Unable to spawn Anvil"); use url::Url; + use crate::clients::interfaces::validity_interface::StarknetValidityContract::stateBlockNumberReturn; + // // https://github.dev/alloy-rs/alloy let provider = ProviderBuilder::new().on_http(Url::from_str("http://localhost:3000").expect("dskj")); // // provider.anvil_auto_impersonate_account(false).await.unwrap(); @@ -62,8 +81,11 @@ async fn update_state_blob_works(#[case] block_no: u64) { // // provider.anvil_set_balance(Address::from_str("0x6E9972213BF459853FA33E28Ab7219e9157C8d02").expect("lol"), U256::from(1000)).await.expect("couldn't set balance"); // provider.anvil_set_balance(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("lol"), U256::from(1000000000)).await.expect("couldn't set balance"); + + + let settings_provider: DefaultSettingsProvider = DefaultSettingsProvider {}; - let ethereum_settlement_client = EthereumSettlementClient::with_settings(&settings_provider); + let ethereum_settlement_client = EthereumSettlementClient::with_test_settings(&settings_provider); provider .anvil_impersonate_account(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("sdjkvb")) .await @@ -76,6 +98,21 @@ async fn update_state_blob_works(#[case] block_no: u64) { .expect("could not get balance") ); + + + let provider = ProviderBuilder::new().on_http(Url::from_str("http://localhost:3000").expect("sdf")); + + // Create a contract instance. + let contract = STARKNET_CORE_CONTRACT::new(Address::from_str("0xc662c410c0ecf747543f5ba90660f6abebd9c8c4").expect("sd"), provider.clone()); + + // Call the contract, retrieve the total supply. + let blockhash = contract.stateBlockHash().call().await.unwrap(); + + let blockumber = contract.stateBlockNumber().call().await.unwrap(); + println!("CURRENT BLOCK NUMBER {}" , blockumber._0.to_string()); + println!("CURRENT BLOCK HASH {}" , blockhash._0.to_string()); + + // println!("Anvil running at `{}`", anvil.endpoint()); let current_path = std::env::current_dir().unwrap().to_str().unwrap().to_string(); @@ -130,19 +167,19 @@ async fn update_state_blob_works(#[case] block_no: u64) { // add delay dor 2 seconds sleep(Duration::from_secs(2)).await; if let Some(txxn) = txn { - println!("{:?}", txxn); + // println!("{:?}", txxn); println!("{}", txxn.hash.to_string()); println!("{}", txxn.from.to_string()); - let dsd = provider - // .get_transaction_receipt(FixedBytes::from_str(update_state_result.as_str()).expect("vdf")) - .get_block(BlockId::Number(BlockNumberOrTag::Pending), BlockTransactionsKind::Full) - .await - .expect(":vdd"); - println!(" reciept {:#?}", dsd); + // let dsd = provider + // // .get_transaction_receipt(FixedBytes::from_str(update_state_result.as_str()).expect("vdf")) + // .get_block(BlockId::Number(BlockNumberOrTag::Pending), BlockTransactionsKind::Full) + // .await + // .expect(":vdd"); + // println!(" reciept {:#?}", dsd); - println!("{:?}", txxn.blob_versioned_hashes); + // println!("{:?}", txxn.blob_versioned_hashes); println!( "Balance : {}", provider @@ -150,5 +187,22 @@ async fn update_state_blob_works(#[case] block_no: u64) { .await .expect("could not get balance") ); + + + + + let provider = ProviderBuilder::new().on_http(Url::from_str("http://localhost:3000").expect("sdf")); + + // Create a contract instance. + let contract = STARKNET_CORE_CONTRACT::new(Address::from_str("0xc662c410c0ecf747543f5ba90660f6abebd9c8c4").expect("sd"), provider.clone()); + + // Call the contract, retrieve the total supply. + let blockhash = contract.stateBlockHash().call().await.unwrap(); + + let blockumber = contract.stateBlockNumber().call().await.unwrap(); + println!("CURRENT BLOCK NUMBER {}" , blockumber._0.to_string()); + println!("CURRENT BLOCK HASH {}" , blockhash._0.to_string()); + + } } From db1a6756d5adc8824acf70b869cbd01cd416e24e Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Fri, 9 Aug 2024 12:32:40 +0530 Subject: [PATCH 10/41] update: cleaner cfg(test) implemented code for update_state_and_blob_test --- crates/settlement-clients/ethereum/src/lib.rs | 74 ++++----- .../ethereum/src/tests/mod.rs | 143 ++++-------------- 2 files changed, 57 insertions(+), 160 deletions(-) diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index e98e0ffc..78352c72 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -11,7 +11,7 @@ use alloy::{ signers::local::PrivateKeySigner, }; use alloy::consensus::{ - BlobTransactionSidecar, SignableTransaction, TxEip4844, TxEip4844WithSidecar, + BlobTransactionSidecar, SignableTransaction, TxEip4844, TxEip4844Variant, TxEip4844WithSidecar, TxEnvelope }; // use eyre::Result; use alloy::eips::eip2718::Encodable2718; @@ -70,7 +70,7 @@ pub struct EthereumSettlementClient { #[cfg(not(test))] provider: Arc, #[cfg(test)] - provider: Arc>, Http>>, + provider: RootProvider>, } impl EthereumSettlementClient { @@ -95,31 +95,20 @@ impl EthereumSettlementClient { } #[cfg(test)] - pub fn with_test_settings(settings: &impl SettingsProvider) -> Self { + pub fn with_test_settings(settings: &impl SettingsProvider, provider : RootProvider>) -> Self { let settlement_cfg: EthereumSettlementConfig = settings.get_settings(SETTLEMENT_SETTINGS_NAME).unwrap(); let private_key = get_env_var_or_panic(ENV_PRIVATE_KEY); let signer: PrivateKeySigner = private_key.parse().expect("Failed to parse private key"); let wallet_address = signer.address(); let wallet = EthereumWallet::from(signer); - - let config = Anvil::new(); - let provider = Arc::new(ProviderBuilder::new().on_anvil_with_config(|_| { - Anvil::new() - .port(3000_u16) - .fork("https://eth.llamarpc.com") - .fork_block_number(20468827) - .arg("--dump-state=/Users/apoorvsadana/Downloads/anvil_state.txt") - })); - let provider2 = Arc::new( + + let fill_provider = Arc::new( ProviderBuilder::new().with_recommended_fillers().wallet(wallet.clone()).on_http(settlement_cfg.rpc_url), ); let core_contract_client = StarknetValidityContractClient::new( - Address::from_str(&settlement_cfg.core_contract_address) - .expect("Failed to convert the validity contract address.") - .0 - .into(), - provider2.clone(), + Address::from_str(&settlement_cfg.core_contract_address).unwrap().0.into(), + fill_provider, ); EthereumSettlementClient { provider, core_contract_client, wallet, wallet_address } @@ -187,8 +176,8 @@ impl SettlementClient for EthereumSettlementClient { } async fn update_state_with_blobs(&self, program_output: Vec<[u8; 32]>, state_diff: Vec>) -> Result { - let trusted_setup = KzgSettings::load_trusted_setup_file(Path::new("/Users/dexterhv/Work/Karnot/madara-alliance/madara-orchestrator/crates/settlement-clients/ethereum/src/trusted_setup.txt")) - .expect("issue while loading the trusted setup"); + //TODO: better file management + let trusted_setup = KzgSettings::load_trusted_setup_file(Path::new("/Users/dexterhv/Work/Karnot/madara-alliance/madara-orchestrator/crates/settlement-clients/ethereum/src/trusted_setup.txt"))?; let (sidecar_blobs, sidecar_commitments, sidecar_proofs) = prepare_sidecar(&state_diff, &trusted_setup).await?; let sidecar = BlobTransactionSidecar::new(sidecar_blobs, sidecar_commitments, sidecar_proofs); @@ -198,13 +187,9 @@ impl SettlementClient for EthereumSettlementClient { let mut max_fee_per_blob_gas: u128 = self.provider.get_blob_base_fee().await?.to_string().parse()?; // TODO: need to send more than current gas price. max_fee_per_blob_gas += 12; - println!("WALLET ADDRESS : {}", self.wallet_address); - println!("Balance : {}", self.provider.get_balance(self.wallet_address).await.expect("could not get balance")); - println!("MAX FEE BLOB : {} {}", max_fee_per_blob_gas, eip1559_est.max_fee_per_gas.to_string()); let max_priority_fee_per_gas: u128 = self.provider.get_max_priority_fee_per_gas().await?.to_string().parse()?; - let nonce = self.provider.get_transaction_count(self.wallet_address).await?.to_string().parse()?; - + // x_0_value : program_output[6] let kzg_proof = Self::build_proof( state_diff, @@ -225,36 +210,29 @@ impl SettlementClient for EthereumSettlementClient { access_list: AccessList(vec![]), blob_versioned_hashes: sidecar.versioned_hashes().collect(), max_fee_per_blob_gas, + // input: get_txn_input_bytes(program_output, kzg_proof), input: Bytes::from(hex::decode("0xb72d42a100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000001706ac7b2661801b4c0733da6ed1d2910b3b97259534ca95a63940932513111fba028bccc051eaae1b9a69b53e64a68021233b4dee2030aeda4be886324b3fbb3e00000000000000000000000000000000000000000000000000000000000a29b8070626a88de6a77855ecd683757207cdd18ba56553dca6c0c98ec523b827bee005ba2078240f1585f96424c2d1ee48211da3b3f9177bf2b9880b4fc91d59e9a2000000000000000000000000000000000000000000000000000000000000000100000000000000002b4e335bc41dc46c71f29928a5094a8c96a0c3536cabe53e0000000000000000810abb1929a0d45cdd62a20f9ccfd5807502334e7deb35d404c86d8b63a5741770fefca2f9b8efb7e663d89097edb3c60595b236f6e78e6f000000000000000000000000000000004a4b8a979fefc4d6b82e030fb082ca98000000000000000000000000000000004e8371c6774260e87b92447d4a2b0e170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000bf67f59d2988a46fbff7ed79a621778a3cd3985b0088eedbe2fe3918b69ccb411713b7fa72079d4eddf291103ccbe41e78a9615c0000000000000000000000000000000000000000000000000000000000194fe601b64b1b3b690b43b9b514fb81377518f4039cd3e4f4914d8a6bdf01d679fb1900000000000000000000000000000000000000000000000000000000000000050000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca000000000000000000000000012ccc443d39da45e5f640b3e71f0c7502152dbac01d4988e248d342439aa025b302e1f07595f6a5c810dcce23e7379e48f05d4cf000000000000000000000000000000000000000000000007f189b5374ad2a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030ab015987628cffee3ef99b9768ef8ca12c6244525f0cd10310046eaa21291b5aca164d044c5b4ad7212c767b165ed5e300000000000000000000000000000000").unwrap()), }; - let tx_sidecar = TxEip4844WithSidecar { tx: tx.clone(), sidecar }; - // let mut variant = TxEip4844Variant::from(tx_sidecar); - // Sign and submit - let mut txn: TransactionRequest = tx.into(); - // txn.set - txn.set_blob_sidecar(tx_sidecar.sidecar); - txn.set_nonce(666068); + + let tx_sidecar = TxEip4844WithSidecar { tx: tx.clone(), sidecar: sidecar.clone() }; + let mut variant = TxEip4844Variant::from(tx_sidecar); + let signature = self.wallet.default_signer().sign_transaction(&mut variant).await?; + let tx_signed = variant.into_signed(signature); + let tx_envelope: TxEnvelope = tx_signed.into(); + let encoded = tx_envelope.encoded_2718(); if cfg!(test) { - txn = txn.with_from(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("lol")); + // Sign and submit + let mut txn_request : TransactionRequest = tx_envelope.into(); + txn_request.set_nonce(666068); + txn_request = txn_request.with_from(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("Unable to impersonate operator.")); + let pending_transaction = self.provider.send_transaction(txn_request).await?; + return Ok(pending_transaction.tx_hash().to_string()); } - // let signature = self.wallet.default_signer().sign_transaction(&mut variant).await?; - - // let tx_signed = variant.into_signed(signature); - // let tx_envelope: TxEnvelope = tx_signed.into(); - // let encoded = tx_envelope.encoded_2718(); - - // let pending_tx = self.provider.send_raw_transaction(&encoded).await?; - - let pending_tx = self.provider.send_transaction(txn).await.expect("qwerty"); - - println!(" pending_tx {:?}", pending_tx ); - - // Checking contract state! - - Ok("0x2b3fb5f9a59c0687e6e33ca0fc2fe7c02be013a52e5935d8a7ec19dbac95d081".into()) + let pending_transaction = self.provider.send_raw_transaction(&encoded).await?; + return Ok(pending_transaction.tx_hash().to_string()); } /// Should verify the inclusion of a tx in the settlement layer diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index dce81df6..5312ce48 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -5,8 +5,10 @@ use std::{ }; use std::io::BufRead; use std::time::Duration; +use alloy::{node_bindings::Anvil, sol}; +use url::Url; -use alloy::{primitives::U256, providers::Provider, sol}; +use alloy::{primitives::U256, providers::Provider}; use alloy::providers::{ext::AnvilApi, ProviderBuilder}; use alloy_primitives::Address; use alloy_primitives::FixedBytes; @@ -44,87 +46,48 @@ sol!( "src/test_data/ABI/starknet_core_contract.json" ); +// TODO: betterment of file routes + #[rstest] #[tokio::test] #[case::basic(20468828)] async fn update_state_blob_works(#[case] block_no: u64) { - // Changes : - // DONE: EthereumSettlementClient : Provider type with cfg test flag - // DONE: EthereumSettlementClient : impl `with_test_settings` with cfg test flag - // DONE: EthereumSettlementClient : `update_state_with_blobs` add `with_from` before transacting - // Send provider to `with_test_settings` from tester. - // Possibly run anvil at the start at PORT 3000 - - // Only Supports Ethereum Blocks - - dotenvy::from_filename("../.env.test").expect("Could not load .env.test file"); - - use std::any::Any; - - use alloy::sol; - // let anvil = Anvil::new().port(3000_u16).fork("https://eth.llamarpc.com").fork_block_number(block_no - 1).try_spawn() - // .expect("Unable to spawn Anvil"); - use url::Url; - - use crate::clients::interfaces::validity_interface::StarknetValidityContract::stateBlockNumberReturn; - - // // https://github.dev/alloy-rs/alloy - let provider = ProviderBuilder::new().on_http(Url::from_str("http://localhost:3000").expect("dskj")); - // // provider.anvil_auto_impersonate_account(false).await.unwrap(); - - // // let gas = U256::from(1337); - // // provider.anvil_set_min_gas_price(gas).await.expect("could not set min gas "); - - // println!("BASE GAS PRICE : {}",provider.get_blob_base_fee().await.expect("could not get base gas price")); - - // // provider.anvil_set_balance(Address::from_str("0x6E9972213BF459853FA33E28Ab7219e9157C8d02").expect("lol"), U256::from(1000)).await.expect("couldn't set balance"); - // provider.anvil_set_balance(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("lol"), U256::from(1000000000)).await.expect("couldn't set balance"); + // Load ENV vars + dotenvy::from_filename("../.env.test").expect("Could not load .env.test file."); + let current_path = std::env::current_dir().unwrap().to_str().unwrap().to_string(); + // Setup Anvil + let _anvil = Anvil::new().port(3000_u16).fork("https://eth.llamarpc.com").fork_block_number(block_no - 1).try_spawn() + .expect("Could not spawn Anvil."); + // Setup Provider + let provider = ProviderBuilder::new().on_http(Url::from_str("http://localhost:3000").expect("Could not create provider.")); - + // Setup EthereumSettlementClient let settings_provider: DefaultSettingsProvider = DefaultSettingsProvider {}; - let ethereum_settlement_client = EthereumSettlementClient::with_test_settings(&settings_provider); + let ethereum_settlement_client = EthereumSettlementClient::with_test_settings(&settings_provider, provider.clone()); + + + // Setup operator account impersonation provider - .anvil_impersonate_account(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("sdjkvb")) + .anvil_impersonate_account(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("Could not impersonate account.")) .await .expect("sdcjb"); - println!( - "Balance : {}", - provider - .get_balance(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("sdjkvb")) - .await - .expect("could not get balance") - ); - - - - let provider = ProviderBuilder::new().on_http(Url::from_str("http://localhost:3000").expect("sdf")); // Create a contract instance. let contract = STARKNET_CORE_CONTRACT::new(Address::from_str("0xc662c410c0ecf747543f5ba90660f6abebd9c8c4").expect("sd"), provider.clone()); - // Call the contract, retrieve the total supply. - let blockhash = contract.stateBlockHash().call().await.unwrap(); - - let blockumber = contract.stateBlockNumber().call().await.unwrap(); - println!("CURRENT BLOCK NUMBER {}" , blockumber._0.to_string()); - println!("CURRENT BLOCK HASH {}" , blockhash._0.to_string()); - - - // println!("Anvil running at `{}`", anvil.endpoint()); - - let current_path = std::env::current_dir().unwrap().to_str().unwrap().to_string(); + // Call the contract, retrieve the current stateBlockNumber. + let prev_block_number = contract.stateBlockNumber().call().await.unwrap(); // Program Output let program_output_file_path = format!("{}{}{}{}", current_path.clone(), "/src/test_data/program_output/", block_no, ".txt"); - println!("{}", program_output_file_path); let mut program_output: Vec<[u8; 32]> = Vec::new(); { - let file = File::open(program_output_file_path).expect("can't read file"); + let file = File::open(program_output_file_path).expect("Failed to read program output file"); let reader = BufReader::new(file); for line in reader.lines() { @@ -143,66 +106,22 @@ async fn update_state_blob_works(#[case] block_no: u64) { let blob_data = fs::read_to_string(blob_data_file_path).expect("Failed to read the blob data txt file"); let blob_data_vec = vec![hex_string_to_u8_vec(&blob_data).unwrap()]; - println!( - "Balance : {}", - provider - .get_balance(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("sdjkvb")) - .await - .expect("could not get balance") - ); - // Sending transaction + // Calling update_state_with_blobs let update_state_result = ethereum_settlement_client .update_state_with_blobs(program_output, blob_data_vec) .await - .expect("update_state_with_blobs failed"); - - println!("{}", update_state_result); - assert!(!update_state_result.is_empty(), "No Transaction Hash"); - let txn = provider - .get_transaction_by_hash(FixedBytes::from_str(update_state_result.as_str()).expect("couln't convert")) - .await - .expect("did not get txn from hash"); - - // add delay dor 2 seconds - sleep(Duration::from_secs(2)).await; - if let Some(txxn) = txn { - // println!("{:?}", txxn); - - println!("{}", txxn.hash.to_string()); - println!("{}", txxn.from.to_string()); - - // let dsd = provider - // // .get_transaction_receipt(FixedBytes::from_str(update_state_result.as_str()).expect("vdf")) - // .get_block(BlockId::Number(BlockNumberOrTag::Pending), BlockTransactionsKind::Full) - // .await - // .expect(":vdd"); - // println!(" reciept {:#?}", dsd); + .expect("Could not go through update_state_with_blobs."); - // println!("{:?}", txxn.blob_versioned_hashes); - println!( - "Balance : {}", - provider - .get_balance(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("sdjkvb")) - .await - .expect("could not get balance") - ); + // Asserting, Expected to receive transaction hash. + assert!(!update_state_result.is_empty(), "No transaction Hash received."); - + // Call the contract, retrieve the latest stateBlockNumber. + let latest_block_number = contract.stateBlockNumber().call().await.unwrap(); - let provider = ProviderBuilder::new().on_http(Url::from_str("http://localhost:3000").expect("sdf")); + println!("PREVIOUS BLOCK NUMBER {}" , prev_block_number._0.to_string()); + println!("CURRENT BLOCK HASH {}" , latest_block_number._0.to_string()); - // Create a contract instance. - let contract = STARKNET_CORE_CONTRACT::new(Address::from_str("0xc662c410c0ecf747543f5ba90660f6abebd9c8c4").expect("sd"), provider.clone()); - - // Call the contract, retrieve the total supply. - let blockhash = contract.stateBlockHash().call().await.unwrap(); - - let blockumber = contract.stateBlockNumber().call().await.unwrap(); - println!("CURRENT BLOCK NUMBER {}" , blockumber._0.to_string()); - println!("CURRENT BLOCK HASH {}" , blockhash._0.to_string()); - - - } + assert_eq!(prev_block_number._0.as_u32() +1 , latest_block_number._0.as_u32()); } From d562e4c6da31a97d0f3cdf900ab05206bd131560 Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Fri, 9 Aug 2024 12:59:01 +0530 Subject: [PATCH 11/41] chore: liniting fixes --- crates/settlement-clients/ethereum/src/lib.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index 78352c72..a71f5604 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -214,24 +214,27 @@ impl SettlementClient for EthereumSettlementClient { input: Bytes::from(hex::decode("0xb72d42a100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000001706ac7b2661801b4c0733da6ed1d2910b3b97259534ca95a63940932513111fba028bccc051eaae1b9a69b53e64a68021233b4dee2030aeda4be886324b3fbb3e00000000000000000000000000000000000000000000000000000000000a29b8070626a88de6a77855ecd683757207cdd18ba56553dca6c0c98ec523b827bee005ba2078240f1585f96424c2d1ee48211da3b3f9177bf2b9880b4fc91d59e9a2000000000000000000000000000000000000000000000000000000000000000100000000000000002b4e335bc41dc46c71f29928a5094a8c96a0c3536cabe53e0000000000000000810abb1929a0d45cdd62a20f9ccfd5807502334e7deb35d404c86d8b63a5741770fefca2f9b8efb7e663d89097edb3c60595b236f6e78e6f000000000000000000000000000000004a4b8a979fefc4d6b82e030fb082ca98000000000000000000000000000000004e8371c6774260e87b92447d4a2b0e170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000bf67f59d2988a46fbff7ed79a621778a3cd3985b0088eedbe2fe3918b69ccb411713b7fa72079d4eddf291103ccbe41e78a9615c0000000000000000000000000000000000000000000000000000000000194fe601b64b1b3b690b43b9b514fb81377518f4039cd3e4f4914d8a6bdf01d679fb1900000000000000000000000000000000000000000000000000000000000000050000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca000000000000000000000000012ccc443d39da45e5f640b3e71f0c7502152dbac01d4988e248d342439aa025b302e1f07595f6a5c810dcce23e7379e48f05d4cf000000000000000000000000000000000000000000000007f189b5374ad2a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030ab015987628cffee3ef99b9768ef8ca12c6244525f0cd10310046eaa21291b5aca164d044c5b4ad7212c767b165ed5e300000000000000000000000000000000").unwrap()), }; - let tx_sidecar = TxEip4844WithSidecar { tx: tx.clone(), sidecar: sidecar.clone() }; let mut variant = TxEip4844Variant::from(tx_sidecar); let signature = self.wallet.default_signer().sign_transaction(&mut variant).await?; let tx_signed = variant.into_signed(signature); let tx_envelope: TxEnvelope = tx_signed.into(); - let encoded = tx_envelope.encoded_2718(); + // IMP: this conversion strips signature from the transaction + let mut txn_request : TransactionRequest = tx_envelope.into(); + + if cfg!(test) { - // Sign and submit - let mut txn_request : TransactionRequest = tx_envelope.into(); txn_request.set_nonce(666068); txn_request = txn_request.with_from(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("Unable to impersonate operator.")); let pending_transaction = self.provider.send_transaction(txn_request).await?; return Ok(pending_transaction.tx_hash().to_string()); } - let pending_transaction = self.provider.send_raw_transaction(&encoded).await?; + // let encoded = tx_envelope.encoded_2718(); + // let pending_tx = self.provider.send_raw_transaction(&encoded).await?; + + let pending_transaction = self.provider.send_transaction(txn_request).await?; return Ok(pending_transaction.tx_hash().to_string()); } From 69cc0b717647f45c83d5eef1616354292253f440 Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Mon, 12 Aug 2024 15:33:48 +0530 Subject: [PATCH 12/41] update: linting fixes --- .../settlement-clients/ethereum/src/config.rs | 6 +-- crates/settlement-clients/ethereum/src/lib.rs | 42 +++++++++-------- .../ethereum/src/tests/mod.rs | 45 ++++++++++--------- 3 files changed, 47 insertions(+), 46 deletions(-) diff --git a/crates/settlement-clients/ethereum/src/config.rs b/crates/settlement-clients/ethereum/src/config.rs index 569fea6f..fc860be3 100644 --- a/crates/settlement-clients/ethereum/src/config.rs +++ b/crates/settlement-clients/ethereum/src/config.rs @@ -7,8 +7,8 @@ use utils::env_utils::get_env_var_or_panic; pub const ENV_ETHEREUM_RPC_URL: &str = "ETHEREUM_RPC_URL"; pub const ENV_CORE_CONTRACT_ADDRESS: &str = "STARKNET_SOLIDITY_CORE_CONTRACT_ADDRESS"; -pub const DEFAULT_SETTLEMENT_CLIENT_RPC : &str = "DEFAULT_SETTLEMENT_CLIENT_RPC"; -pub const DEFAULT_L1_CORE_CONTRACT_ADDRESS : &str = "DEFAULT_L1_CORE_CONTRACT_ADDRESS"; +pub const DEFAULT_SETTLEMENT_CLIENT_RPC: &str = "DEFAULT_SETTLEMENT_CLIENT_RPC"; +pub const DEFAULT_L1_CORE_CONTRACT_ADDRESS: &str = "DEFAULT_L1_CORE_CONTRACT_ADDRESS"; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct EthereumSettlementConfig { @@ -29,7 +29,7 @@ impl Default for EthereumSettlementConfig { fn default() -> Self { Self { rpc_url: get_env_var_or_panic(DEFAULT_SETTLEMENT_CLIENT_RPC).parse().unwrap(), - core_contract_address: get_env_var_or_panic(DEFAULT_L1_CORE_CONTRACT_ADDRESS).into(), + core_contract_address: get_env_var_or_panic(DEFAULT_L1_CORE_CONTRACT_ADDRESS), } } } diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index a71f5604..02e5796e 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -2,7 +2,9 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use std::sync::Arc; -use alloy::{node_bindings::Anvil, providers::ProviderBuilder, sol}; +use alloy::consensus::{ + BlobTransactionSidecar, SignableTransaction, TxEip4844, TxEip4844Variant, TxEip4844WithSidecar, TxEnvelope, +}; use alloy::{ network::EthereumWallet, primitives::{Address, B256, U256}, @@ -10,39 +12,30 @@ use alloy::{ rpc::types::TransactionReceipt, signers::local::PrivateKeySigner, }; -use alloy::consensus::{ - BlobTransactionSidecar, SignableTransaction, TxEip4844, TxEip4844Variant, TxEip4844WithSidecar, TxEnvelope -}; + // use eyre::Result; -use alloy::eips::eip2718::Encodable2718; use alloy::eips::eip2930::AccessList; use alloy::eips::eip4844::BYTES_PER_BLOB; use alloy::hex; use alloy::network::TransactionBuilder; -use alloy::providers::ext::AnvilApi; // use alloy::node_bindings::Anvil; -use alloy::providers::layers::AnvilProvider; -use alloy::providers::RootProvider; use alloy::rpc::types::TransactionRequest; -use alloy::transports::http::Http; use alloy_primitives::Bytes; use async_trait::async_trait; use c_kzg::{Blob, Bytes32, KzgCommitment, KzgProof, KzgSettings}; use color_eyre::eyre::eyre; use color_eyre::Result; use mockall::{automock, lazy_static, predicate::*}; -use reqwest::Client; +use alloy::providers::ProviderBuilder; use conversion::prepare_sidecar; -use settlement_client_interface::{SETTLEMENT_SETTINGS_NAME, SettlementClient, SettlementVerificationStatus}; -use types::EthHttpProvider; +use settlement_client_interface::{SettlementClient, SettlementVerificationStatus, SETTLEMENT_SETTINGS_NAME}; use utils::{env_utils::get_env_var_or_panic, settings::SettingsProvider}; use crate::clients::interfaces::validity_interface::StarknetValidityContractTrait; use crate::clients::StarknetValidityContractClient; use crate::config::EthereumSettlementConfig; use crate::conversion::{slice_slice_u8_to_vec_u256, slice_u8_to_u256}; - pub mod clients; pub mod config; pub mod conversion; @@ -50,6 +43,12 @@ pub mod conversion; mod tests; pub mod types; +#[cfg(test)] +use {alloy::providers::RootProvider, alloy::transports::http::Http, reqwest::Client}; + +#[cfg(not(test))] +use types::EthHttpProvider; + pub const ENV_PRIVATE_KEY: &str = "ETHEREUM_PRIVATE_KEY"; lazy_static! { @@ -60,8 +59,6 @@ lazy_static! { .expect("Error loading trusted setup file"); } - - #[allow(dead_code)] pub struct EthereumSettlementClient { core_contract_client: StarknetValidityContractClient, @@ -95,14 +92,14 @@ impl EthereumSettlementClient { } #[cfg(test)] - pub fn with_test_settings(settings: &impl SettingsProvider, provider : RootProvider>) -> Self { + pub fn with_test_settings(settings: &impl SettingsProvider, provider: RootProvider>) -> Self { let settlement_cfg: EthereumSettlementConfig = settings.get_settings(SETTLEMENT_SETTINGS_NAME).unwrap(); let private_key = get_env_var_or_panic(ENV_PRIVATE_KEY); let signer: PrivateKeySigner = private_key.parse().expect("Failed to parse private key"); let wallet_address = signer.address(); let wallet = EthereumWallet::from(signer); - + let fill_provider = Arc::new( ProviderBuilder::new().with_recommended_fillers().wallet(wallet.clone()).on_http(settlement_cfg.rpc_url), ); @@ -189,7 +186,7 @@ impl SettlementClient for EthereumSettlementClient { max_fee_per_blob_gas += 12; let max_priority_fee_per_gas: u128 = self.provider.get_max_priority_fee_per_gas().await?.to_string().parse()?; let nonce = self.provider.get_transaction_count(self.wallet_address).await?.to_string().parse()?; - + // x_0_value : program_output[6] let kzg_proof = Self::build_proof( state_diff, @@ -220,13 +217,14 @@ impl SettlementClient for EthereumSettlementClient { let tx_signed = variant.into_signed(signature); let tx_envelope: TxEnvelope = tx_signed.into(); // IMP: this conversion strips signature from the transaction - let mut txn_request : TransactionRequest = tx_envelope.into(); - - + let mut txn_request: TransactionRequest = tx_envelope.into(); if cfg!(test) { txn_request.set_nonce(666068); - txn_request = txn_request.with_from(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("Unable to impersonate operator.")); + txn_request = txn_request.with_from( + Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7") + .expect("Unable to impersonate operator."), + ); let pending_transaction = self.provider.send_transaction(txn_request).await?; return Ok(pending_transaction.tx_hash().to_string()); } diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index 5312ce48..df44811a 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -1,20 +1,17 @@ +use alloy::{node_bindings::Anvil, sol}; +use std::io::BufRead; use std::{ fs::{self, File}, io::BufReader, str::FromStr, }; -use std::io::BufRead; -use std::time::Duration; -use alloy::{node_bindings::Anvil, sol}; use url::Url; -use alloy::{primitives::U256, providers::Provider}; use alloy::providers::{ext::AnvilApi, ProviderBuilder}; +use alloy::{primitives::U256, providers::Provider}; use alloy_primitives::Address; -use alloy_primitives::FixedBytes; use color_eyre::eyre::eyre; use rstest::*; -use tokio::time::sleep; use settlement_client_interface::SettlementClient; use utils::settings::default::DefaultSettingsProvider; @@ -38,7 +35,7 @@ fn hex_string_to_u8_vec(hex_str: &str) -> color_eyre::Result> { Ok(result) } - // Codegen from ABI file to interact with the contract. +// Codegen from ABI file to interact with the contract. sol!( #[allow(missing_docs)] #[sol(rpc)] @@ -52,31 +49,39 @@ sol!( #[tokio::test] #[case::basic(20468828)] async fn update_state_blob_works(#[case] block_no: u64) { - // Load ENV vars dotenvy::from_filename("../.env.test").expect("Could not load .env.test file."); let current_path = std::env::current_dir().unwrap().to_str().unwrap().to_string(); // Setup Anvil - let _anvil = Anvil::new().port(3000_u16).fork("https://eth.llamarpc.com").fork_block_number(block_no - 1).try_spawn() - .expect("Could not spawn Anvil."); + let _anvil = Anvil::new() + .port(3000_u16) + .fork("https://eth.llamarpc.com") + .fork_block_number(block_no - 1) + .try_spawn() + .expect("Could not spawn Anvil."); // Setup Provider - let provider = ProviderBuilder::new().on_http(Url::from_str("http://localhost:3000").expect("Could not create provider.")); - + let provider = + ProviderBuilder::new().on_http(Url::from_str("http://localhost:3000").expect("Could not create provider.")); + // Setup EthereumSettlementClient let settings_provider: DefaultSettingsProvider = DefaultSettingsProvider {}; let ethereum_settlement_client = EthereumSettlementClient::with_test_settings(&settings_provider, provider.clone()); - - + // Setup operator account impersonation provider - .anvil_impersonate_account(Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("Could not impersonate account.")) + .anvil_impersonate_account( + Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("Could not impersonate account."), + ) .await .expect("sdcjb"); // Create a contract instance. - let contract = STARKNET_CORE_CONTRACT::new(Address::from_str("0xc662c410c0ecf747543f5ba90660f6abebd9c8c4").expect("sd"), provider.clone()); + let contract = STARKNET_CORE_CONTRACT::new( + Address::from_str("0xc662c410c0ecf747543f5ba90660f6abebd9c8c4").expect("sd"), + provider.clone(), + ); // Call the contract, retrieve the current stateBlockNumber. let prev_block_number = contract.stateBlockNumber().call().await.unwrap(); @@ -106,22 +111,20 @@ async fn update_state_blob_works(#[case] block_no: u64) { let blob_data = fs::read_to_string(blob_data_file_path).expect("Failed to read the blob data txt file"); let blob_data_vec = vec![hex_string_to_u8_vec(&blob_data).unwrap()]; - // Calling update_state_with_blobs let update_state_result = ethereum_settlement_client .update_state_with_blobs(program_output, blob_data_vec) .await .expect("Could not go through update_state_with_blobs."); - // Asserting, Expected to receive transaction hash. assert!(!update_state_result.is_empty(), "No transaction Hash received."); // Call the contract, retrieve the latest stateBlockNumber. let latest_block_number = contract.stateBlockNumber().call().await.unwrap(); - println!("PREVIOUS BLOCK NUMBER {}" , prev_block_number._0.to_string()); - println!("CURRENT BLOCK HASH {}" , latest_block_number._0.to_string()); + println!("PREVIOUS BLOCK NUMBER {}", prev_block_number._0); + println!("CURRENT BLOCK HASH {}", latest_block_number._0); - assert_eq!(prev_block_number._0.as_u32() +1 , latest_block_number._0.as_u32()); + assert_eq!(prev_block_number._0.as_u32() + 1, latest_block_number._0.as_u32()); } From 46fc944daa55c2f51734b5cd2bdf75c7e661cc67 Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Mon, 12 Aug 2024 15:37:50 +0530 Subject: [PATCH 13/41] docs: changelog --- CHANGELOG.md | 1 + Cargo.lock | 356 ++++++++++++++++++++++----------------------------- 2 files changed, 153 insertions(+), 204 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f2a02f2..7d317260 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Added tests for state update job. - Tests for DA job. - Database tests +- Tests for Settlement client. ## Changed diff --git a/Cargo.lock b/Cargo.lock index cce7126f..bd538040 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -87,28 +87,28 @@ dependencies = [ [[package]] name = "alloy" -version = "0.2.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f4a4aaae80afd4be443a6aecd92a6b255dcdd000f97996928efb33d8a71e100" +checksum = "9134b68e24175eff6c3c4d2bffeefb0a1b7435462130862c88d1524ca376e7e5" dependencies = [ - "alloy-consensus 0.2.1", + "alloy-consensus 0.1.2", "alloy-contract", - "alloy-core 0.7.7", - "alloy-eips 0.2.1", + "alloy-core 0.7.6", + "alloy-eips 0.1.2", "alloy-genesis", - "alloy-network 0.2.1", - "alloy-node-bindings", - "alloy-provider 0.2.1", + "alloy-network 0.1.2", + "alloy-provider 0.1.2", "alloy-pubsub", - "alloy-rpc-client 0.2.1", - "alloy-rpc-types 0.2.1", - "alloy-serde 0.2.1", - "alloy-signer 0.2.1", + "alloy-rpc-client 0.1.2", + "alloy-rpc-types 0.1.2", + "alloy-serde 0.1.2", + "alloy-signer 0.1.2", "alloy-signer-local", - "alloy-transport 0.2.1", - "alloy-transport-http 0.2.1", + "alloy-transport 0.1.2", + "alloy-transport-http 0.1.2", "alloy-transport-ipc", "alloy-transport-ws", + "reqwest 0.12.5", ] [[package]] @@ -134,34 +134,33 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "0.2.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04c309895995eaa4bfcc345f5515a39c7df9447798645cc8bf462b6c5bf1dc96" +checksum = "a016bfa21193744d4c38b3f3ab845462284d129e5e23c7cc0fafca7e92d9db37" dependencies = [ - "alloy-eips 0.2.1", - "alloy-primitives 0.7.7", + "alloy-eips 0.1.2", + "alloy-primitives 0.7.6", "alloy-rlp", - "alloy-serde 0.2.1", + "alloy-serde 0.1.2", "c-kzg", "serde", ] [[package]] name = "alloy-contract" -version = "0.2.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f4e0ef72b0876ae3068b2ed7dfae9ae1779ce13cfaec2ee1f08f5bd0348dc57" +checksum = "e47b2a620fd588d463ccf0f5931b41357664b293a8d31592768845a2a101bb9e" dependencies = [ - "alloy-dyn-abi 0.7.7", - "alloy-json-abi 0.7.7", - "alloy-network 0.2.1", - "alloy-network-primitives", - "alloy-primitives 0.7.7", - "alloy-provider 0.2.1", + "alloy-dyn-abi 0.7.6", + "alloy-json-abi 0.7.6", + "alloy-network 0.1.2", + "alloy-primitives 0.7.6", + "alloy-provider 0.1.2", "alloy-pubsub", "alloy-rpc-types-eth", - "alloy-sol-types 0.7.7", - "alloy-transport 0.2.1", + "alloy-sol-types 0.7.6", + "alloy-transport 0.1.2", "futures", "futures-util", "thiserror", @@ -181,14 +180,14 @@ dependencies = [ [[package]] name = "alloy-core" -version = "0.7.7" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "529fc6310dc1126c8de51c376cbc59c79c7f662bd742be7dc67055d5421a81b4" +checksum = "5af3faff14c12c8b11037e0a093dd157c3702becb8435577a2408534d0758315" dependencies = [ - "alloy-dyn-abi 0.7.7", - "alloy-json-abi 0.7.7", - "alloy-primitives 0.7.7", - "alloy-sol-types 0.7.7", + "alloy-dyn-abi 0.7.6", + "alloy-json-abi 0.7.6", + "alloy-primitives 0.7.6", + "alloy-sol-types 0.7.6", ] [[package]] @@ -210,14 +209,14 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "0.7.7" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413902aa18a97569e60f679c23f46a18db1656d87ab4d4e49d0e1e52042f66df" +checksum = "cb6e6436a9530f25010d13653e206fab4c9feddacf21a54de8d7311b275bc56b" dependencies = [ - "alloy-json-abi 0.7.7", - "alloy-primitives 0.7.7", - "alloy-sol-type-parser 0.7.7", - "alloy-sol-types 0.7.7", + "alloy-json-abi 0.7.6", + "alloy-primitives 0.7.6", + "alloy-sol-type-parser 0.7.6", + "alloy-sol-types 0.7.6", "const-hex", "itoa", "serde", @@ -240,16 +239,15 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "0.2.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9431c99a3b3fe606ede4b3d4043bdfbcb780c45b8d8d226c3804e2b75cfbe68" +checksum = "32d6d8118b83b0489cfb7e6435106948add2b35217f4a5004ef895f613f60299" dependencies = [ - "alloy-primitives 0.7.7", + "alloy-primitives 0.7.6", "alloy-rlp", - "alloy-serde 0.2.1", + "alloy-serde 0.1.2", "c-kzg", "derive_more", - "k256", "once_cell", "serde", "sha2", @@ -257,12 +255,12 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "0.2.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79614dfe86144328da11098edcc7bc1a3f25ad8d3134a9eb9e857e06f0d9840d" +checksum = "894f33a7822abb018db56b10ab90398e63273ce1b5a33282afd186c132d764a6" dependencies = [ - "alloy-primitives 0.7.7", - "alloy-serde 0.2.1", + "alloy-primitives 0.7.6", + "alloy-serde 0.1.2", "serde", ] @@ -280,12 +278,12 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.7.7" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc05b04ac331a9f07e3a4036ef7926e49a8bf84a99a1ccfc7e2ab55a5fcbb372" +checksum = "aaeaccd50238126e3a0ff9387c7c568837726ad4f4e399b528ca88104d6c25ef" dependencies = [ - "alloy-primitives 0.7.7", - "alloy-sol-type-parser 0.7.7", + "alloy-primitives 0.7.6", + "alloy-sol-type-parser 0.7.6", "serde", "serde_json", ] @@ -303,12 +301,11 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "0.2.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57e2865c4c3bb4cdad3f0d9ec1ab5c0c657ba69a375651bd35e32fb6c180ccc2" +checksum = "61f0ae6e93b885cc70fe8dae449e7fd629751dbee8f59767eaaa7285333c5727" dependencies = [ - "alloy-primitives 0.7.7", - "alloy-sol-types 0.7.7", + "alloy-primitives 0.7.6", "serde", "serde_json", "thiserror", @@ -334,52 +331,24 @@ dependencies = [ [[package]] name = "alloy-network" -version = "0.2.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e701fc87ef9a3139154b0b4ccb935b565d27ffd9de020fe541bf2dec5ae4ede" +checksum = "dc122cbee2b8523854cc11d87bcd5773741602c553d2d2d106d82eeb9c16924a" dependencies = [ - "alloy-consensus 0.2.1", - "alloy-eips 0.2.1", - "alloy-json-rpc 0.2.1", - "alloy-network-primitives", - "alloy-primitives 0.7.7", + "alloy-consensus 0.1.2", + "alloy-eips 0.1.2", + "alloy-json-rpc 0.1.2", + "alloy-primitives 0.7.6", "alloy-rpc-types-eth", - "alloy-serde 0.2.1", - "alloy-signer 0.2.1", - "alloy-sol-types 0.7.7", + "alloy-serde 0.1.2", + "alloy-signer 0.1.2", + "alloy-sol-types 0.7.6", "async-trait", "auto_impl", "futures-utils-wasm", "thiserror", ] -[[package]] -name = "alloy-network-primitives" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec9d5a0f9170b10988b6774498a022845e13eda94318440d17709d50687f67f9" -dependencies = [ - "alloy-primitives 0.7.7", - "alloy-serde 0.2.1", - "serde", -] - -[[package]] -name = "alloy-node-bindings" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16faebb9ea31a244fd6ce3288d47df4be96797d9c3c020144b8f2c31543a4512" -dependencies = [ - "alloy-genesis", - "alloy-primitives 0.7.7", - "k256", - "serde_json", - "tempfile", - "thiserror", - "tracing", - "url", -] - [[package]] name = "alloy-primitives" version = "0.6.4" @@ -404,9 +373,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.7.7" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccb3ead547f4532bc8af961649942f0b9c16ee9226e26caa3f38420651cc0bf4" +checksum = "f783611babedbbe90db3478c120fb5f5daacceffc210b39adc0af4fe0da70bad" dependencies = [ "alloy-rlp", "bytes", @@ -451,25 +420,21 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "0.2.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9c0ab10b93de601a6396fc7ff2ea10d3b28c46f079338fa562107ebf9857c8" +checksum = "3d5af289798fe8783acd0c5f10644d9d26f54a12bc52a083e4f3b31718e9bf92" dependencies = [ "alloy-chains", - "alloy-consensus 0.2.1", - "alloy-eips 0.2.1", - "alloy-json-rpc 0.2.1", - "alloy-network 0.2.1", - "alloy-network-primitives", - "alloy-node-bindings", - "alloy-primitives 0.7.7", + "alloy-consensus 0.1.2", + "alloy-eips 0.1.2", + "alloy-json-rpc 0.1.2", + "alloy-network 0.1.2", + "alloy-primitives 0.7.6", "alloy-pubsub", - "alloy-rpc-client 0.2.1", - "alloy-rpc-types-anvil", + "alloy-rpc-client 0.1.2", "alloy-rpc-types-eth", - "alloy-signer-local", - "alloy-transport 0.2.1", - "alloy-transport-http 0.2.1", + "alloy-transport 0.1.2", + "alloy-transport-http 0.1.2", "alloy-transport-ipc", "alloy-transport-ws", "async-stream", @@ -490,13 +455,13 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "0.2.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f5da2c55cbaf229bad3c5f8b00b5ab66c74ef093e5f3a753d874cfecf7d2281" +checksum = "702f330b7da123a71465ab9d39616292f8344a2811c28f2cc8d8438a69d79e35" dependencies = [ - "alloy-json-rpc 0.2.1", - "alloy-primitives 0.7.7", - "alloy-transport 0.2.1", + "alloy-json-rpc 0.1.2", + "alloy-primitives 0.7.6", + "alloy-transport 0.1.2", "bimap", "futures", "serde", @@ -551,15 +516,15 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "0.2.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b38e3ffdb285df5d9f60cb988d336d9b8e3505acb78750c3bc60336a7af41d3" +checksum = "b40fcb53b2a9d0a78a4968b2eca8805a4b7011b9ee3fdfa2acaf137c5128f36b" dependencies = [ - "alloy-json-rpc 0.2.1", - "alloy-primitives 0.7.7", + "alloy-json-rpc 0.1.2", + "alloy-primitives 0.7.6", "alloy-pubsub", - "alloy-transport 0.2.1", - "alloy-transport-http 0.2.1", + "alloy-transport 0.1.2", + "alloy-transport-http 0.1.2", "alloy-transport-ipc", "alloy-transport-ws", "futures", @@ -604,39 +569,27 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "0.2.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c31a3750b8f5a350d17354e46a52b0f2f19ec5f2006d816935af599dedc521" +checksum = "50f2fbe956a3e0f0975c798f488dc6be96b669544df3737e18f4a325b42f4c86" dependencies = [ "alloy-rpc-types-engine", "alloy-rpc-types-eth", - "alloy-serde 0.2.1", - "serde", -] - -[[package]] -name = "alloy-rpc-types-anvil" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ab6509cd38b2e8c8da726e0f61c1e314a81df06a38d37ddec8bced3f8d25ed" -dependencies = [ - "alloy-primitives 0.7.7", - "alloy-serde 0.2.1", - "serde", + "alloy-serde 0.1.2", ] [[package]] name = "alloy-rpc-types-engine" -version = "0.2.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff63f51b2fb2f547df5218527fd0653afb1947bf7fead5b3ce58c75d170b30f7" +checksum = "cd473d98ec552f8229cd6d566bd2b0bbfc5bb4efcefbb5288c834aa8fd832020" dependencies = [ - "alloy-consensus 0.2.1", - "alloy-eips 0.2.1", - "alloy-primitives 0.7.7", + "alloy-consensus 0.1.2", + "alloy-eips 0.1.2", + "alloy-primitives 0.7.6", "alloy-rlp", "alloy-rpc-types-eth", - "alloy-serde 0.2.1", + "alloy-serde 0.1.2", "jsonwebtoken", "rand", "serde", @@ -645,17 +598,16 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "0.2.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81e18424d962d7700a882fe423714bd5b9dde74c7a7589d4255ea64068773aef" +checksum = "083f443a83b9313373817236a8f4bea09cca862618e9177d822aee579640a5d6" dependencies = [ - "alloy-consensus 0.2.1", - "alloy-eips 0.2.1", - "alloy-network-primitives", - "alloy-primitives 0.7.7", + "alloy-consensus 0.1.2", + "alloy-eips 0.1.2", + "alloy-primitives 0.7.6", "alloy-rlp", - "alloy-serde 0.2.1", - "alloy-sol-types 0.7.7", + "alloy-serde 0.1.2", + "alloy-sol-types 0.7.6", "itertools 0.13.0", "serde", "serde_json", @@ -674,11 +626,11 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "0.2.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33feda6a53e6079895aed1d08dcb98a1377b000d80d16370fbbdb8155d547ef" +checksum = "d94da1c0c4e27cc344b05626fe22a89dc6b8b531b9475f3b7691dbf6913e4109" dependencies = [ - "alloy-primitives 0.7.7", + "alloy-primitives 0.7.6", "serde", "serde_json", ] @@ -698,11 +650,11 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "0.2.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740a25b92e849ed7b0fa013951fe2f64be9af1ad5abe805037b44fb7770c5c47" +checksum = "58d876be3afd8b78979540084ff63995292a26aa527ad0d44276405780aa0ffd" dependencies = [ - "alloy-primitives 0.7.7", + "alloy-primitives 0.7.6", "async-trait", "auto_impl", "elliptic-curve 0.13.8", @@ -712,14 +664,14 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "0.2.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b0707d4f63e4356a110b30ef3add8732ab6d181dd7be4607bf79b8777105cee" +checksum = "d40a37dc216c269b8a7244047cb1c18a9c69f7a0332ab2c4c2aa4cbb1a31468b" dependencies = [ - "alloy-consensus 0.2.1", - "alloy-network 0.2.1", - "alloy-primitives 0.7.7", - "alloy-signer 0.2.1", + "alloy-consensus 0.1.2", + "alloy-network 0.1.2", + "alloy-primitives 0.7.6", + "alloy-signer 0.1.2", "async-trait", "k256", "rand", @@ -761,9 +713,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.7.7" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b40397ddcdcc266f59f959770f601ce1280e699a91fc1862f29cef91707cd09" +checksum = "4bad41a7c19498e3f6079f7744656328699f8ea3e783bdd10d85788cd439f572" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -775,11 +727,11 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "0.7.7" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "867a5469d61480fea08c7333ffeca52d5b621f5ca2e44f271b117ec1fc9a0525" +checksum = "fd9899da7d011b4fe4c406a524ed3e3f963797dbc93b45479d60341d3a27b252" dependencies = [ - "alloy-json-abi 0.7.7", + "alloy-json-abi 0.7.6", "alloy-sol-macro-input", "const-hex", "heck 0.5.0", @@ -788,17 +740,17 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.66", - "syn-solidity 0.7.7", + "syn-solidity 0.7.6", "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "0.7.7" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e482dc33a32b6fadbc0f599adea520bd3aaa585c141a80b404d0a3e3fa72528" +checksum = "d32d595768fdc61331a132b6f65db41afae41b9b97d36c21eb1b955c422a7e60" dependencies = [ - "alloy-json-abi 0.7.7", + "alloy-json-abi 0.7.6", "const-hex", "dunce", "heck 0.5.0", @@ -806,7 +758,7 @@ dependencies = [ "quote", "serde_json", "syn 2.0.66", - "syn-solidity 0.7.7", + "syn-solidity 0.7.6", ] [[package]] @@ -820,11 +772,10 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "0.7.7" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbcba3ca07cf7975f15d871b721fb18031eec8bce51103907f6dcce00b255d98" +checksum = "baa2fbd22d353d8685bd9fee11ba2d8b5c3b1d11e56adb3265fcf1f32bfdf404" dependencies = [ - "serde", "winnow 0.6.13", ] @@ -842,13 +793,13 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "0.7.7" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91ca40fa20793ae9c3841b83e74569d1cc9af29a2f5237314fd3452d51e38c7" +checksum = "a49042c6d3b66a9fe6b2b5a8bf0d39fc2ae1ee0310a2a26ffedd79fb097878dd" dependencies = [ - "alloy-json-abi 0.7.7", - "alloy-primitives 0.7.7", - "alloy-sol-macro 0.7.7", + "alloy-json-abi 0.7.6", + "alloy-primitives 0.7.6", + "alloy-sol-macro 0.7.6", "const-hex", "serde", ] @@ -873,11 +824,11 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "0.2.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0590afbdacf2f8cca49d025a2466f3b6584a016a8b28f532f29f8da1007bae" +checksum = "245af9541f0a0dbd5258669c80dfe3af118164cacec978a520041fc130550deb" dependencies = [ - "alloy-json-rpc 0.2.1", + "alloy-json-rpc 0.1.2", "base64 0.22.1", "futures-util", "futures-utils-wasm", @@ -886,7 +837,6 @@ dependencies = [ "thiserror", "tokio", "tower", - "tracing", "url", ] @@ -905,12 +855,12 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "0.2.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2437d145d80ea1aecde8574d2058cceb8b3c9cba05f6aea8e67907c660d46698" +checksum = "5619c017e1fdaa1db87f9182f4f0ed97c53d674957f4902fba655e972d359c6c" dependencies = [ - "alloy-json-rpc 0.2.1", - "alloy-transport 0.2.1", + "alloy-json-rpc 0.1.2", + "alloy-transport 0.1.2", "reqwest 0.12.5", "serde_json", "tower", @@ -920,13 +870,13 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "0.2.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804494366e20468776db4e18f9eb5db7db0fe14f1271eb6dbf155d867233405c" +checksum = "173cefa110afac7a53cf2e75519327761f2344d305eea2993f3af1b2c1fc1c44" dependencies = [ - "alloy-json-rpc 0.2.1", + "alloy-json-rpc 0.1.2", "alloy-pubsub", - "alloy-transport 0.2.1", + "alloy-transport 0.1.2", "bytes", "futures", "interprocess", @@ -939,12 +889,12 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "0.2.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af855163e7df008799941aa6dd324a43ef2bf264b08ba4b22d44aad6ced65300" +checksum = "9c0aff8af5be5e58856c5cdd1e46db2c67c7ecd3a652d9100b4822c96c899947" dependencies = [ "alloy-pubsub", - "alloy-transport 0.2.1", + "alloy-transport 0.1.2", "futures", "http 1.1.0", "rustls 0.23.10", @@ -4129,13 +4079,11 @@ dependencies = [ name = "ethereum-settlement-client" version = "0.1.0" dependencies = [ - "alloy 0.2.1", - "alloy-primitives 0.7.7", + "alloy 0.1.2", "async-trait", "c-kzg", "color-eyre", "dotenv", - "dotenvy", "mockall 0.12.1", "reqwest 0.12.5", "rstest 0.18.2", @@ -4672,7 +4620,7 @@ dependencies = [ name = "gps-fact-checker" version = "0.1.0" dependencies = [ - "alloy 0.2.1", + "alloy 0.1.2", "async-trait", "cairo-vm 1.0.0-rc3", "itertools 0.13.0", @@ -6366,7 +6314,7 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" name = "orchestrator" version = "0.1.0" dependencies = [ - "alloy 0.2.1", + "alloy 0.1.2", "arc-swap", "assert_matches", "async-std", @@ -8335,7 +8283,7 @@ dependencies = [ name = "sharp-service" version = "0.1.0" dependencies = [ - "alloy 0.2.1", + "alloy 0.1.2", "async-trait", "cairo-vm 1.0.0-rc3", "gps-fact-checker", @@ -9006,9 +8954,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.7.7" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c837dc8852cb7074e46b444afb81783140dab12c58867b49fb3898fbafedf7ea" +checksum = "8d71e19bca02c807c9faa67b5a47673ff231b6e7449b251695188522f1dc44b2" dependencies = [ "paste", "proc-macro2", From c6bfdcebf6574d63fbd5ef46f253f60f232b70b7 Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Tue, 13 Aug 2024 07:32:06 +0530 Subject: [PATCH 14/41] update: Nonce prefetch for state_update --- .../src/jobs/state_update_job/mod.rs | 17 +++++++++++++---- .../src/tests/jobs/state_update_job/mod.rs | 8 +++++--- crates/settlement-clients/ethereum/src/lib.rs | 15 ++++++++++++--- .../ethereum/src/tests/mod.rs | 6 ++++-- .../settlement-client-interface/src/lib.rs | 10 +++++++++- crates/settlement-clients/starknet/src/lib.rs | 14 ++++++++++++-- 6 files changed, 55 insertions(+), 15 deletions(-) diff --git a/crates/orchestrator/src/jobs/state_update_job/mod.rs b/crates/orchestrator/src/jobs/state_update_job/mod.rs index d60c86d1..77ca3f43 100644 --- a/crates/orchestrator/src/jobs/state_update_job/mod.rs +++ b/crates/orchestrator/src/jobs/state_update_job/mod.rs @@ -62,15 +62,18 @@ impl Job for StateUpdateJob { block_numbers = block_numbers.into_iter().filter(|&block| block >= last_failed_block).collect::>(); } + let mut nonce = config.settlement_client().get_nonce().await?; + let mut sent_tx_hashes: Vec = Vec::with_capacity(block_numbers.len()); for block_no in block_numbers.iter() { let snos = self.fetch_snos_for_block(*block_no).await; - let tx_hash = self.update_state_for_block(config, *block_no, snos).await.map_err(|e| { + let tx_hash = self.update_state_for_block(config, *block_no, snos, nonce).await.map_err(|e| { job.metadata.insert(JOB_METADATA_STATE_UPDATE_LAST_FAILED_BLOCK_NO.into(), block_no.to_string()); self.insert_attempts_into_metadata(job, &attempt_no, &sent_tx_hashes); eyre!("Block #{block_no} - Error occured during the state update: {e}") })?; sent_tx_hashes.push(tx_hash); + nonce += 1; } self.insert_attempts_into_metadata(job, &attempt_no, &sent_tx_hashes); @@ -190,15 +193,21 @@ impl StateUpdateJob { } /// Update the state for the corresponding block using the settlement layer. - async fn update_state_for_block(&self, config: &Config, block_no: u64, snos: StarknetOsOutput) -> Result { + async fn update_state_for_block( + &self, + config: &Config, + block_no: u64, + snos: StarknetOsOutput, + nonce: u64, + ) -> Result { let settlement_client = config.settlement_client(); let last_tx_hash_executed = if snos.use_kzg_da == Felt252::ZERO { unimplemented!("update_state_for_block not implemented as of now for calldata DA.") } else if snos.use_kzg_da == Felt252::ONE { let blob_data = fetch_blob_data_for_block(block_no).await?; - + // Fetching nonce before the transaction is run // Sending update_state transaction from the settlement client - settlement_client.update_state_with_blobs(vec![], blob_data).await? + settlement_client.update_state_with_blobs(vec![], blob_data, nonce).await? } else { return Err(eyre!("Block #{} - SNOS error, [use_kzg_da] should be either 0 or 1.", block_no)); }; diff --git a/crates/orchestrator/src/tests/jobs/state_update_job/mod.rs b/crates/orchestrator/src/tests/jobs/state_update_job/mod.rs index ae46cc21..e0c183b2 100644 --- a/crates/orchestrator/src/tests/jobs/state_update_job/mod.rs +++ b/crates/orchestrator/src/tests/jobs/state_update_job/mod.rs @@ -6,7 +6,7 @@ use bytes::Bytes; use httpmock::prelude::*; use mockall::predicate::eq; use rstest::*; -use settlement_client_interface::MockSettlementClient; +use settlement_client_interface::{MockSettlementClient, SettlementClient}; use color_eyre::eyre::eyre; @@ -178,11 +178,13 @@ async fn test_process_job() { .expect("Failed to read the blob data txt file"); storage_client.expect_get_data().with(eq(x_0_key)).returning(move |_| Ok(Bytes::from(x_0.clone()))); + let nonce = settlement_client.get_nonce().await.expect("Unable to fetch nonce for settlement client."); + settlement_client .expect_update_state_with_blobs() // TODO: vec![] is program_output - .with(eq(program_output), eq(state_diff)) - .returning(|_, _| Ok(String::from("0x5d17fac98d9454030426606019364f6e68d915b91f6210ef1e2628cd6987442"))); + .with(eq(program_output), eq(state_diff), eq(nonce)) + .returning(|_, _, _| Ok(String::from("0x5d17fac98d9454030426606019364f6e68d915b91f6210ef1e2628cd6987442"))); } let config_init = init_config( diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index 02e5796e..34a43691 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -23,7 +23,7 @@ use alloy::rpc::types::TransactionRequest; use alloy_primitives::Bytes; use async_trait::async_trait; use c_kzg::{Blob, Bytes32, KzgCommitment, KzgProof, KzgSettings}; -use color_eyre::eyre::eyre; +use color_eyre::eyre::{eyre, Ok}; use color_eyre::Result; use mockall::{automock, lazy_static, predicate::*}; @@ -172,7 +172,12 @@ impl SettlementClient for EthereumSettlementClient { Ok(format!("0x{:x}", tx_receipt.transaction_hash)) } - async fn update_state_with_blobs(&self, program_output: Vec<[u8; 32]>, state_diff: Vec>) -> Result { + async fn update_state_with_blobs( + &self, + program_output: Vec<[u8; 32]>, + state_diff: Vec>, + nonce: u64, + ) -> Result { //TODO: better file management let trusted_setup = KzgSettings::load_trusted_setup_file(Path::new("/Users/dexterhv/Work/Karnot/madara-alliance/madara-orchestrator/crates/settlement-clients/ethereum/src/trusted_setup.txt"))?; let (sidecar_blobs, sidecar_commitments, sidecar_proofs) = prepare_sidecar(&state_diff, &trusted_setup).await?; @@ -185,7 +190,6 @@ impl SettlementClient for EthereumSettlementClient { // TODO: need to send more than current gas price. max_fee_per_blob_gas += 12; let max_priority_fee_per_gas: u128 = self.provider.get_max_priority_fee_per_gas().await?.to_string().parse()?; - let nonce = self.provider.get_transaction_count(self.wallet_address).await?.to_string().parse()?; // x_0_value : program_output[6] let kzg_proof = Self::build_proof( @@ -264,4 +268,9 @@ impl SettlementClient for EthereumSettlementClient { let block_number = self.core_contract_client.state_block_number().await?; Ok(block_number.try_into()?) } + + async fn get_nonce(&self) -> Result { + let nonce = self.provider.get_transaction_count(self.wallet_address).await?.to_string().parse()?; + Ok(nonce) + } } diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index df44811a..325f300d 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -75,7 +75,9 @@ async fn update_state_blob_works(#[case] block_no: u64) { Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("Could not impersonate account."), ) .await - .expect("sdcjb"); + .expect("Unable to impersonate account."); + + let nonce = ethereum_settlement_client.get_nonce().await.expect("Unable to fetch nonce"); // Create a contract instance. let contract = STARKNET_CORE_CONTRACT::new( @@ -113,7 +115,7 @@ async fn update_state_blob_works(#[case] block_no: u64) { // Calling update_state_with_blobs let update_state_result = ethereum_settlement_client - .update_state_with_blobs(program_output, blob_data_vec) + .update_state_with_blobs(program_output, blob_data_vec, nonce) .await .expect("Could not go through update_state_with_blobs."); diff --git a/crates/settlement-clients/settlement-client-interface/src/lib.rs b/crates/settlement-clients/settlement-client-interface/src/lib.rs index 974b071a..655aa089 100644 --- a/crates/settlement-clients/settlement-client-interface/src/lib.rs +++ b/crates/settlement-clients/settlement-client-interface/src/lib.rs @@ -29,7 +29,12 @@ pub trait SettlementClient: Send + Sync { ) -> Result; /// Should be used to update state on contract and publish the blob on ethereum. - async fn update_state_with_blobs(&self, program_output: Vec<[u8; 32]>, state_diff: Vec>) -> Result; + async fn update_state_with_blobs( + &self, + program_output: Vec<[u8; 32]>, + state_diff: Vec>, + nonce: u64, + ) -> Result; /// Should be used to update state on core contract when DA is in blobs/alt DA async fn update_state_blobs(&self, program_output: Vec<[u8; 32]>, kzg_proof: [u8; 48]) -> Result; @@ -42,6 +47,9 @@ pub trait SettlementClient: Send + Sync { /// Should retrieves the last settled block in the settlement layer async fn get_last_settled_block(&self) -> Result; + + /// Should retrieve the latest transaction count to be used as nonce. + async fn get_nonce(&self) -> Result; } /// Trait for every new SettlementConfig to implement diff --git a/crates/settlement-clients/starknet/src/lib.rs b/crates/settlement-clients/starknet/src/lib.rs index abcd10df..e40c2927 100644 --- a/crates/settlement-clients/starknet/src/lib.rs +++ b/crates/settlement-clients/starknet/src/lib.rs @@ -4,7 +4,7 @@ pub mod conversion; use std::sync::Arc; use async_trait::async_trait; -use color_eyre::eyre::eyre; +use color_eyre::eyre::{eyre, Ok}; use color_eyre::Result; use lazy_static::lazy_static; use mockall::{automock, predicate::*}; @@ -156,7 +156,12 @@ impl SettlementClient for StarknetSettlementClient { /// Should be used to update state on core contract and publishing the blob simultaneously #[allow(unused)] - async fn update_state_with_blobs(&self, program_output: Vec<[u8; 32]>, state_diff: Vec>) -> Result { + async fn update_state_with_blobs( + &self, + program_output: Vec<[u8; 32]>, + state_diff: Vec>, + nonce: u64, + ) -> Result { !unimplemented!("not implemented yet.") } @@ -201,4 +206,9 @@ impl SettlementClient for StarknetSettlementClient { } Ok(block_number[0].try_into()?) } + + /// Returns the nonce for the wallet in use. + async fn get_nonce(&self) -> Result { + todo!("Yet to impl nonce call for Starknet.") + } } From f3fc8261b72059ad2ead4a96ab163f6faa20e7a8 Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Tue, 13 Aug 2024 16:06:30 +0530 Subject: [PATCH 15/41] update: creation of input_data works --- crates/orchestrator/src/jobs/da_job/mod.rs | 2 +- .../ethereum/src/conversion.rs | 40 ++++++++++++++ crates/settlement-clients/ethereum/src/lib.rs | 5 +- .../ethereum/src/tests/mod.rs | 52 ++++++++++++++++++- 4 files changed, 93 insertions(+), 6 deletions(-) diff --git a/crates/orchestrator/src/jobs/da_job/mod.rs b/crates/orchestrator/src/jobs/da_job/mod.rs index 5e49d75c..ef05934e 100644 --- a/crates/orchestrator/src/jobs/da_job/mod.rs +++ b/crates/orchestrator/src/jobs/da_job/mod.rs @@ -537,7 +537,7 @@ pub mod test { } } - fn vec_u8_to_hex_string(data: &[u8]) -> String { + pub fn vec_u8_to_hex_string(data: &[u8]) -> String { let hex_chars: Vec = data.iter().map(|byte| format!("{:02x}", byte)).collect(); let mut new_hex_chars = hex_chars.join(""); diff --git a/crates/settlement-clients/ethereum/src/conversion.rs b/crates/settlement-clients/ethereum/src/conversion.rs index 6423096c..9bc67687 100644 --- a/crates/settlement-clients/ethereum/src/conversion.rs +++ b/crates/settlement-clients/ethereum/src/conversion.rs @@ -1,3 +1,4 @@ +use alloy::dyn_abi::parser::Error; use alloy::eips::eip4844::BYTES_PER_BLOB; use alloy::primitives::Bytes; use alloy::primitives::FixedBytes; @@ -43,6 +44,45 @@ pub(crate) fn get_txn_input_bytes(program_output: Vec<[u8; 32]>, kzg_proof: [u8; Bytes::from(program_output_hex_string + &kzg_proof_hex_string + function_selector) } +/// Function to construct the transaction's `input data` for updating the state in the core contract. +/// HEX Concatenation: MethodId, Offset, length for program_output, lines count, program_output, length for kzg_proof, kzg_proof +/// All 64 chars, if lesser padded from left with 0s +pub fn get_input_data_for_eip_4844(program_output: Vec<[u8; 32]>, kzg_proof: [u8; 48]) -> Result { + // bytes4(keccak256(bytes("updateStateKzgDA(uint256[],bytes)"))) + let method_id_hex = "0xb72d42a1"; + + // offset for updateStateKzgDA is 64 + let offset: u64 = 64; + let offset_hex = format!("{:0>64x}", offset); + + // program_output + let program_output_length = program_output.len(); + let program_output_hex = vec_u8_32_to_hex_string(program_output); + + // length for program_output: 3*64 [offset, length, lines all have 64 char length] + length of program_output + let length_program_output = (3 * 64 + program_output_hex.len()) / 2; + let length_program_output_hex = format!("{:0>64x}", length_program_output); + + // lines count for program_output + let lines_count_hex = format!("{:0>64x}", program_output_length); + + // length of KZG proof + let length_kzg_hex = format!("{:0>64x}", kzg_proof.len()); + + // KZG proof + let kzg_proof_hex = u8_48_to_hex_string(kzg_proof); + + let input_data = method_id_hex.to_string() + + &offset_hex + + &length_program_output_hex + + &lines_count_hex + + &program_output_hex + + &length_kzg_hex + + &kzg_proof_hex; + + Ok(Bytes::from(input_data)) +} + pub(crate) fn vec_u8_32_to_hex_string(data: Vec<[u8; 32]>) -> String { data.into_iter().fold(String::new(), |mut output, arr| { // Convert the array to a hex string diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index 34a43691..cbed317a 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -112,7 +112,7 @@ impl EthereumSettlementClient { } /// Build kzg proof for the x_0 point evaluation - async fn build_proof(blob_data: Vec>, x_0_value: Bytes32) -> Result { + pub fn build_proof(blob_data: Vec>, x_0_value: Bytes32) -> Result { // Assuming that there is only one blob in the whole Vec> array for now. // Later we will add the support for multiple blob in single blob_data vec. assert_eq!(blob_data.len(), 1); @@ -196,7 +196,6 @@ impl SettlementClient for EthereumSettlementClient { state_diff, Bytes32::from_bytes(program_output[6].as_slice()).expect("Not able to get x_0 point params."), ) - .await .expect("Unable to build KZG proof for given params.") .to_owned(); @@ -229,8 +228,6 @@ impl SettlementClient for EthereumSettlementClient { Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7") .expect("Unable to impersonate operator."), ); - let pending_transaction = self.provider.send_transaction(txn_request).await?; - return Ok(pending_transaction.tx_hash().to_string()); } // let encoded = tx_envelope.encoded_2718(); diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index 325f300d..7103e599 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -7,8 +7,8 @@ use std::{ }; use url::Url; +use alloy::primitives::U256; use alloy::providers::{ext::AnvilApi, ProviderBuilder}; -use alloy::{primitives::U256, providers::Provider}; use alloy_primitives::Address; use color_eyre::eyre::eyre; use rstest::*; @@ -130,3 +130,53 @@ async fn update_state_blob_works(#[case] block_no: u64) { assert_eq!(prev_block_number._0.as_u32() + 1, latest_block_number._0.as_u32()); } + +#[rstest] +#[tokio::test] +#[case::basic(20468828)] +async fn creating_input_data_works(#[case] block_no: u64) { + use alloy_primitives::Bytes; + use c_kzg::Bytes32; + + use crate::conversion::{get_input_data_for_eip_4844, to_padded_hex}; + + let current_path = std::env::current_dir().unwrap().to_str().unwrap().to_string(); + + let program_output_file_path = + format!("{}{}{}{}", current_path.clone(), "/src/test_data/program_output/", block_no, ".txt"); + + let mut program_output: Vec<[u8; 32]> = Vec::new(); + let file = File::open(program_output_file_path).expect("Failed to read program output file"); + let reader = BufReader::new(file); + + for line in reader.lines() { + let line = line.expect("can't read line"); + let trimmed = line.trim(); + if !trimmed.is_empty() { + let v_0 = U256::from_str(trimmed).expect("Unable to convert line").to_be_bytes_vec(); + let v_1 = v_0.as_slice(); + let v_2 = to_padded_hex(v_1); + // let v_3 = v_2.replace("0x", ""); + println!("V2 {:?}", v_2); + let v_4 = hex_string_to_u8_vec(&v_2).expect("unable to convert"); + let v_5: [u8; 32] = v_4.try_into().expect("Vector length must be 32"); + program_output.push(v_5) + } + } + + let x_0_value_bytes32 = Bytes32::from(program_output[8]); + + // Blob Data + let blob_data_file_path = format!("{}{}{}{}", current_path.clone(), "/src/test_data/blob_data/", block_no, ".txt"); + println!("{}", blob_data_file_path); + let blob_data = fs::read_to_string(blob_data_file_path).expect("Failed to read the blob data txt file"); + let blob_data_vec = vec![hex_string_to_u8_vec(&blob_data).unwrap()]; + + let kzg_proof = EthereumSettlementClient::build_proof(blob_data_vec, x_0_value_bytes32) + .expect("Unable to build KZG proof for given params.") + .to_owned(); + + let input_bytes = get_input_data_for_eip_4844(program_output, kzg_proof).expect("unable to create input data"); + let expected = Bytes::from("0xb72d42a100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000001706ac7b2661801b4c0733da6ed1d2910b3b97259534ca95a63940932513111fba028bccc051eaae1b9a69b53e64a68021233b4dee2030aeda4be886324b3fbb3e00000000000000000000000000000000000000000000000000000000000a29b8070626a88de6a77855ecd683757207cdd18ba56553dca6c0c98ec523b827bee005ba2078240f1585f96424c2d1ee48211da3b3f9177bf2b9880b4fc91d59e9a2000000000000000000000000000000000000000000000000000000000000000100000000000000002b4e335bc41dc46c71f29928a5094a8c96a0c3536cabe53e0000000000000000810abb1929a0d45cdd62a20f9ccfd5807502334e7deb35d404c86d8b63a5741770fefca2f9b8efb7e663d89097edb3c60595b236f6e78e6f000000000000000000000000000000004a4b8a979fefc4d6b82e030fb082ca98000000000000000000000000000000004e8371c6774260e87b92447d4a2b0e170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000bf67f59d2988a46fbff7ed79a621778a3cd3985b0088eedbe2fe3918b69ccb411713b7fa72079d4eddf291103ccbe41e78a9615c0000000000000000000000000000000000000000000000000000000000194fe601b64b1b3b690b43b9b514fb81377518f4039cd3e4f4914d8a6bdf01d679fb1900000000000000000000000000000000000000000000000000000000000000050000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca000000000000000000000000012ccc443d39da45e5f640b3e71f0c7502152dbac01d4988e248d342439aa025b302e1f07595f6a5c810dcce23e7379e48f05d4cf000000000000000000000000000000000000000000000007f189b5374ad2a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030ab015987628cffee3ef99b9768ef8ca12c6244525f0cd10310046eaa21291b5aca164d044c5b4ad7212c767b165ed5e300000000000000000000000000000000"); + assert_eq!(input_bytes, expected); +} From d28b98b26ba4cc712bd80d27f8ffc6cb7a302cab Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Tue, 13 Aug 2024 17:12:24 +0530 Subject: [PATCH 16/41] update: using correct input bytes --- .../settlement-clients/ethereum/src/conversion.rs | 4 ++-- crates/settlement-clients/ethereum/src/lib.rs | 11 ++++++----- .../settlement-clients/ethereum/src/tests/mod.rs | 15 +++++++++++---- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/crates/settlement-clients/ethereum/src/conversion.rs b/crates/settlement-clients/ethereum/src/conversion.rs index 9bc67687..d4fd6cab 100644 --- a/crates/settlement-clients/ethereum/src/conversion.rs +++ b/crates/settlement-clients/ethereum/src/conversion.rs @@ -47,7 +47,7 @@ pub(crate) fn get_txn_input_bytes(program_output: Vec<[u8; 32]>, kzg_proof: [u8; /// Function to construct the transaction's `input data` for updating the state in the core contract. /// HEX Concatenation: MethodId, Offset, length for program_output, lines count, program_output, length for kzg_proof, kzg_proof /// All 64 chars, if lesser padded from left with 0s -pub fn get_input_data_for_eip_4844(program_output: Vec<[u8; 32]>, kzg_proof: [u8; 48]) -> Result { +pub fn get_input_data_for_eip_4844(program_output: Vec<[u8; 32]>, kzg_proof: [u8; 48]) -> Result { // bytes4(keccak256(bytes("updateStateKzgDA(uint256[],bytes)"))) let method_id_hex = "0xb72d42a1"; @@ -80,7 +80,7 @@ pub fn get_input_data_for_eip_4844(program_output: Vec<[u8; 32]>, kzg_proof: [u8 + &length_kzg_hex + &kzg_proof_hex; - Ok(Bytes::from(input_data)) + Ok(input_data) } pub(crate) fn vec_u8_32_to_hex_string(data: Vec<[u8; 32]>) -> String { diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index cbed317a..689da3fe 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -28,7 +28,7 @@ use color_eyre::Result; use mockall::{automock, lazy_static, predicate::*}; use alloy::providers::ProviderBuilder; -use conversion::prepare_sidecar; +use conversion::{get_input_data_for_eip_4844, prepare_sidecar}; use settlement_client_interface::{SettlementClient, SettlementVerificationStatus, SETTLEMENT_SETTINGS_NAME}; use utils::{env_utils::get_env_var_or_panic, settings::SettingsProvider}; @@ -191,14 +191,16 @@ impl SettlementClient for EthereumSettlementClient { max_fee_per_blob_gas += 12; let max_priority_fee_per_gas: u128 = self.provider.get_max_priority_fee_per_gas().await?.to_string().parse()?; - // x_0_value : program_output[6] + // x_0_value : program_output[8] let kzg_proof = Self::build_proof( state_diff, - Bytes32::from_bytes(program_output[6].as_slice()).expect("Not able to get x_0 point params."), + Bytes32::from_bytes(program_output[8].as_slice()).expect("Not able to get x_0 point params."), ) .expect("Unable to build KZG proof for given params.") .to_owned(); + let input_bytes = get_input_data_for_eip_4844(program_output, kzg_proof)?; + let tx: TxEip4844 = TxEip4844 { chain_id, nonce, @@ -210,8 +212,7 @@ impl SettlementClient for EthereumSettlementClient { access_list: AccessList(vec![]), blob_versioned_hashes: sidecar.versioned_hashes().collect(), max_fee_per_blob_gas, - // input: get_txn_input_bytes(program_output, kzg_proof), - input: Bytes::from(hex::decode("0xb72d42a100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000001706ac7b2661801b4c0733da6ed1d2910b3b97259534ca95a63940932513111fba028bccc051eaae1b9a69b53e64a68021233b4dee2030aeda4be886324b3fbb3e00000000000000000000000000000000000000000000000000000000000a29b8070626a88de6a77855ecd683757207cdd18ba56553dca6c0c98ec523b827bee005ba2078240f1585f96424c2d1ee48211da3b3f9177bf2b9880b4fc91d59e9a2000000000000000000000000000000000000000000000000000000000000000100000000000000002b4e335bc41dc46c71f29928a5094a8c96a0c3536cabe53e0000000000000000810abb1929a0d45cdd62a20f9ccfd5807502334e7deb35d404c86d8b63a5741770fefca2f9b8efb7e663d89097edb3c60595b236f6e78e6f000000000000000000000000000000004a4b8a979fefc4d6b82e030fb082ca98000000000000000000000000000000004e8371c6774260e87b92447d4a2b0e170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000bf67f59d2988a46fbff7ed79a621778a3cd3985b0088eedbe2fe3918b69ccb411713b7fa72079d4eddf291103ccbe41e78a9615c0000000000000000000000000000000000000000000000000000000000194fe601b64b1b3b690b43b9b514fb81377518f4039cd3e4f4914d8a6bdf01d679fb1900000000000000000000000000000000000000000000000000000000000000050000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca000000000000000000000000012ccc443d39da45e5f640b3e71f0c7502152dbac01d4988e248d342439aa025b302e1f07595f6a5c810dcce23e7379e48f05d4cf000000000000000000000000000000000000000000000007f189b5374ad2a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030ab015987628cffee3ef99b9768ef8ca12c6244525f0cd10310046eaa21291b5aca164d044c5b4ad7212c767b165ed5e300000000000000000000000000000000").unwrap()), + input: Bytes::from(hex::decode(input_bytes)?), }; let tx_sidecar = TxEip4844WithSidecar { tx: tx.clone(), sidecar: sidecar.clone() }; diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index 7103e599..7dde37a7 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -50,6 +50,8 @@ sol!( #[case::basic(20468828)] async fn update_state_blob_works(#[case] block_no: u64) { // Load ENV vars + + use crate::conversion::to_padded_hex; dotenvy::from_filename("../.env.test").expect("Could not load .env.test file."); let current_path = std::env::current_dir().unwrap().to_str().unwrap().to_string(); @@ -101,8 +103,13 @@ async fn update_state_blob_works(#[case] block_no: u64) { let line = line.expect("can't read line"); let trimmed = line.trim(); if !trimmed.is_empty() { - let line_u8_32: [u8; 32] = U256::from_str(trimmed).expect("unable to convert line").to_le_bytes(); - program_output.push(line_u8_32); + let v_0 = U256::from_str(trimmed).expect("Unable to convert line").to_be_bytes_vec(); + let v_1 = v_0.as_slice(); + let v_2 = to_padded_hex(v_1); + // let v_3 = v_2.replace("0x", ""); + let v_4 = hex_string_to_u8_vec(&v_2).expect("unable to convert"); + let v_5: [u8; 32] = v_4.try_into().expect("Vector length must be 32"); + program_output.push(v_5) } } } @@ -113,6 +120,7 @@ async fn update_state_blob_works(#[case] block_no: u64) { let blob_data = fs::read_to_string(blob_data_file_path).expect("Failed to read the blob data txt file"); let blob_data_vec = vec![hex_string_to_u8_vec(&blob_data).unwrap()]; + // Calling update_state_with_blobs let update_state_result = ethereum_settlement_client .update_state_with_blobs(program_output, blob_data_vec, nonce) @@ -157,7 +165,6 @@ async fn creating_input_data_works(#[case] block_no: u64) { let v_1 = v_0.as_slice(); let v_2 = to_padded_hex(v_1); // let v_3 = v_2.replace("0x", ""); - println!("V2 {:?}", v_2); let v_4 = hex_string_to_u8_vec(&v_2).expect("unable to convert"); let v_5: [u8; 32] = v_4.try_into().expect("Vector length must be 32"); program_output.push(v_5) @@ -177,6 +184,6 @@ async fn creating_input_data_works(#[case] block_no: u64) { .to_owned(); let input_bytes = get_input_data_for_eip_4844(program_output, kzg_proof).expect("unable to create input data"); - let expected = Bytes::from("0xb72d42a100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000001706ac7b2661801b4c0733da6ed1d2910b3b97259534ca95a63940932513111fba028bccc051eaae1b9a69b53e64a68021233b4dee2030aeda4be886324b3fbb3e00000000000000000000000000000000000000000000000000000000000a29b8070626a88de6a77855ecd683757207cdd18ba56553dca6c0c98ec523b827bee005ba2078240f1585f96424c2d1ee48211da3b3f9177bf2b9880b4fc91d59e9a2000000000000000000000000000000000000000000000000000000000000000100000000000000002b4e335bc41dc46c71f29928a5094a8c96a0c3536cabe53e0000000000000000810abb1929a0d45cdd62a20f9ccfd5807502334e7deb35d404c86d8b63a5741770fefca2f9b8efb7e663d89097edb3c60595b236f6e78e6f000000000000000000000000000000004a4b8a979fefc4d6b82e030fb082ca98000000000000000000000000000000004e8371c6774260e87b92447d4a2b0e170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000bf67f59d2988a46fbff7ed79a621778a3cd3985b0088eedbe2fe3918b69ccb411713b7fa72079d4eddf291103ccbe41e78a9615c0000000000000000000000000000000000000000000000000000000000194fe601b64b1b3b690b43b9b514fb81377518f4039cd3e4f4914d8a6bdf01d679fb1900000000000000000000000000000000000000000000000000000000000000050000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca000000000000000000000000012ccc443d39da45e5f640b3e71f0c7502152dbac01d4988e248d342439aa025b302e1f07595f6a5c810dcce23e7379e48f05d4cf000000000000000000000000000000000000000000000007f189b5374ad2a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030ab015987628cffee3ef99b9768ef8ca12c6244525f0cd10310046eaa21291b5aca164d044c5b4ad7212c767b165ed5e300000000000000000000000000000000"); + let expected = "0xb72d42a100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000001706ac7b2661801b4c0733da6ed1d2910b3b97259534ca95a63940932513111fba028bccc051eaae1b9a69b53e64a68021233b4dee2030aeda4be886324b3fbb3e00000000000000000000000000000000000000000000000000000000000a29b8070626a88de6a77855ecd683757207cdd18ba56553dca6c0c98ec523b827bee005ba2078240f1585f96424c2d1ee48211da3b3f9177bf2b9880b4fc91d59e9a2000000000000000000000000000000000000000000000000000000000000000100000000000000002b4e335bc41dc46c71f29928a5094a8c96a0c3536cabe53e0000000000000000810abb1929a0d45cdd62a20f9ccfd5807502334e7deb35d404c86d8b63a5741770fefca2f9b8efb7e663d89097edb3c60595b236f6e78e6f000000000000000000000000000000004a4b8a979fefc4d6b82e030fb082ca98000000000000000000000000000000004e8371c6774260e87b92447d4a2b0e170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000bf67f59d2988a46fbff7ed79a621778a3cd3985b0088eedbe2fe3918b69ccb411713b7fa72079d4eddf291103ccbe41e78a9615c0000000000000000000000000000000000000000000000000000000000194fe601b64b1b3b690b43b9b514fb81377518f4039cd3e4f4914d8a6bdf01d679fb1900000000000000000000000000000000000000000000000000000000000000050000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca000000000000000000000000012ccc443d39da45e5f640b3e71f0c7502152dbac01d4988e248d342439aa025b302e1f07595f6a5c810dcce23e7379e48f05d4cf000000000000000000000000000000000000000000000007f189b5374ad2a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030ab015987628cffee3ef99b9768ef8ca12c6244525f0cd10310046eaa21291b5aca164d044c5b4ad7212c767b165ed5e300000000000000000000000000000000"; assert_eq!(input_bytes, expected); } From a03f9db3266fab28d31481a621156c224ee64f1f Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Tue, 13 Aug 2024 17:17:58 +0530 Subject: [PATCH 17/41] chore: lint fix --- .../ethereum/src/conversion.rs | 53 +------------------ crates/settlement-clients/ethereum/src/lib.rs | 2 +- .../ethereum/src/tests/mod.rs | 2 - 3 files changed, 2 insertions(+), 55 deletions(-) diff --git a/crates/settlement-clients/ethereum/src/conversion.rs b/crates/settlement-clients/ethereum/src/conversion.rs index d4fd6cab..ce355003 100644 --- a/crates/settlement-clients/ethereum/src/conversion.rs +++ b/crates/settlement-clients/ethereum/src/conversion.rs @@ -1,8 +1,7 @@ use alloy::dyn_abi::parser::Error; use alloy::eips::eip4844::BYTES_PER_BLOB; -use alloy::primitives::Bytes; -use alloy::primitives::FixedBytes; use alloy::primitives::U256; +use alloy_primitives::FixedBytes; use c_kzg::{Blob, KzgCommitment, KzgProof, KzgSettings}; use color_eyre::{eyre::ContextCompat, Result as EyreResult}; use std::fmt::Write; @@ -34,16 +33,6 @@ pub(crate) fn to_padded_hex(slice: &[u8]) -> String { format!("{:0<64}", hex) } -/// Function to construct the transaction for updating the state in core contract. -pub(crate) fn get_txn_input_bytes(program_output: Vec<[u8; 32]>, kzg_proof: [u8; 48]) -> Bytes { - let program_output_hex_string = vec_u8_32_to_hex_string(program_output); - let kzg_proof_hex_string = u8_48_to_hex_string(kzg_proof); - // cast keccak "updateStateKzgDA(uint256[] calldata programOutput, bytes calldata kzgProof)" | cut -b 1-10 - let function_selector = "0x1a790556"; - - Bytes::from(program_output_hex_string + &kzg_proof_hex_string + function_selector) -} - /// Function to construct the transaction's `input data` for updating the state in the core contract. /// HEX Concatenation: MethodId, Offset, length for program_output, lines count, program_output, length for kzg_proof, kzg_proof /// All 64 chars, if lesser padded from left with 0s @@ -273,46 +262,6 @@ mod tests { assert_eq!(result, expected); } - #[rstest] - #[case::typical( - vec![ - [0xFF;32], - [0xF5;32], - ], - [0xF1;48], - format!("{}{}{}{}{}", - "ff".repeat(32), "f5".repeat(32), - "f1".repeat(48), "00".repeat(16), - "0x1a790556" - ) - )] - #[case::typical( - vec![ - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32], - [32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1], - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 0, 0] - ], - [ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, - 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - ], - format!("{}{}{}", - "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a0908070605040302010102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e0000", - "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3000000000000000000000000000000000", - "0x1a790556" - ) - )] - - fn get_txn_input_bytes_works( - #[case] program_output: Vec<[u8; 32]>, - #[case] kzg_proof: [u8; 48], - #[case] expected_output: String, - ) { - let result: Bytes = get_txn_input_bytes(program_output, kzg_proof); - //TODO: converting expected value to match result, we would ideally want to convert the result to match expected - assert_eq!(result, Bytes::from(expected_output)); - } - #[rstest] #[case("20462788")] #[case("20462818")] diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index 689da3fe..1104f4b8 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -212,7 +212,7 @@ impl SettlementClient for EthereumSettlementClient { access_list: AccessList(vec![]), blob_versioned_hashes: sidecar.versioned_hashes().collect(), max_fee_per_blob_gas, - input: Bytes::from(hex::decode(input_bytes)?), + input: Bytes::from(hex::decode(input_bytes)?), }; let tx_sidecar = TxEip4844WithSidecar { tx: tx.clone(), sidecar: sidecar.clone() }; diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index 7dde37a7..e3732c86 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -120,7 +120,6 @@ async fn update_state_blob_works(#[case] block_no: u64) { let blob_data = fs::read_to_string(blob_data_file_path).expect("Failed to read the blob data txt file"); let blob_data_vec = vec![hex_string_to_u8_vec(&blob_data).unwrap()]; - // Calling update_state_with_blobs let update_state_result = ethereum_settlement_client .update_state_with_blobs(program_output, blob_data_vec, nonce) @@ -143,7 +142,6 @@ async fn update_state_blob_works(#[case] block_no: u64) { #[tokio::test] #[case::basic(20468828)] async fn creating_input_data_works(#[case] block_no: u64) { - use alloy_primitives::Bytes; use c_kzg::Bytes32; use crate::conversion::{get_input_data_for_eip_4844, to_padded_hex}; From 06d97cd3f78e410fec28102176aff6310f4843de Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Wed, 14 Aug 2024 10:20:38 +0530 Subject: [PATCH 18/41] update: test normal transaction --- .env.test | 4 +- crates/settlement-clients/ethereum/src/lib.rs | 20 +++++- .../ethereum/src/tests/mod.rs | 63 +++++++++++++++---- 3 files changed, 73 insertions(+), 14 deletions(-) diff --git a/.env.test b/.env.test index 5d4f5e95..adb5e43f 100644 --- a/.env.test +++ b/.env.test @@ -29,4 +29,6 @@ MONGODB_CONNECTION_STRING="mongodb://localhost:27017" # Ethereum Settlement DEFAULT_SETTLEMENT_CLIENT_RPC="http://localhost:3000" -DEFAULT_L1_CORE_CONTRACT_ADDRESS="0xc662c410C0ECf747543f5bA90660f6ABeBD9C8c4" \ No newline at end of file +DEFAULT_L1_CORE_CONTRACT_ADDRESS="0xc662c410C0ECf747543f5bA90660f6ABeBD9C8c4" +TEST_IMPERSONATE_OPERATOR=0 +TEST_DUMMY_CONTRACT_ADDRESS="0xE5b6F5e695BA6E4aeD92B68c4CC8Df1160D69A81" \ No newline at end of file diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index 1104f4b8..eeee49c8 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -103,8 +103,15 @@ impl EthereumSettlementClient { let fill_provider = Arc::new( ProviderBuilder::new().with_recommended_fillers().wallet(wallet.clone()).on_http(settlement_cfg.rpc_url), ); + + let core_contract_address = if get_env_var_or_panic("TEST_IMPERSONATE_OPERATOR") == "0".to_string() { + get_env_var_or_panic("TEST_DUMMY_CONTRACT_ADDRESS") + } else { + settlement_cfg.core_contract_address + }; + let core_contract_client = StarknetValidityContractClient::new( - Address::from_str(&settlement_cfg.core_contract_address).unwrap().0.into(), + Address::from_str(&core_contract_address).unwrap().0.into(), fill_provider, ); @@ -216,6 +223,8 @@ impl SettlementClient for EthereumSettlementClient { }; let tx_sidecar = TxEip4844WithSidecar { tx: tx.clone(), sidecar: sidecar.clone() }; + + println!("CONTTRRAACCTTT {:?}", tx.to); let mut variant = TxEip4844Variant::from(tx_sidecar); let signature = self.wallet.default_signer().sign_transaction(&mut variant).await?; let tx_signed = variant.into_signed(signature); @@ -223,7 +232,9 @@ impl SettlementClient for EthereumSettlementClient { // IMP: this conversion strips signature from the transaction let mut txn_request: TransactionRequest = tx_envelope.into(); - if cfg!(test) { + println!("CONTTRRAACCTTT #2 {:?}", tx.to); + + if cfg!(test) && get_env_var_or_panic("TEST_IMPERSONATE_OPERATOR") == "1".to_string() { txn_request.set_nonce(666068); txn_request = txn_request.with_from( Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7") @@ -231,10 +242,15 @@ impl SettlementClient for EthereumSettlementClient { ); } + println!("CONTTRRAACCTTT #3 {:?}", tx.to); + + // let encoded = tx_envelope.encoded_2718(); // let pending_tx = self.provider.send_raw_transaction(&encoded).await?; let pending_transaction = self.provider.send_transaction(txn_request).await?; + println!("CONTTRRAACCTTT #4 {:?}", pending_transaction.tx_hash().to_string()); + return Ok(pending_transaction.tx_hash().to_string()); } diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index e3732c86..43cc02f3 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -1,4 +1,5 @@ use alloy::{node_bindings::Anvil, sol}; +use utils::env_utils::get_env_var_or_panic; use std::io::BufRead; use std::{ fs::{self, File}, @@ -45,12 +46,33 @@ sol!( // TODO: betterment of file routes + + +// TODO: Checking send_transaction +// Create a dummy contract and deploy on anvil with same methodId as core contract +// Make an env variable that will tell if we are testing impersonation or not +// Check against the env variable, if we are not impersonation then we should talk to the dummy address + +sol! { + #[allow(missing_docs)] + #[sol(rpc, bytecode="6080604052348015600e575f80fd5b506101c18061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c8063b72d42a11461002d575b5f80fd5b6100476004803603810190610042919061010d565b610049565b005b50505050565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f8083601f84011261007857610077610057565b5b8235905067ffffffffffffffff8111156100955761009461005b565b5b6020830191508360208202830111156100b1576100b061005f565b5b9250929050565b5f8083601f8401126100cd576100cc610057565b5b8235905067ffffffffffffffff8111156100ea576100e961005b565b5b6020830191508360018202830111156101065761010561005f565b5b9250929050565b5f805f80604085870312156101255761012461004f565b5b5f85013567ffffffffffffffff81111561014257610141610053565b5b61014e87828801610063565b9450945050602085013567ffffffffffffffff81111561017157610170610053565b5b61017d878288016100b8565b92509250509295919450925056fea2646970667358221220fa7488d5a2a9e6c21e6f46145a831b0f04fdebab83868dc2b996c17f8cba4d8064736f6c634300081a0033")] + contract DummyCoreContract { + function updateStateKzgDA(uint256[] calldata programOutput, bytes calldata kzgProof) external { + } + } +} + #[rstest] #[tokio::test] #[case::basic(20468828)] async fn update_state_blob_works(#[case] block_no: u64) { - // Load ENV vars + use std::time::Duration; + use alloy::providers::Provider; + use alloy_primitives::FixedBytes; + use tokio::time::sleep; + + // Load ENV vars use crate::conversion::to_padded_hex; dotenvy::from_filename("../.env.test").expect("Could not load .env.test file."); let current_path = std::env::current_dir().unwrap().to_str().unwrap().to_string(); @@ -71,13 +93,19 @@ async fn update_state_blob_works(#[case] block_no: u64) { let settings_provider: DefaultSettingsProvider = DefaultSettingsProvider {}; let ethereum_settlement_client = EthereumSettlementClient::with_test_settings(&settings_provider, provider.clone()); - // Setup operator account impersonation - provider - .anvil_impersonate_account( - Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("Could not impersonate account."), - ) - .await - .expect("Unable to impersonate account."); + let impersonate_acount = get_env_var_or_panic("TEST_IMPERSONATE_OPERATOR"); + + if impersonate_acount == "0".to_string(){ + let contract = DummyCoreContract::deploy(&provider).await.expect("Unable to deploy address"); + println!("Deployed contract at address: {}", contract.address()); + } else { + provider + .anvil_impersonate_account( + Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("Could not impersonate account."), + ) + .await + .expect("Unable to impersonate account."); + } let nonce = ethereum_settlement_client.get_nonce().await.expect("Unable to fetch nonce"); @@ -132,10 +160,23 @@ async fn update_state_blob_works(#[case] block_no: u64) { // Call the contract, retrieve the latest stateBlockNumber. let latest_block_number = contract.stateBlockNumber().call().await.unwrap(); - println!("PREVIOUS BLOCK NUMBER {}", prev_block_number._0); - println!("CURRENT BLOCK HASH {}", latest_block_number._0); + if impersonate_acount == "1".to_string() { + println!("PREVIOUS BLOCK NUMBER {}", prev_block_number._0); + println!("CURRENT BLOCK HASH {}", latest_block_number._0); + assert_eq!(prev_block_number._0.as_u32() + 1, latest_block_number._0.as_u32()); + } else { + sleep(Duration::from_secs(2)).await; + let txn = provider.get_transaction_by_hash(FixedBytes::from_str(update_state_result.as_str()).expect("couln't convert")) + .await + .expect("did not get txn from hash"); + println!("{:?}", txn); + sleep(Duration::from_secs(2)).await; + + assert!(!txn.is_none()); + + + } - assert_eq!(prev_block_number._0.as_u32() + 1, latest_block_number._0.as_u32()); } #[rstest] From 5aa7b10a770213b29b75979d0dbfa446ac428ec5 Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Wed, 14 Aug 2024 13:15:11 +0530 Subject: [PATCH 19/41] update: dummy contract and impersonation tests ready --- .env.test | 2 +- Cargo.lock | 357 ++++++++++-------- crates/settlement-clients/ethereum/Cargo.toml | 1 + crates/settlement-clients/ethereum/src/lib.rs | 37 +- .../ethereum/src/tests/mod.rs | 304 +++++++++------ 5 files changed, 422 insertions(+), 279 deletions(-) diff --git a/.env.test b/.env.test index adb5e43f..4fdaae38 100644 --- a/.env.test +++ b/.env.test @@ -30,5 +30,5 @@ MONGODB_CONNECTION_STRING="mongodb://localhost:27017" # Ethereum Settlement DEFAULT_SETTLEMENT_CLIENT_RPC="http://localhost:3000" DEFAULT_L1_CORE_CONTRACT_ADDRESS="0xc662c410C0ECf747543f5bA90660f6ABeBD9C8c4" -TEST_IMPERSONATE_OPERATOR=0 +TEST_IMPERSONATE_OPERATOR="1" TEST_DUMMY_CONTRACT_ADDRESS="0xE5b6F5e695BA6E4aeD92B68c4CC8Df1160D69A81" \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index bd538040..973fa262 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -87,28 +87,28 @@ dependencies = [ [[package]] name = "alloy" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9134b68e24175eff6c3c4d2bffeefb0a1b7435462130862c88d1524ca376e7e5" +checksum = "3f4a4aaae80afd4be443a6aecd92a6b255dcdd000f97996928efb33d8a71e100" dependencies = [ - "alloy-consensus 0.1.2", + "alloy-consensus 0.2.1", "alloy-contract", - "alloy-core 0.7.6", - "alloy-eips 0.1.2", + "alloy-core 0.7.7", + "alloy-eips 0.2.1", "alloy-genesis", - "alloy-network 0.1.2", - "alloy-provider 0.1.2", + "alloy-network 0.2.1", + "alloy-node-bindings", + "alloy-provider 0.2.1", "alloy-pubsub", - "alloy-rpc-client 0.1.2", - "alloy-rpc-types 0.1.2", - "alloy-serde 0.1.2", - "alloy-signer 0.1.2", + "alloy-rpc-client 0.2.1", + "alloy-rpc-types 0.2.1", + "alloy-serde 0.2.1", + "alloy-signer 0.2.1", "alloy-signer-local", - "alloy-transport 0.1.2", - "alloy-transport-http 0.1.2", + "alloy-transport 0.2.1", + "alloy-transport-http 0.2.1", "alloy-transport-ipc", "alloy-transport-ws", - "reqwest 0.12.5", ] [[package]] @@ -134,33 +134,34 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a016bfa21193744d4c38b3f3ab845462284d129e5e23c7cc0fafca7e92d9db37" +checksum = "04c309895995eaa4bfcc345f5515a39c7df9447798645cc8bf462b6c5bf1dc96" dependencies = [ - "alloy-eips 0.1.2", - "alloy-primitives 0.7.6", + "alloy-eips 0.2.1", + "alloy-primitives 0.7.7", "alloy-rlp", - "alloy-serde 0.1.2", + "alloy-serde 0.2.1", "c-kzg", "serde", ] [[package]] name = "alloy-contract" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e47b2a620fd588d463ccf0f5931b41357664b293a8d31592768845a2a101bb9e" +checksum = "3f4e0ef72b0876ae3068b2ed7dfae9ae1779ce13cfaec2ee1f08f5bd0348dc57" dependencies = [ - "alloy-dyn-abi 0.7.6", - "alloy-json-abi 0.7.6", - "alloy-network 0.1.2", - "alloy-primitives 0.7.6", - "alloy-provider 0.1.2", + "alloy-dyn-abi 0.7.7", + "alloy-json-abi 0.7.7", + "alloy-network 0.2.1", + "alloy-network-primitives", + "alloy-primitives 0.7.7", + "alloy-provider 0.2.1", "alloy-pubsub", "alloy-rpc-types-eth", - "alloy-sol-types 0.7.6", - "alloy-transport 0.1.2", + "alloy-sol-types 0.7.7", + "alloy-transport 0.2.1", "futures", "futures-util", "thiserror", @@ -180,14 +181,14 @@ dependencies = [ [[package]] name = "alloy-core" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5af3faff14c12c8b11037e0a093dd157c3702becb8435577a2408534d0758315" +checksum = "529fc6310dc1126c8de51c376cbc59c79c7f662bd742be7dc67055d5421a81b4" dependencies = [ - "alloy-dyn-abi 0.7.6", - "alloy-json-abi 0.7.6", - "alloy-primitives 0.7.6", - "alloy-sol-types 0.7.6", + "alloy-dyn-abi 0.7.7", + "alloy-json-abi 0.7.7", + "alloy-primitives 0.7.7", + "alloy-sol-types 0.7.7", ] [[package]] @@ -209,14 +210,14 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6e6436a9530f25010d13653e206fab4c9feddacf21a54de8d7311b275bc56b" +checksum = "413902aa18a97569e60f679c23f46a18db1656d87ab4d4e49d0e1e52042f66df" dependencies = [ - "alloy-json-abi 0.7.6", - "alloy-primitives 0.7.6", - "alloy-sol-type-parser 0.7.6", - "alloy-sol-types 0.7.6", + "alloy-json-abi 0.7.7", + "alloy-primitives 0.7.7", + "alloy-sol-type-parser 0.7.7", + "alloy-sol-types 0.7.7", "const-hex", "itoa", "serde", @@ -239,15 +240,16 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d6d8118b83b0489cfb7e6435106948add2b35217f4a5004ef895f613f60299" +checksum = "d9431c99a3b3fe606ede4b3d4043bdfbcb780c45b8d8d226c3804e2b75cfbe68" dependencies = [ - "alloy-primitives 0.7.6", + "alloy-primitives 0.7.7", "alloy-rlp", - "alloy-serde 0.1.2", + "alloy-serde 0.2.1", "c-kzg", "derive_more", + "k256", "once_cell", "serde", "sha2", @@ -255,12 +257,12 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "894f33a7822abb018db56b10ab90398e63273ce1b5a33282afd186c132d764a6" +checksum = "79614dfe86144328da11098edcc7bc1a3f25ad8d3134a9eb9e857e06f0d9840d" dependencies = [ - "alloy-primitives 0.7.6", - "alloy-serde 0.1.2", + "alloy-primitives 0.7.7", + "alloy-serde 0.2.1", "serde", ] @@ -278,12 +280,12 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaeaccd50238126e3a0ff9387c7c568837726ad4f4e399b528ca88104d6c25ef" +checksum = "bc05b04ac331a9f07e3a4036ef7926e49a8bf84a99a1ccfc7e2ab55a5fcbb372" dependencies = [ - "alloy-primitives 0.7.6", - "alloy-sol-type-parser 0.7.6", + "alloy-primitives 0.7.7", + "alloy-sol-type-parser 0.7.7", "serde", "serde_json", ] @@ -301,11 +303,12 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61f0ae6e93b885cc70fe8dae449e7fd629751dbee8f59767eaaa7285333c5727" +checksum = "57e2865c4c3bb4cdad3f0d9ec1ab5c0c657ba69a375651bd35e32fb6c180ccc2" dependencies = [ - "alloy-primitives 0.7.6", + "alloy-primitives 0.7.7", + "alloy-sol-types 0.7.7", "serde", "serde_json", "thiserror", @@ -331,24 +334,52 @@ dependencies = [ [[package]] name = "alloy-network" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc122cbee2b8523854cc11d87bcd5773741602c553d2d2d106d82eeb9c16924a" +checksum = "6e701fc87ef9a3139154b0b4ccb935b565d27ffd9de020fe541bf2dec5ae4ede" dependencies = [ - "alloy-consensus 0.1.2", - "alloy-eips 0.1.2", - "alloy-json-rpc 0.1.2", - "alloy-primitives 0.7.6", + "alloy-consensus 0.2.1", + "alloy-eips 0.2.1", + "alloy-json-rpc 0.2.1", + "alloy-network-primitives", + "alloy-primitives 0.7.7", "alloy-rpc-types-eth", - "alloy-serde 0.1.2", - "alloy-signer 0.1.2", - "alloy-sol-types 0.7.6", + "alloy-serde 0.2.1", + "alloy-signer 0.2.1", + "alloy-sol-types 0.7.7", "async-trait", "auto_impl", "futures-utils-wasm", "thiserror", ] +[[package]] +name = "alloy-network-primitives" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec9d5a0f9170b10988b6774498a022845e13eda94318440d17709d50687f67f9" +dependencies = [ + "alloy-primitives 0.7.7", + "alloy-serde 0.2.1", + "serde", +] + +[[package]] +name = "alloy-node-bindings" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16faebb9ea31a244fd6ce3288d47df4be96797d9c3c020144b8f2c31543a4512" +dependencies = [ + "alloy-genesis", + "alloy-primitives 0.7.7", + "k256", + "serde_json", + "tempfile", + "thiserror", + "tracing", + "url", +] + [[package]] name = "alloy-primitives" version = "0.6.4" @@ -373,9 +404,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f783611babedbbe90db3478c120fb5f5daacceffc210b39adc0af4fe0da70bad" +checksum = "ccb3ead547f4532bc8af961649942f0b9c16ee9226e26caa3f38420651cc0bf4" dependencies = [ "alloy-rlp", "bytes", @@ -420,21 +451,25 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d5af289798fe8783acd0c5f10644d9d26f54a12bc52a083e4f3b31718e9bf92" +checksum = "3f9c0ab10b93de601a6396fc7ff2ea10d3b28c46f079338fa562107ebf9857c8" dependencies = [ "alloy-chains", - "alloy-consensus 0.1.2", - "alloy-eips 0.1.2", - "alloy-json-rpc 0.1.2", - "alloy-network 0.1.2", - "alloy-primitives 0.7.6", + "alloy-consensus 0.2.1", + "alloy-eips 0.2.1", + "alloy-json-rpc 0.2.1", + "alloy-network 0.2.1", + "alloy-network-primitives", + "alloy-node-bindings", + "alloy-primitives 0.7.7", "alloy-pubsub", - "alloy-rpc-client 0.1.2", + "alloy-rpc-client 0.2.1", + "alloy-rpc-types-anvil", "alloy-rpc-types-eth", - "alloy-transport 0.1.2", - "alloy-transport-http 0.1.2", + "alloy-signer-local", + "alloy-transport 0.2.1", + "alloy-transport-http 0.2.1", "alloy-transport-ipc", "alloy-transport-ws", "async-stream", @@ -455,13 +490,13 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702f330b7da123a71465ab9d39616292f8344a2811c28f2cc8d8438a69d79e35" +checksum = "3f5da2c55cbaf229bad3c5f8b00b5ab66c74ef093e5f3a753d874cfecf7d2281" dependencies = [ - "alloy-json-rpc 0.1.2", - "alloy-primitives 0.7.6", - "alloy-transport 0.1.2", + "alloy-json-rpc 0.2.1", + "alloy-primitives 0.7.7", + "alloy-transport 0.2.1", "bimap", "futures", "serde", @@ -516,15 +551,15 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b40fcb53b2a9d0a78a4968b2eca8805a4b7011b9ee3fdfa2acaf137c5128f36b" +checksum = "5b38e3ffdb285df5d9f60cb988d336d9b8e3505acb78750c3bc60336a7af41d3" dependencies = [ - "alloy-json-rpc 0.1.2", - "alloy-primitives 0.7.6", + "alloy-json-rpc 0.2.1", + "alloy-primitives 0.7.7", "alloy-pubsub", - "alloy-transport 0.1.2", - "alloy-transport-http 0.1.2", + "alloy-transport 0.2.1", + "alloy-transport-http 0.2.1", "alloy-transport-ipc", "alloy-transport-ws", "futures", @@ -569,27 +604,39 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f2fbe956a3e0f0975c798f488dc6be96b669544df3737e18f4a325b42f4c86" +checksum = "e6c31a3750b8f5a350d17354e46a52b0f2f19ec5f2006d816935af599dedc521" dependencies = [ "alloy-rpc-types-engine", "alloy-rpc-types-eth", - "alloy-serde 0.1.2", + "alloy-serde 0.2.1", + "serde", +] + +[[package]] +name = "alloy-rpc-types-anvil" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ab6509cd38b2e8c8da726e0f61c1e314a81df06a38d37ddec8bced3f8d25ed" +dependencies = [ + "alloy-primitives 0.7.7", + "alloy-serde 0.2.1", + "serde", ] [[package]] name = "alloy-rpc-types-engine" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd473d98ec552f8229cd6d566bd2b0bbfc5bb4efcefbb5288c834aa8fd832020" +checksum = "ff63f51b2fb2f547df5218527fd0653afb1947bf7fead5b3ce58c75d170b30f7" dependencies = [ - "alloy-consensus 0.1.2", - "alloy-eips 0.1.2", - "alloy-primitives 0.7.6", + "alloy-consensus 0.2.1", + "alloy-eips 0.2.1", + "alloy-primitives 0.7.7", "alloy-rlp", "alloy-rpc-types-eth", - "alloy-serde 0.1.2", + "alloy-serde 0.2.1", "jsonwebtoken", "rand", "serde", @@ -598,16 +645,17 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "083f443a83b9313373817236a8f4bea09cca862618e9177d822aee579640a5d6" +checksum = "81e18424d962d7700a882fe423714bd5b9dde74c7a7589d4255ea64068773aef" dependencies = [ - "alloy-consensus 0.1.2", - "alloy-eips 0.1.2", - "alloy-primitives 0.7.6", + "alloy-consensus 0.2.1", + "alloy-eips 0.2.1", + "alloy-network-primitives", + "alloy-primitives 0.7.7", "alloy-rlp", - "alloy-serde 0.1.2", - "alloy-sol-types 0.7.6", + "alloy-serde 0.2.1", + "alloy-sol-types 0.7.7", "itertools 0.13.0", "serde", "serde_json", @@ -626,11 +674,11 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d94da1c0c4e27cc344b05626fe22a89dc6b8b531b9475f3b7691dbf6913e4109" +checksum = "e33feda6a53e6079895aed1d08dcb98a1377b000d80d16370fbbdb8155d547ef" dependencies = [ - "alloy-primitives 0.7.6", + "alloy-primitives 0.7.7", "serde", "serde_json", ] @@ -650,11 +698,11 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58d876be3afd8b78979540084ff63995292a26aa527ad0d44276405780aa0ffd" +checksum = "740a25b92e849ed7b0fa013951fe2f64be9af1ad5abe805037b44fb7770c5c47" dependencies = [ - "alloy-primitives 0.7.6", + "alloy-primitives 0.7.7", "async-trait", "auto_impl", "elliptic-curve 0.13.8", @@ -664,14 +712,14 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40a37dc216c269b8a7244047cb1c18a9c69f7a0332ab2c4c2aa4cbb1a31468b" +checksum = "1b0707d4f63e4356a110b30ef3add8732ab6d181dd7be4607bf79b8777105cee" dependencies = [ - "alloy-consensus 0.1.2", - "alloy-network 0.1.2", - "alloy-primitives 0.7.6", - "alloy-signer 0.1.2", + "alloy-consensus 0.2.1", + "alloy-network 0.2.1", + "alloy-primitives 0.7.7", + "alloy-signer 0.2.1", "async-trait", "k256", "rand", @@ -713,9 +761,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bad41a7c19498e3f6079f7744656328699f8ea3e783bdd10d85788cd439f572" +checksum = "2b40397ddcdcc266f59f959770f601ce1280e699a91fc1862f29cef91707cd09" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -727,11 +775,11 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd9899da7d011b4fe4c406a524ed3e3f963797dbc93b45479d60341d3a27b252" +checksum = "867a5469d61480fea08c7333ffeca52d5b621f5ca2e44f271b117ec1fc9a0525" dependencies = [ - "alloy-json-abi 0.7.6", + "alloy-json-abi 0.7.7", "alloy-sol-macro-input", "const-hex", "heck 0.5.0", @@ -740,17 +788,17 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.66", - "syn-solidity 0.7.6", + "syn-solidity 0.7.7", "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32d595768fdc61331a132b6f65db41afae41b9b97d36c21eb1b955c422a7e60" +checksum = "2e482dc33a32b6fadbc0f599adea520bd3aaa585c141a80b404d0a3e3fa72528" dependencies = [ - "alloy-json-abi 0.7.6", + "alloy-json-abi 0.7.7", "const-hex", "dunce", "heck 0.5.0", @@ -758,7 +806,7 @@ dependencies = [ "quote", "serde_json", "syn 2.0.66", - "syn-solidity 0.7.6", + "syn-solidity 0.7.7", ] [[package]] @@ -772,10 +820,11 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baa2fbd22d353d8685bd9fee11ba2d8b5c3b1d11e56adb3265fcf1f32bfdf404" +checksum = "cbcba3ca07cf7975f15d871b721fb18031eec8bce51103907f6dcce00b255d98" dependencies = [ + "serde", "winnow 0.6.13", ] @@ -793,13 +842,13 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a49042c6d3b66a9fe6b2b5a8bf0d39fc2ae1ee0310a2a26ffedd79fb097878dd" +checksum = "a91ca40fa20793ae9c3841b83e74569d1cc9af29a2f5237314fd3452d51e38c7" dependencies = [ - "alloy-json-abi 0.7.6", - "alloy-primitives 0.7.6", - "alloy-sol-macro 0.7.6", + "alloy-json-abi 0.7.7", + "alloy-primitives 0.7.7", + "alloy-sol-macro 0.7.7", "const-hex", "serde", ] @@ -824,11 +873,11 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245af9541f0a0dbd5258669c80dfe3af118164cacec978a520041fc130550deb" +checksum = "3d0590afbdacf2f8cca49d025a2466f3b6584a016a8b28f532f29f8da1007bae" dependencies = [ - "alloy-json-rpc 0.1.2", + "alloy-json-rpc 0.2.1", "base64 0.22.1", "futures-util", "futures-utils-wasm", @@ -837,6 +886,7 @@ dependencies = [ "thiserror", "tokio", "tower", + "tracing", "url", ] @@ -855,12 +905,12 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5619c017e1fdaa1db87f9182f4f0ed97c53d674957f4902fba655e972d359c6c" +checksum = "2437d145d80ea1aecde8574d2058cceb8b3c9cba05f6aea8e67907c660d46698" dependencies = [ - "alloy-json-rpc 0.1.2", - "alloy-transport 0.1.2", + "alloy-json-rpc 0.2.1", + "alloy-transport 0.2.1", "reqwest 0.12.5", "serde_json", "tower", @@ -870,13 +920,13 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "173cefa110afac7a53cf2e75519327761f2344d305eea2993f3af1b2c1fc1c44" +checksum = "804494366e20468776db4e18f9eb5db7db0fe14f1271eb6dbf155d867233405c" dependencies = [ - "alloy-json-rpc 0.1.2", + "alloy-json-rpc 0.2.1", "alloy-pubsub", - "alloy-transport 0.1.2", + "alloy-transport 0.2.1", "bytes", "futures", "interprocess", @@ -889,12 +939,12 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c0aff8af5be5e58856c5cdd1e46db2c67c7ecd3a652d9100b4822c96c899947" +checksum = "af855163e7df008799941aa6dd324a43ef2bf264b08ba4b22d44aad6ced65300" dependencies = [ "alloy-pubsub", - "alloy-transport 0.1.2", + "alloy-transport 0.2.1", "futures", "http 1.1.0", "rustls 0.23.10", @@ -4079,11 +4129,14 @@ dependencies = [ name = "ethereum-settlement-client" version = "0.1.0" dependencies = [ - "alloy 0.1.2", + "alloy 0.2.1", + "alloy-primitives 0.7.7", "async-trait", "c-kzg", "color-eyre", "dotenv", + "dotenvy", + "lazy_static", "mockall 0.12.1", "reqwest 0.12.5", "rstest 0.18.2", @@ -4620,7 +4673,7 @@ dependencies = [ name = "gps-fact-checker" version = "0.1.0" dependencies = [ - "alloy 0.1.2", + "alloy 0.2.1", "async-trait", "cairo-vm 1.0.0-rc3", "itertools 0.13.0", @@ -6314,7 +6367,7 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" name = "orchestrator" version = "0.1.0" dependencies = [ - "alloy 0.1.2", + "alloy 0.2.1", "arc-swap", "assert_matches", "async-std", @@ -8283,7 +8336,7 @@ dependencies = [ name = "sharp-service" version = "0.1.0" dependencies = [ - "alloy 0.1.2", + "alloy 0.2.1", "async-trait", "cairo-vm 1.0.0-rc3", "gps-fact-checker", @@ -8954,9 +9007,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d71e19bca02c807c9faa67b5a47673ff231b6e7449b251695188522f1dc44b2" +checksum = "c837dc8852cb7074e46b444afb81783140dab12c58867b49fb3898fbafedf7ea" dependencies = [ "paste", "proc-macro2", diff --git a/crates/settlement-clients/ethereum/Cargo.toml b/crates/settlement-clients/ethereum/Cargo.toml index d885e979..a6f2155b 100644 --- a/crates/settlement-clients/ethereum/Cargo.toml +++ b/crates/settlement-clients/ethereum/Cargo.toml @@ -7,6 +7,7 @@ edition.workspace = true alloy = { workspace = true, features = ["full", "node-bindings" ] } alloy-primitives = { version = "0.7.7", default-features = false } async-trait = { workspace = true } +lazy_static = "1.4.0" c-kzg = "1.0.0" color-eyre = { workspace = true } dotenv = "0.15" diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index eeee49c8..d920152f 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -39,6 +39,15 @@ use crate::conversion::{slice_slice_u8_to_vec_u256, slice_u8_to_u256}; pub mod clients; pub mod config; pub mod conversion; + +#[cfg(test)] +lazy_static! { + static ref SHOULD_IMPERSONATE_ACCOUNT: bool = get_env_var_or_panic("TEST_IMPERSONATE_OPERATOR") == "1".to_string(); + static ref TEST_DUMMY_CONTRACT_ADDRESS: String = get_env_var_or_panic("TEST_DUMMY_CONTRACT_ADDRESS"); + static ref ADDRESS_TO_IMPERSONATE: Address = Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("Unable to parse address"); + static ref TEST_NONCE: u64 = 666068; +} + #[cfg(test)] mod tests; pub mod types; @@ -104,14 +113,14 @@ impl EthereumSettlementClient { ProviderBuilder::new().with_recommended_fillers().wallet(wallet.clone()).on_http(settlement_cfg.rpc_url), ); - let core_contract_address = if get_env_var_or_panic("TEST_IMPERSONATE_OPERATOR") == "0".to_string() { - get_env_var_or_panic("TEST_DUMMY_CONTRACT_ADDRESS") + let core_contract_address = if *SHOULD_IMPERSONATE_ACCOUNT { + &settlement_cfg.core_contract_address } else { - settlement_cfg.core_contract_address + &*TEST_DUMMY_CONTRACT_ADDRESS }; let core_contract_client = StarknetValidityContractClient::new( - Address::from_str(&core_contract_address).unwrap().0.into(), + Address::from_str(core_contract_address).unwrap().0.into(), fill_provider, ); @@ -186,6 +195,7 @@ impl SettlementClient for EthereumSettlementClient { nonce: u64, ) -> Result { //TODO: better file management + let trusted_setup = KzgSettings::load_trusted_setup_file(Path::new("/Users/dexterhv/Work/Karnot/madara-alliance/madara-orchestrator/crates/settlement-clients/ethereum/src/trusted_setup.txt"))?; let (sidecar_blobs, sidecar_commitments, sidecar_proofs) = prepare_sidecar(&state_diff, &trusted_setup).await?; let sidecar = BlobTransactionSidecar::new(sidecar_blobs, sidecar_commitments, sidecar_proofs); @@ -224,7 +234,6 @@ impl SettlementClient for EthereumSettlementClient { let tx_sidecar = TxEip4844WithSidecar { tx: tx.clone(), sidecar: sidecar.clone() }; - println!("CONTTRRAACCTTT {:?}", tx.to); let mut variant = TxEip4844Variant::from(tx_sidecar); let signature = self.wallet.default_signer().sign_transaction(&mut variant).await?; let tx_signed = variant.into_signed(signature); @@ -232,25 +241,15 @@ impl SettlementClient for EthereumSettlementClient { // IMP: this conversion strips signature from the transaction let mut txn_request: TransactionRequest = tx_envelope.into(); - println!("CONTTRRAACCTTT #2 {:?}", tx.to); - - if cfg!(test) && get_env_var_or_panic("TEST_IMPERSONATE_OPERATOR") == "1".to_string() { - txn_request.set_nonce(666068); + #[cfg(test)] + if *SHOULD_IMPERSONATE_ACCOUNT { + txn_request.set_nonce(*TEST_NONCE); txn_request = txn_request.with_from( - Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7") - .expect("Unable to impersonate operator."), + *ADDRESS_TO_IMPERSONATE ); } - println!("CONTTRRAACCTTT #3 {:?}", tx.to); - - - // let encoded = tx_envelope.encoded_2718(); - // let pending_tx = self.provider.send_raw_transaction(&encoded).await?; - let pending_transaction = self.provider.send_transaction(txn_request).await?; - println!("CONTTRRAACCTTT #4 {:?}", pending_transaction.tx_hash().to_string()); - return Ok(pending_transaction.tx_hash().to_string()); } diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index 43cc02f3..826d912b 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -1,12 +1,14 @@ +use alloy::node_bindings::AnvilInstance; use alloy::{node_bindings::Anvil, sol}; use utils::env_utils::get_env_var_or_panic; +use std::env; use std::io::BufRead; +use std::path::PathBuf; use std::{ fs::{self, File}, io::BufReader, str::FromStr, }; -use url::Url; use alloy::primitives::U256; use alloy::providers::{ext::AnvilApi, ProviderBuilder}; @@ -17,136 +19,150 @@ use rstest::*; use settlement_client_interface::SettlementClient; use utils::settings::default::DefaultSettingsProvider; +use alloy::providers::Provider; +use alloy_primitives::FixedBytes; use crate::EthereumSettlementClient; +use crate::conversion::to_padded_hex; -fn hex_string_to_u8_vec(hex_str: &str) -> color_eyre::Result> { - // Remove any spaces or non-hex characters from the input string - let cleaned_str: String = hex_str.chars().filter(|c| c.is_ascii_hexdigit()).collect(); - - // Convert the cleaned hex string to a Vec - let mut result = Vec::new(); - for chunk in cleaned_str.as_bytes().chunks(2) { - if let Ok(byte_val) = u8::from_str_radix(std::str::from_utf8(chunk)?, 16) { - result.push(byte_val); - } else { - return Err(eyre!("Error parsing hex string: {}", cleaned_str)); - } +// Using the Pipe trait to write chained operations easier +trait Pipe: Sized { + fn pipe T>(self, f: F) -> T { + f(self) } - - Ok(result) } -// Codegen from ABI file to interact with the contract. -sol!( - #[allow(missing_docs)] - #[sol(rpc)] - STARKNET_CORE_CONTRACT, - "src/test_data/ABI/starknet_core_contract.json" -); +// Implement Pipe for all types +impl Pipe for S {} // TODO: betterment of file routes - - - // TODO: Checking send_transaction // Create a dummy contract and deploy on anvil with same methodId as core contract // Make an env variable that will tell if we are testing impersonation or not // Check against the env variable, if we are not impersonation then we should talk to the dummy address -sol! { - #[allow(missing_docs)] - #[sol(rpc, bytecode="6080604052348015600e575f80fd5b506101c18061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c8063b72d42a11461002d575b5f80fd5b6100476004803603810190610042919061010d565b610049565b005b50505050565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f8083601f84011261007857610077610057565b5b8235905067ffffffffffffffff8111156100955761009461005b565b5b6020830191508360208202830111156100b1576100b061005f565b5b9250929050565b5f8083601f8401126100cd576100cc610057565b5b8235905067ffffffffffffffff8111156100ea576100e961005b565b5b6020830191508360018202830111156101065761010561005f565b5b9250929050565b5f805f80604085870312156101255761012461004f565b5b5f85013567ffffffffffffffff81111561014257610141610053565b5b61014e87828801610063565b9450945050602085013567ffffffffffffffff81111561017157610170610053565b5b61017d878288016100b8565b92509250509295919450925056fea2646970667358221220fa7488d5a2a9e6c21e6f46145a831b0f04fdebab83868dc2b996c17f8cba4d8064736f6c634300081a0033")] - contract DummyCoreContract { - function updateStateKzgDA(uint256[] calldata programOutput, bytes calldata kzgProof) external { - } - } +use lazy_static::lazy_static; + +lazy_static! { + static ref ENV_FILE_PATH: PathBuf = PathBuf::from(".env.test"); + static ref CURRENT_PATH: String = env::current_dir() + .expect("Failed to get current directory") + .to_str() + .expect("Path contains invalid Unicode") + .to_string(); + static ref PORT : u16 = 3000_u16; + static ref ETH_RPC : String = "https://eth.llamarpc.com".to_string(); + static ref SHOULD_IMPERSONATE_ACCOUNT: bool = get_env_var_or_panic("TEST_IMPERSONATE_OPERATOR") == "1".to_string(); + static ref TEST_DUMMY_CONTRACT_ADDRESS : String = get_env_var_or_panic("TEST_DUMMY_CONTRACT_ADDRESS"); + static ref STARKNET_OPERATOR_ADDRESS : Address = Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("Could not impersonate account."); + static ref STARKNET_CORE_CONTRACT_ADDRESS : Address = Address::from_str("0xc662c410c0ecf747543f5ba90660f6abebd9c8c4").expect("Could not impersonate account."); } -#[rstest] -#[tokio::test] -#[case::basic(20468828)] -async fn update_state_blob_works(#[case] block_no: u64) { - use std::time::Duration; - - use alloy::providers::Provider; - use alloy_primitives::FixedBytes; - use tokio::time::sleep; +pub struct TestFixture { + pub anvil: AnvilInstance, + pub ethereum_settlement_client: EthereumSettlementClient, + pub provider: alloy::providers::RootProvider> +} +fn ethereum_test_fixture(block_no: u64) -> TestFixture { // Load ENV vars - use crate::conversion::to_padded_hex; - dotenvy::from_filename("../.env.test").expect("Could not load .env.test file."); - let current_path = std::env::current_dir().unwrap().to_str().unwrap().to_string(); + dotenvy::from_filename(&*ENV_FILE_PATH).expect("Could not load .env.test file."); // Setup Anvil - let _anvil = Anvil::new() - .port(3000_u16) - .fork("https://eth.llamarpc.com") + let anvil = Anvil::new() + .port(*PORT) + .fork(&*ETH_RPC) .fork_block_number(block_no - 1) .try_spawn() .expect("Could not spawn Anvil."); // Setup Provider let provider = - ProviderBuilder::new().on_http(Url::from_str("http://localhost:3000").expect("Could not create provider.")); + ProviderBuilder::new().on_http(anvil.endpoint_url()); // Setup EthereumSettlementClient let settings_provider: DefaultSettingsProvider = DefaultSettingsProvider {}; let ethereum_settlement_client = EthereumSettlementClient::with_test_settings(&settings_provider, provider.clone()); - let impersonate_acount = get_env_var_or_panic("TEST_IMPERSONATE_OPERATOR"); - - if impersonate_acount == "0".to_string(){ - let contract = DummyCoreContract::deploy(&provider).await.expect("Unable to deploy address"); - println!("Deployed contract at address: {}", contract.address()); - } else { - provider - .anvil_impersonate_account( - Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("Could not impersonate account."), - ) - .await - .expect("Unable to impersonate account."); + TestFixture { + anvil, + ethereum_settlement_client, + provider, } +} + +#[rstest] +#[tokio::test] +#[case::basic(20468828)] +async fn update_state_blob_with_dummy_contract_works(#[case] block_no: u64) { + env::set_var("TEST_IMPERSONATE_OPERATOR", "0"); + + let TestFixture { + anvil, + ethereum_settlement_client, + provider, + } = ethereum_test_fixture(block_no); + + + // Deploying a dummy contract + let contract = DummyCoreContract::deploy(&provider).await.expect("Unable to deploy address"); + assert_eq!(contract.address().to_string(), *TEST_DUMMY_CONTRACT_ADDRESS, "Dummy Contract got deployed on unexpected address"); + + // Getting latest nonce after deployment + let nonce = ethereum_settlement_client.get_nonce().await.expect("Unable to fetch nonce"); + + // generating program output and blob vector + let program_output = get_program_output(block_no); + let blob_data_vec = get_blob_data(block_no); + + // Calling update_state_with_blobs + let update_state_result = ethereum_settlement_client + .update_state_with_blobs(program_output, blob_data_vec, nonce) + .await + .expect("Could not go through update_state_with_blobs."); + + // Asserting, Expected to receive transaction hash. + assert!(!update_state_result.is_empty(), "No transaction Hash received."); + + let txn = provider.get_transaction_by_hash(FixedBytes::from_str(update_state_result.as_str()).expect("Unable to convert txn")) + .await.expect("did not get txn from hash").unwrap(); + + assert_eq!(txn.hash.to_string(),update_state_result.to_string()); + assert!(txn.signature.is_some()); + assert_eq!(txn.to.unwrap().to_string(), *TEST_DUMMY_CONTRACT_ADDRESS); +} + +#[rstest] +#[tokio::test] +#[case::basic(20468828)] +async fn update_state_blob_with_impersonation_works(#[case] block_no: u64) { + + let TestFixture { + anvil, + ethereum_settlement_client, + provider, + } = ethereum_test_fixture(block_no); + + provider + .anvil_impersonate_account( + *STARKNET_OPERATOR_ADDRESS, + ) + .await + .expect("Unable to impersonate account."); let nonce = ethereum_settlement_client.get_nonce().await.expect("Unable to fetch nonce"); // Create a contract instance. let contract = STARKNET_CORE_CONTRACT::new( - Address::from_str("0xc662c410c0ecf747543f5ba90660f6abebd9c8c4").expect("sd"), + *STARKNET_CORE_CONTRACT_ADDRESS, provider.clone(), ); // Call the contract, retrieve the current stateBlockNumber. let prev_block_number = contract.stateBlockNumber().call().await.unwrap(); - // Program Output - let program_output_file_path = - format!("{}{}{}{}", current_path.clone(), "/src/test_data/program_output/", block_no, ".txt"); - - let mut program_output: Vec<[u8; 32]> = Vec::new(); - { - let file = File::open(program_output_file_path).expect("Failed to read program output file"); - let reader = BufReader::new(file); - - for line in reader.lines() { - let line = line.expect("can't read line"); - let trimmed = line.trim(); - if !trimmed.is_empty() { - let v_0 = U256::from_str(trimmed).expect("Unable to convert line").to_be_bytes_vec(); - let v_1 = v_0.as_slice(); - let v_2 = to_padded_hex(v_1); - // let v_3 = v_2.replace("0x", ""); - let v_4 = hex_string_to_u8_vec(&v_2).expect("unable to convert"); - let v_5: [u8; 32] = v_4.try_into().expect("Vector length must be 32"); - program_output.push(v_5) - } - } - } - - // Blob Data - let blob_data_file_path = format!("{}{}{}{}", current_path.clone(), "/src/test_data/blob_data/", block_no, ".txt"); - println!("{}", blob_data_file_path); - let blob_data = fs::read_to_string(blob_data_file_path).expect("Failed to read the blob data txt file"); - let blob_data_vec = vec![hex_string_to_u8_vec(&blob_data).unwrap()]; + // generating program output and blob vector + let program_output = get_program_output(block_no); + let blob_data_vec = get_blob_data(block_no); // Calling update_state_with_blobs let update_state_result = ethereum_settlement_client @@ -160,23 +176,9 @@ async fn update_state_blob_works(#[case] block_no: u64) { // Call the contract, retrieve the latest stateBlockNumber. let latest_block_number = contract.stateBlockNumber().call().await.unwrap(); - if impersonate_acount == "1".to_string() { - println!("PREVIOUS BLOCK NUMBER {}", prev_block_number._0); - println!("CURRENT BLOCK HASH {}", latest_block_number._0); - assert_eq!(prev_block_number._0.as_u32() + 1, latest_block_number._0.as_u32()); - } else { - sleep(Duration::from_secs(2)).await; - let txn = provider.get_transaction_by_hash(FixedBytes::from_str(update_state_result.as_str()).expect("couln't convert")) - .await - .expect("did not get txn from hash"); - println!("{:?}", txn); - sleep(Duration::from_secs(2)).await; - - assert!(!txn.is_none()); - - - } - + println!("PREVIOUS BLOCK NUMBER {}", prev_block_number._0); + println!("CURRENT BLOCK HASH {}", latest_block_number._0); + assert_eq!(prev_block_number._0.as_u32() + 1, latest_block_number._0.as_u32()); } #[rstest] @@ -226,3 +228,91 @@ async fn creating_input_data_works(#[case] block_no: u64) { let expected = "0xb72d42a100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000001706ac7b2661801b4c0733da6ed1d2910b3b97259534ca95a63940932513111fba028bccc051eaae1b9a69b53e64a68021233b4dee2030aeda4be886324b3fbb3e00000000000000000000000000000000000000000000000000000000000a29b8070626a88de6a77855ecd683757207cdd18ba56553dca6c0c98ec523b827bee005ba2078240f1585f96424c2d1ee48211da3b3f9177bf2b9880b4fc91d59e9a2000000000000000000000000000000000000000000000000000000000000000100000000000000002b4e335bc41dc46c71f29928a5094a8c96a0c3536cabe53e0000000000000000810abb1929a0d45cdd62a20f9ccfd5807502334e7deb35d404c86d8b63a5741770fefca2f9b8efb7e663d89097edb3c60595b236f6e78e6f000000000000000000000000000000004a4b8a979fefc4d6b82e030fb082ca98000000000000000000000000000000004e8371c6774260e87b92447d4a2b0e170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000bf67f59d2988a46fbff7ed79a621778a3cd3985b0088eedbe2fe3918b69ccb411713b7fa72079d4eddf291103ccbe41e78a9615c0000000000000000000000000000000000000000000000000000000000194fe601b64b1b3b690b43b9b514fb81377518f4039cd3e4f4914d8a6bdf01d679fb1900000000000000000000000000000000000000000000000000000000000000050000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca000000000000000000000000012ccc443d39da45e5f640b3e71f0c7502152dbac01d4988e248d342439aa025b302e1f07595f6a5c810dcce23e7379e48f05d4cf000000000000000000000000000000000000000000000007f189b5374ad2a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030ab015987628cffee3ef99b9768ef8ca12c6244525f0cd10310046eaa21291b5aca164d044c5b4ad7212c767b165ed5e300000000000000000000000000000000"; assert_eq!(input_bytes, expected); } + + + + +// SOLIDITY FUNCTIONS NEEDED +sol!( + #[allow(missing_docs)] + #[sol(rpc)] + STARKNET_CORE_CONTRACT, + "src/test_data/ABI/starknet_core_contract.json" +); + +sol! { + #[allow(missing_docs)] + #[sol(rpc, bytecode="6080604052348015600e575f80fd5b506101c18061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c8063b72d42a11461002d575b5f80fd5b6100476004803603810190610042919061010d565b610049565b005b50505050565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f8083601f84011261007857610077610057565b5b8235905067ffffffffffffffff8111156100955761009461005b565b5b6020830191508360208202830111156100b1576100b061005f565b5b9250929050565b5f8083601f8401126100cd576100cc610057565b5b8235905067ffffffffffffffff8111156100ea576100e961005b565b5b6020830191508360018202830111156101065761010561005f565b5b9250929050565b5f805f80604085870312156101255761012461004f565b5b5f85013567ffffffffffffffff81111561014257610141610053565b5b61014e87828801610063565b9450945050602085013567ffffffffffffffff81111561017157610170610053565b5b61017d878288016100b8565b92509250509295919450925056fea2646970667358221220fa7488d5a2a9e6c21e6f46145a831b0f04fdebab83868dc2b996c17f8cba4d8064736f6c634300081a0033")] + contract DummyCoreContract { + function updateStateKzgDA(uint256[] calldata programOutput, bytes calldata kzgProof) external { + } + } +} + + + + + + + + + + + + + +// UTILITY FUNCTIONS NEEDED + +fn get_program_output(block_no : u64) -> Vec<[u8; 32]> { + // Program Output + let program_output_file_path = + format!("{}{}{}{}", *CURRENT_PATH, "/src/test_data/program_output/", block_no, ".txt"); + + let mut program_output: Vec<[u8; 32]> = Vec::new(); + let file = File::open(program_output_file_path).expect("Failed to read program output file"); + let reader = BufReader::new(file); + + for line in reader.lines() { + let line = line.expect("can't read line"); + let trimmed = line.trim(); + assert!(!trimmed.is_empty()); + + let result: [u8; 32] = U256::from_str(trimmed) + .expect("Unable to convert line") + .to_be_bytes_vec() + .as_slice() + .pipe(|bytes| to_padded_hex(bytes)) + .pipe(|hex| hex_string_to_u8_vec(&hex).expect("unable to convert")) + .try_into() + .expect("Vector length must be 32"); + + program_output.push(result) + } + program_output +} + +fn get_blob_data(block_no : u64) -> Vec> { + // Blob Data + let blob_data_file_path = format!("{}{}{}{}", *CURRENT_PATH, "/src/test_data/blob_data/", block_no, ".txt"); + let blob_data = fs::read_to_string(blob_data_file_path).expect("Failed to read the blob data txt file"); + let blob_data_vec = vec![hex_string_to_u8_vec(&blob_data).unwrap()]; + blob_data_vec +} + + +fn hex_string_to_u8_vec(hex_str: &str) -> color_eyre::Result> { + // Remove any spaces or non-hex characters from the input string + let cleaned_str: String = hex_str.chars().filter(|c| c.is_ascii_hexdigit()).collect(); + + // Convert the cleaned hex string to a Vec + let mut result = Vec::new(); + for chunk in cleaned_str.as_bytes().chunks(2) { + if let Ok(byte_val) = u8::from_str_radix(std::str::from_utf8(chunk)?, 16) { + result.push(byte_val); + } else { + return Err(eyre!("Error parsing hex string: {}", cleaned_str)); + } + } + + Ok(result) +} From e7023af346fc15e90f764f32b472074f84403fe9 Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Wed, 14 Aug 2024 15:08:32 +0530 Subject: [PATCH 20/41] update: test cases for settlement client --- crates/settlement-clients/ethereum/src/lib.rs | 8 +- .../ethereum/src/tests/mod.rs | 158 +++++++++--------- 2 files changed, 84 insertions(+), 82 deletions(-) diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index d920152f..994f341a 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -44,7 +44,8 @@ pub mod conversion; lazy_static! { static ref SHOULD_IMPERSONATE_ACCOUNT: bool = get_env_var_or_panic("TEST_IMPERSONATE_OPERATOR") == "1".to_string(); static ref TEST_DUMMY_CONTRACT_ADDRESS: String = get_env_var_or_panic("TEST_DUMMY_CONTRACT_ADDRESS"); - static ref ADDRESS_TO_IMPERSONATE: Address = Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("Unable to parse address"); + static ref ADDRESS_TO_IMPERSONATE: Address = + Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("Unable to parse address"); static ref TEST_NONCE: u64 = 666068; } @@ -239,14 +240,13 @@ impl SettlementClient for EthereumSettlementClient { let tx_signed = variant.into_signed(signature); let tx_envelope: TxEnvelope = tx_signed.into(); // IMP: this conversion strips signature from the transaction + let mut txn_request: TransactionRequest = tx_envelope.into(); #[cfg(test)] if *SHOULD_IMPERSONATE_ACCOUNT { txn_request.set_nonce(*TEST_NONCE); - txn_request = txn_request.with_from( - *ADDRESS_TO_IMPERSONATE - ); + txn_request = txn_request.with_from(*ADDRESS_TO_IMPERSONATE); } let pending_transaction = self.provider.send_transaction(txn_request).await?; diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index 826d912b..73dea882 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -1,28 +1,30 @@ use alloy::node_bindings::AnvilInstance; +use alloy::primitives::U256; +use alloy::providers::{ext::AnvilApi, ProviderBuilder}; use alloy::{node_bindings::Anvil, sol}; -use utils::env_utils::get_env_var_or_panic; +use alloy_primitives::Address; +use color_eyre::eyre::eyre; +use rstest::*; +use settlement_client_interface::SettlementVerificationStatus; use std::env; use std::io::BufRead; use std::path::PathBuf; +use std::time::Duration; use std::{ fs::{self, File}, io::BufReader, str::FromStr, }; - -use alloy::primitives::U256; -use alloy::providers::{ext::AnvilApi, ProviderBuilder}; -use alloy_primitives::Address; -use color_eyre::eyre::eyre; -use rstest::*; +use tokio::time::sleep; +use utils::env_utils::get_env_var_or_panic; use settlement_client_interface::SettlementClient; use utils::settings::default::DefaultSettingsProvider; +use crate::conversion::to_padded_hex; +use crate::EthereumSettlementClient; use alloy::providers::Provider; use alloy_primitives::FixedBytes; -use crate::EthereumSettlementClient; -use crate::conversion::to_padded_hex; // Using the Pipe trait to write chained operations easier trait Pipe: Sized { @@ -35,10 +37,6 @@ trait Pipe: Sized { impl Pipe for S {} // TODO: betterment of file routes -// TODO: Checking send_transaction -// Create a dummy contract and deploy on anvil with same methodId as core contract -// Make an env variable that will tell if we are testing impersonation or not -// Check against the env variable, if we are not impersonation then we should talk to the dummy address use lazy_static::lazy_static; @@ -49,18 +47,20 @@ lazy_static! { .to_str() .expect("Path contains invalid Unicode") .to_string(); - static ref PORT : u16 = 3000_u16; - static ref ETH_RPC : String = "https://eth.llamarpc.com".to_string(); + static ref PORT: u16 = 3000_u16; + static ref ETH_RPC: String = "https://eth.llamarpc.com".to_string(); static ref SHOULD_IMPERSONATE_ACCOUNT: bool = get_env_var_or_panic("TEST_IMPERSONATE_OPERATOR") == "1".to_string(); - static ref TEST_DUMMY_CONTRACT_ADDRESS : String = get_env_var_or_panic("TEST_DUMMY_CONTRACT_ADDRESS"); - static ref STARKNET_OPERATOR_ADDRESS : Address = Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("Could not impersonate account."); - static ref STARKNET_CORE_CONTRACT_ADDRESS : Address = Address::from_str("0xc662c410c0ecf747543f5ba90660f6abebd9c8c4").expect("Could not impersonate account."); + static ref TEST_DUMMY_CONTRACT_ADDRESS: String = get_env_var_or_panic("TEST_DUMMY_CONTRACT_ADDRESS"); + static ref STARKNET_OPERATOR_ADDRESS: Address = + Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("Could not impersonate account."); + static ref STARKNET_CORE_CONTRACT_ADDRESS: Address = + Address::from_str("0xc662c410c0ecf747543f5ba90660f6abebd9c8c4").expect("Could not impersonate account."); } pub struct TestFixture { pub anvil: AnvilInstance, pub ethereum_settlement_client: EthereumSettlementClient, - pub provider: alloy::providers::RootProvider> + pub provider: alloy::providers::RootProvider>, } fn ethereum_test_fixture(block_no: u64) -> TestFixture { @@ -76,18 +76,13 @@ fn ethereum_test_fixture(block_no: u64) -> TestFixture { .expect("Could not spawn Anvil."); // Setup Provider - let provider = - ProviderBuilder::new().on_http(anvil.endpoint_url()); + let provider = ProviderBuilder::new().on_http(anvil.endpoint_url()); // Setup EthereumSettlementClient let settings_provider: DefaultSettingsProvider = DefaultSettingsProvider {}; let ethereum_settlement_client = EthereumSettlementClient::with_test_settings(&settings_provider, provider.clone()); - TestFixture { - anvil, - ethereum_settlement_client, - provider, - } + TestFixture { anvil, ethereum_settlement_client, provider } } #[rstest] @@ -96,17 +91,16 @@ fn ethereum_test_fixture(block_no: u64) -> TestFixture { async fn update_state_blob_with_dummy_contract_works(#[case] block_no: u64) { env::set_var("TEST_IMPERSONATE_OPERATOR", "0"); - let TestFixture { - anvil, - ethereum_settlement_client, - provider, - } = ethereum_test_fixture(block_no); - + let TestFixture { anvil, ethereum_settlement_client, provider } = ethereum_test_fixture(block_no); // Deploying a dummy contract let contract = DummyCoreContract::deploy(&provider).await.expect("Unable to deploy address"); - assert_eq!(contract.address().to_string(), *TEST_DUMMY_CONTRACT_ADDRESS, "Dummy Contract got deployed on unexpected address"); - + assert_eq!( + contract.address().to_string(), + *TEST_DUMMY_CONTRACT_ADDRESS, + "Dummy Contract got deployed on unexpected address" + ); + // Getting latest nonce after deployment let nonce = ethereum_settlement_client.get_nonce().await.expect("Unable to fetch nonce"); @@ -123,39 +117,41 @@ async fn update_state_blob_with_dummy_contract_works(#[case] block_no: u64) { // Asserting, Expected to receive transaction hash. assert!(!update_state_result.is_empty(), "No transaction Hash received."); - let txn = provider.get_transaction_by_hash(FixedBytes::from_str(update_state_result.as_str()).expect("Unable to convert txn")) - .await.expect("did not get txn from hash").unwrap(); + let txn = provider + .get_transaction_by_hash(FixedBytes::from_str(update_state_result.as_str()).expect("Unable to convert txn")) + .await + .expect("did not get txn from hash") + .unwrap(); - assert_eq!(txn.hash.to_string(),update_state_result.to_string()); + assert_eq!(txn.hash.to_string(), update_state_result.to_string()); assert!(txn.signature.is_some()); assert_eq!(txn.to.unwrap().to_string(), *TEST_DUMMY_CONTRACT_ADDRESS); + + // Testing verify_tx_inclusion + sleep(Duration::from_secs(2)).await; + let _ = ethereum_settlement_client + .wait_for_tx_finality(update_state_result.as_str()) + .await + .expect("Could not wait for txn finality."); + let verified_inclusion = ethereum_settlement_client + .verify_tx_inclusion(update_state_result.as_str()) + .await + .expect("Could not verify inclusion."); + assert_eq!(verified_inclusion, SettlementVerificationStatus::Verified); } #[rstest] #[tokio::test] #[case::basic(20468828)] async fn update_state_blob_with_impersonation_works(#[case] block_no: u64) { - - let TestFixture { - anvil, - ethereum_settlement_client, - provider, - } = ethereum_test_fixture(block_no); - - provider - .anvil_impersonate_account( - *STARKNET_OPERATOR_ADDRESS, - ) - .await - .expect("Unable to impersonate account."); + let TestFixture { anvil, ethereum_settlement_client, provider } = ethereum_test_fixture(block_no); + + provider.anvil_impersonate_account(*STARKNET_OPERATOR_ADDRESS).await.expect("Unable to impersonate account."); let nonce = ethereum_settlement_client.get_nonce().await.expect("Unable to fetch nonce"); // Create a contract instance. - let contract = STARKNET_CORE_CONTRACT::new( - *STARKNET_CORE_CONTRACT_ADDRESS, - provider.clone(), - ); + let contract = STARKNET_CORE_CONTRACT::new(*STARKNET_CORE_CONTRACT_ADDRESS, provider.clone()); // Call the contract, retrieve the current stateBlockNumber. let prev_block_number = contract.stateBlockNumber().call().await.unwrap(); @@ -179,6 +175,28 @@ async fn update_state_blob_with_impersonation_works(#[case] block_no: u64) { println!("PREVIOUS BLOCK NUMBER {}", prev_block_number._0); println!("CURRENT BLOCK HASH {}", latest_block_number._0); assert_eq!(prev_block_number._0.as_u32() + 1, latest_block_number._0.as_u32()); + + // Testing verify_tx_inclusion + sleep(Duration::from_secs(2)).await; + ethereum_settlement_client + .wait_for_tx_finality(update_state_result.as_str()) + .await + .expect("Could not wait for txn finality."); + let verified_inclusion = ethereum_settlement_client + .verify_tx_inclusion(update_state_result.as_str()) + .await + .expect("Could not verify inclusion."); + assert_eq!(verified_inclusion, SettlementVerificationStatus::Verified); +} + +#[rstest] +#[tokio::test] +#[case::typical(20468828, 666039)] +async fn get_last_settled_block_typical_works(#[case] block_no: u64, #[case] expected: u64) { + let TestFixture { anvil, ethereum_settlement_client, provider } = ethereum_test_fixture(block_no); + + let result = ethereum_settlement_client.get_last_settled_block().await.expect("Could not get last settled block."); + assert_eq!(expected, result); } #[rstest] @@ -229,9 +247,6 @@ async fn creating_input_data_works(#[case] block_no: u64) { assert_eq!(input_bytes, expected); } - - - // SOLIDITY FUNCTIONS NEEDED sol!( #[allow(missing_docs)] @@ -249,24 +264,12 @@ sol! { } } - - - - - - - - - - - - // UTILITY FUNCTIONS NEEDED -fn get_program_output(block_no : u64) -> Vec<[u8; 32]> { +fn get_program_output(block_no: u64) -> Vec<[u8; 32]> { // Program Output let program_output_file_path = - format!("{}{}{}{}", *CURRENT_PATH, "/src/test_data/program_output/", block_no, ".txt"); + format!("{}{}{}{}", *CURRENT_PATH, "/src/test_data/program_output/", block_no, ".txt"); let mut program_output: Vec<[u8; 32]> = Vec::new(); let file = File::open(program_output_file_path).expect("Failed to read program output file"); @@ -291,15 +294,14 @@ fn get_program_output(block_no : u64) -> Vec<[u8; 32]> { program_output } -fn get_blob_data(block_no : u64) -> Vec> { - // Blob Data - let blob_data_file_path = format!("{}{}{}{}", *CURRENT_PATH, "/src/test_data/blob_data/", block_no, ".txt"); - let blob_data = fs::read_to_string(blob_data_file_path).expect("Failed to read the blob data txt file"); - let blob_data_vec = vec![hex_string_to_u8_vec(&blob_data).unwrap()]; - blob_data_vec +fn get_blob_data(block_no: u64) -> Vec> { + // Blob Data + let blob_data_file_path = format!("{}{}{}{}", *CURRENT_PATH, "/src/test_data/blob_data/", block_no, ".txt"); + let blob_data = fs::read_to_string(blob_data_file_path).expect("Failed to read the blob data txt file"); + let blob_data_vec = vec![hex_string_to_u8_vec(&blob_data).unwrap()]; + blob_data_vec } - fn hex_string_to_u8_vec(hex_str: &str) -> color_eyre::Result> { // Remove any spaces or non-hex characters from the input string let cleaned_str: String = hex_str.chars().filter(|c| c.is_ascii_hexdigit()).collect(); From 391d693e13e7a723473116ff9045909b0eb1c22a Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Wed, 14 Aug 2024 21:13:57 +0530 Subject: [PATCH 21/41] chore: lint fixes --- .../src/tests/jobs/state_update_job/mod.rs | 13 +++++++++---- crates/settlement-clients/ethereum/Cargo.toml | 6 +++--- crates/settlement-clients/ethereum/src/lib.rs | 12 ++++++++---- crates/settlement-clients/ethereum/src/tests/mod.rs | 6 +++--- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/crates/orchestrator/src/tests/jobs/state_update_job/mod.rs b/crates/orchestrator/src/tests/jobs/state_update_job/mod.rs index e0c183b2..945328c6 100644 --- a/crates/orchestrator/src/tests/jobs/state_update_job/mod.rs +++ b/crates/orchestrator/src/tests/jobs/state_update_job/mod.rs @@ -6,7 +6,7 @@ use bytes::Bytes; use httpmock::prelude::*; use mockall::predicate::eq; use rstest::*; -use settlement_client_interface::{MockSettlementClient, SettlementClient}; +use settlement_client_interface::MockSettlementClient; use color_eyre::eyre::eyre; @@ -76,14 +76,17 @@ async fn test_process_job_works( // functions while fetching the blob data from storage client. TestConfigBuilder::new().build().await; + let nonce: u64 = 3; + settlement_client.expect_get_nonce().with().returning(move || Ok(nonce)); + // Adding expectations for each block number to be called by settlement client. for block in block_numbers.iter().skip(processing_start_index as usize) { let blob_data = fetch_blob_data_for_block(block.to_u64().unwrap()).await.unwrap(); settlement_client .expect_update_state_with_blobs() - .with(eq(vec![]), eq(blob_data)) + .with(eq(vec![]), eq(blob_data), eq(nonce)) .times(1) - .returning(|_, _| Ok("0xbeef".to_string())); + .returning(|_, _, _| Ok("0xbeef".to_string())); } settlement_client.expect_get_last_settled_block().with().returning(move || Ok(651052)); @@ -178,7 +181,9 @@ async fn test_process_job() { .expect("Failed to read the blob data txt file"); storage_client.expect_get_data().with(eq(x_0_key)).returning(move |_| Ok(Bytes::from(x_0.clone()))); - let nonce = settlement_client.get_nonce().await.expect("Unable to fetch nonce for settlement client."); + // let nonce = settlement_client.get_nonce().await.expect("Unable to fetch nonce for settlement client."); + let nonce: u64 = 1; + settlement_client.expect_get_nonce().returning(|| Ok(1)); settlement_client .expect_update_state_with_blobs() diff --git a/crates/settlement-clients/ethereum/Cargo.toml b/crates/settlement-clients/ethereum/Cargo.toml index a6f2155b..94b454d6 100644 --- a/crates/settlement-clients/ethereum/Cargo.toml +++ b/crates/settlement-clients/ethereum/Cargo.toml @@ -4,13 +4,14 @@ version.workspace = true edition.workspace = true [dependencies] -alloy = { workspace = true, features = ["full", "node-bindings" ] } +alloy = { workspace = true, features = ["full", "node-bindings"] } alloy-primitives = { version = "0.7.7", default-features = false } async-trait = { workspace = true } -lazy_static = "1.4.0" c-kzg = "1.0.0" color-eyre = { workspace = true } dotenv = "0.15" +dotenvy = { workspace = true } +lazy_static = "1.4.0" mockall = "0.12.1" reqwest = { version = "0.12.3" } rstest = { workspace = true } @@ -20,7 +21,6 @@ snos = { workspace = true } tokio = { workspace = true } url = { workspace = true } utils = { workspace = true } -dotenvy = {workspace = true} [dev-dependencies] tokio-test = "*" diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index 994f341a..40905248 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -17,7 +17,6 @@ use alloy::{ use alloy::eips::eip2930::AccessList; use alloy::eips::eip4844::BYTES_PER_BLOB; use alloy::hex; -use alloy::network::TransactionBuilder; // use alloy::node_bindings::Anvil; use alloy::rpc::types::TransactionRequest; use alloy_primitives::Bytes; @@ -42,7 +41,7 @@ pub mod conversion; #[cfg(test)] lazy_static! { - static ref SHOULD_IMPERSONATE_ACCOUNT: bool = get_env_var_or_panic("TEST_IMPERSONATE_OPERATOR") == "1".to_string(); + static ref SHOULD_IMPERSONATE_ACCOUNT: bool = get_env_var_or_panic("TEST_IMPERSONATE_OPERATOR") == *"1"; static ref TEST_DUMMY_CONTRACT_ADDRESS: String = get_env_var_or_panic("TEST_DUMMY_CONTRACT_ADDRESS"); static ref ADDRESS_TO_IMPERSONATE: Address = Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("Unable to parse address"); @@ -196,6 +195,8 @@ impl SettlementClient for EthereumSettlementClient { nonce: u64, ) -> Result { //TODO: better file management + #[cfg(test)] + use alloy::network::TransactionBuilder; let trusted_setup = KzgSettings::load_trusted_setup_file(Path::new("/Users/dexterhv/Work/Karnot/madara-alliance/madara-orchestrator/crates/settlement-clients/ethereum/src/trusted_setup.txt"))?; let (sidecar_blobs, sidecar_commitments, sidecar_proofs) = prepare_sidecar(&state_diff, &trusted_setup).await?; @@ -240,9 +241,12 @@ impl SettlementClient for EthereumSettlementClient { let tx_signed = variant.into_signed(signature); let tx_envelope: TxEnvelope = tx_signed.into(); // IMP: this conversion strips signature from the transaction - - let mut txn_request: TransactionRequest = tx_envelope.into(); + #[cfg(not(test))] + let txn_request: TransactionRequest = tx_envelope.into(); + + #[cfg(test)] + let mut txn_request: TransactionRequest = tx_envelope.into(); #[cfg(test)] if *SHOULD_IMPERSONATE_ACCOUNT { txn_request.set_nonce(*TEST_NONCE); diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index 73dea882..e24aeed0 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -49,7 +49,7 @@ lazy_static! { .to_string(); static ref PORT: u16 = 3000_u16; static ref ETH_RPC: String = "https://eth.llamarpc.com".to_string(); - static ref SHOULD_IMPERSONATE_ACCOUNT: bool = get_env_var_or_panic("TEST_IMPERSONATE_OPERATOR") == "1".to_string(); + static ref SHOULD_IMPERSONATE_ACCOUNT: bool = get_env_var_or_panic("TEST_IMPERSONATE_OPERATOR") == *"1"; static ref TEST_DUMMY_CONTRACT_ADDRESS: String = get_env_var_or_panic("TEST_DUMMY_CONTRACT_ADDRESS"); static ref STARKNET_OPERATOR_ADDRESS: Address = Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("Could not impersonate account."); @@ -129,7 +129,7 @@ async fn update_state_blob_with_dummy_contract_works(#[case] block_no: u64) { // Testing verify_tx_inclusion sleep(Duration::from_secs(2)).await; - let _ = ethereum_settlement_client + ethereum_settlement_client .wait_for_tx_finality(update_state_result.as_str()) .await .expect("Could not wait for txn finality."); @@ -284,7 +284,7 @@ fn get_program_output(block_no: u64) -> Vec<[u8; 32]> { .expect("Unable to convert line") .to_be_bytes_vec() .as_slice() - .pipe(|bytes| to_padded_hex(bytes)) + .pipe(to_padded_hex) .pipe(|hex| hex_string_to_u8_vec(&hex).expect("unable to convert")) .try_into() .expect("Vector length must be 32"); From 856fa1f86f76507e4bfe3a77c737cf56f752e343 Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Wed, 14 Aug 2024 21:20:22 +0530 Subject: [PATCH 22/41] chore: fix lints --- crates/settlement-clients/ethereum/src/tests/mod.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index e24aeed0..0049cea8 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -1,4 +1,3 @@ -use alloy::node_bindings::AnvilInstance; use alloy::primitives::U256; use alloy::providers::{ext::AnvilApi, ProviderBuilder}; use alloy::{node_bindings::Anvil, sol}; @@ -58,7 +57,6 @@ lazy_static! { } pub struct TestFixture { - pub anvil: AnvilInstance, pub ethereum_settlement_client: EthereumSettlementClient, pub provider: alloy::providers::RootProvider>, } @@ -82,7 +80,7 @@ fn ethereum_test_fixture(block_no: u64) -> TestFixture { let settings_provider: DefaultSettingsProvider = DefaultSettingsProvider {}; let ethereum_settlement_client = EthereumSettlementClient::with_test_settings(&settings_provider, provider.clone()); - TestFixture { anvil, ethereum_settlement_client, provider } + TestFixture { ethereum_settlement_client, provider } } #[rstest] @@ -91,7 +89,7 @@ fn ethereum_test_fixture(block_no: u64) -> TestFixture { async fn update_state_blob_with_dummy_contract_works(#[case] block_no: u64) { env::set_var("TEST_IMPERSONATE_OPERATOR", "0"); - let TestFixture { anvil, ethereum_settlement_client, provider } = ethereum_test_fixture(block_no); + let TestFixture { ethereum_settlement_client, provider } = ethereum_test_fixture(block_no); // Deploying a dummy contract let contract = DummyCoreContract::deploy(&provider).await.expect("Unable to deploy address"); @@ -144,7 +142,7 @@ async fn update_state_blob_with_dummy_contract_works(#[case] block_no: u64) { #[tokio::test] #[case::basic(20468828)] async fn update_state_blob_with_impersonation_works(#[case] block_no: u64) { - let TestFixture { anvil, ethereum_settlement_client, provider } = ethereum_test_fixture(block_no); + let TestFixture { ethereum_settlement_client, provider } = ethereum_test_fixture(block_no); provider.anvil_impersonate_account(*STARKNET_OPERATOR_ADDRESS).await.expect("Unable to impersonate account."); @@ -193,7 +191,7 @@ async fn update_state_blob_with_impersonation_works(#[case] block_no: u64) { #[tokio::test] #[case::typical(20468828, 666039)] async fn get_last_settled_block_typical_works(#[case] block_no: u64, #[case] expected: u64) { - let TestFixture { anvil, ethereum_settlement_client, provider } = ethereum_test_fixture(block_no); + let TestFixture { ethereum_settlement_client, provider: _ } = ethereum_test_fixture(block_no); let result = ethereum_settlement_client.get_last_settled_block().await.expect("Could not get last settled block."); assert_eq!(expected, result); From 6b69fddc5e7a5eca99a750261be33a4bd570132b Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Fri, 16 Aug 2024 12:13:51 +0530 Subject: [PATCH 23/41] update: Changes for PR review --- CHANGELOG.md | 2 +- crates/orchestrator/src/jobs/da_job/mod.rs | 2 +- .../src/tests/jobs/state_update_job/mod.rs | 2 + .../ethereum/src/conversion.rs | 36 +++---- crates/settlement-clients/ethereum/src/lib.rs | 18 +++- .../ethereum/src/tests/mod.rs | 98 +++++++------------ 6 files changed, 66 insertions(+), 92 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9db1653..329ae434 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## Added +- Tests for Settlement client. - added coveralls support - moved mongodb serde behind feature flag - implemented DA worker. @@ -21,7 +22,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Added tests for state update job. - Tests for DA job. - Database tests -- Tests for Settlement client. ## Changed diff --git a/crates/orchestrator/src/jobs/da_job/mod.rs b/crates/orchestrator/src/jobs/da_job/mod.rs index ef05934e..5e49d75c 100644 --- a/crates/orchestrator/src/jobs/da_job/mod.rs +++ b/crates/orchestrator/src/jobs/da_job/mod.rs @@ -537,7 +537,7 @@ pub mod test { } } - pub fn vec_u8_to_hex_string(data: &[u8]) -> String { + fn vec_u8_to_hex_string(data: &[u8]) -> String { let hex_chars: Vec = data.iter().map(|byte| format!("{:02x}", byte)).collect(); let mut new_hex_chars = hex_chars.join(""); diff --git a/crates/orchestrator/src/tests/jobs/state_update_job/mod.rs b/crates/orchestrator/src/tests/jobs/state_update_job/mod.rs index a955ead4..96734860 100644 --- a/crates/orchestrator/src/tests/jobs/state_update_job/mod.rs +++ b/crates/orchestrator/src/tests/jobs/state_update_job/mod.rs @@ -76,6 +76,8 @@ async fn test_process_job_works( // functions while fetching the blob data from storage client. TestConfigBuilder::new().build().await; + // test_process_job_works uses nonce just to write expect_update_state_with_blobs for a mocked settlement client, + // which means that nonce ideally is never checked against, hence supplying any `u64` `nonce` works. let nonce: u64 = 3; settlement_client.expect_get_nonce().with().returning(move || Ok(nonce)); diff --git a/crates/settlement-clients/ethereum/src/conversion.rs b/crates/settlement-clients/ethereum/src/conversion.rs index ce355003..79b1234b 100644 --- a/crates/settlement-clients/ethereum/src/conversion.rs +++ b/crates/settlement-clients/ethereum/src/conversion.rs @@ -8,7 +8,7 @@ use std::fmt::Write; /// Converts a `&[Vec]` to `Vec`. Each inner slice is expected to be exactly 32 bytes long. /// Pads with zeros if any inner slice is shorter than 32 bytes. -pub(crate) fn slice_slice_u8_to_vec_u256(slices: &[[u8; 32]]) -> EyreResult> { +pub(crate) fn vec_u8_32_to_vec_u256(slices: &[[u8; 32]]) -> EyreResult> { slices.iter().map(|slice| slice_u8_to_u256(slice)).collect() } @@ -143,28 +143,14 @@ mod tests { #[case::short(&[0xFF; 16], U256::from_be_slice(&[0xFF; 16]))] #[case::empty(&[], U256::ZERO)] fn slice_u8_to_u256_works(#[case] slice: &[u8], #[case] expected: U256) { - match slice_u8_to_u256(slice) { - Ok(response) => { - assert_eq!(response, expected); - } - Err(e) => { - panic!("{}", e); - } - } + assert_eq!(slice_u8_to_u256(slice).expect("slice_u8_to_u256 failed"), expected) } #[rstest] + #[should_panic(expected = "could not convert &[u8] to U256")] #[case::over(&[0xFF; 33])] fn slice_u8_to_u256_panics(#[case] slice: &[u8]) { - let result: Result, color_eyre::eyre::Error> = slice_u8_to_u256(slice); - match result { - Ok(_) => { - panic!("{}", "Should not have passed"); - } - Err(report) => { - assert_eq!(report.to_string(), "could not convert &[u8] to U256") - } - } + let _ = slice_u8_to_u256(slice); } #[rstest] @@ -197,8 +183,8 @@ mod tests { U256::from_be_slice(&[0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), ] )] - fn slice_slice_u8_to_vec_u256_works(#[case] slices: &[[u8; 32]], #[case] expected: Vec) { - match slice_slice_u8_to_vec_u256(slices) { + fn vec_u8_32_to_vec_u256_works(#[case] slices: &[[u8; 32]], #[case] expected: Vec) { + match vec_u8_32_to_vec_u256(slices) { Ok(response) => { assert_eq!(response, expected); } @@ -262,11 +248,13 @@ mod tests { assert_eq!(result, expected); } + // block_no here are Ethereum(mainnet) blocks, we are creating sidecar and validating + // the function by matching pre-existing commitments against computed. #[rstest] #[case("20462788")] #[case("20462818")] #[tokio::test] - async fn prepare_sidecar_works(#[case] block_no: String) { + async fn prepare_sidecar_works(#[case] fork_block_no: String) { // Trusted Setup let current_path = std::env::current_dir().unwrap().to_str().unwrap().to_string(); @@ -276,19 +264,19 @@ mod tests { // Blob Data let blob_data_file_path = - format!("{}{}{}{}", current_path.clone(), "/src/test_data/blob_data/", block_no, ".txt"); + format!("{}{}{}{}", current_path.clone(), "/src/test_data/blob_data/", fork_block_no, ".txt"); println!("{}", blob_data_file_path); let blob_data = fs::read_to_string(blob_data_file_path).expect("Failed to read the blob data txt file"); // Blob Commitment let blob_commitment_file_path = - format!("{}{}{}{}", current_path.clone(), "/src/test_data/blob_commitment/", block_no, ".txt"); + format!("{}{}{}{}", current_path.clone(), "/src/test_data/blob_commitment/", fork_block_no, ".txt"); let blob_commitment = fs::read_to_string(blob_commitment_file_path).expect("Failed to read the blob data txt file"); // Blob Proof let blob_proof_file_path = - format!("{}{}{}{}", current_path.clone(), "/src/test_data/blob_proof/", block_no, ".txt"); + format!("{}{}{}{}", current_path.clone(), "/src/test_data/blob_proof/", fork_block_no, ".txt"); let blob_proof = fs::read_to_string(blob_proof_file_path).expect("Failed to read the blob data txt file"); fn hex_string_to_u8_vec(hex_str: &str) -> color_eyre::Result> { diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index 40905248..13a1107b 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -34,11 +34,18 @@ use utils::{env_utils::get_env_var_or_panic, settings::SettingsProvider}; use crate::clients::interfaces::validity_interface::StarknetValidityContractTrait; use crate::clients::StarknetValidityContractClient; use crate::config::EthereumSettlementConfig; -use crate::conversion::{slice_slice_u8_to_vec_u256, slice_u8_to_u256}; +use crate::conversion::{slice_u8_to_u256, vec_u8_32_to_vec_u256}; pub mod clients; pub mod config; pub mod conversion; +// IMPORTANT to understand #[cfg(test)], #[cfg(not(test))] and SHOULD_IMPERSONATE_ACCOUNT +// Two tests : `update_state_blob_with_dummy_contract_works` & `update_state_blob_with_impersonation_works` use a env var `TEST_IMPERSONATE_OPERATOR` to inform the function `update_state_with_blobs` about the kind of testing, +// `TEST_IMPERSONATE_OPERATOR` can have any of "0" or "1" value : +// - if "0" then : Testing against Dummy Contract. +// - if "1" then : Testing via impersonating `Starknet Operator Address`. +// Note : changing between "0" and "1" is handled automatically by each test function, `no` manual change in `env.test` is needed. + #[cfg(test)] lazy_static! { static ref SHOULD_IMPERSONATE_ACCOUNT: bool = get_env_var_or_panic("TEST_IMPERSONATE_OPERATOR") == *"1"; @@ -93,7 +100,10 @@ impl EthereumSettlementClient { ProviderBuilder::new().with_recommended_fillers().wallet(wallet.clone()).on_http(settlement_cfg.rpc_url), ); let core_contract_client = StarknetValidityContractClient::new( - Address::from_str(&settlement_cfg.core_contract_address).unwrap().0.into(), + Address::from_str(&settlement_cfg.core_contract_address) + .expect("Failed to convert the validity contract address.") + .0 + .into(), provider.clone(), ); @@ -173,7 +183,7 @@ impl SettlementClient for EthereumSettlementClient { onchain_data_hash: [u8; 32], onchain_data_size: usize, ) -> Result { - let program_output: Vec = slice_slice_u8_to_vec_u256(program_output.as_slice())?; + let program_output: Vec = vec_u8_32_to_vec_u256(program_output.as_slice())?; let onchain_data_hash: U256 = slice_u8_to_u256(&onchain_data_hash)?; let onchain_data_size: U256 = onchain_data_size.try_into()?; let tx_receipt = @@ -183,7 +193,7 @@ impl SettlementClient for EthereumSettlementClient { /// Should be used to update state on core contract when DA is in blobs/alt DA async fn update_state_blobs(&self, program_output: Vec<[u8; 32]>, kzg_proof: [u8; 48]) -> Result { - let program_output: Vec = slice_slice_u8_to_vec_u256(&program_output)?; + let program_output: Vec = vec_u8_32_to_vec_u256(&program_output)?; let tx_receipt = self.core_contract_client.update_state_kzg(program_output, kzg_proof).await?; Ok(format!("0x{:x}", tx_receipt.transaction_hash)) } diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index 0049cea8..fd654acd 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -56,12 +56,29 @@ lazy_static! { Address::from_str("0xc662c410c0ecf747543f5ba90660f6abebd9c8c4").expect("Could not impersonate account."); } -pub struct TestFixture { +// SOLIDITY FUNCTIONS NEEDED +sol!( + #[allow(missing_docs)] + #[sol(rpc)] + STARKNET_CORE_CONTRACT, + "src/test_data/abi/starknet_core_contract.json" +); + +sol! { + #[allow(missing_docs)] + #[sol(rpc, bytecode="6080604052348015600e575f80fd5b506101c18061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c8063b72d42a11461002d575b5f80fd5b6100476004803603810190610042919061010d565b610049565b005b50505050565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f8083601f84011261007857610077610057565b5b8235905067ffffffffffffffff8111156100955761009461005b565b5b6020830191508360208202830111156100b1576100b061005f565b5b9250929050565b5f8083601f8401126100cd576100cc610057565b5b8235905067ffffffffffffffff8111156100ea576100e961005b565b5b6020830191508360018202830111156101065761010561005f565b5b9250929050565b5f805f80604085870312156101255761012461004f565b5b5f85013567ffffffffffffffff81111561014257610141610053565b5b61014e87828801610063565b9450945050602085013567ffffffffffffffff81111561017157610170610053565b5b61017d878288016100b8565b92509250509295919450925056fea2646970667358221220fa7488d5a2a9e6c21e6f46145a831b0f04fdebab83868dc2b996c17f8cba4d8064736f6c634300081a0033")] + contract DummyCoreContract { + function updateStateKzgDA(uint256[] calldata programOutput, bytes calldata kzgProof) external { + } + } +} + +pub struct TestSetup { pub ethereum_settlement_client: EthereumSettlementClient, pub provider: alloy::providers::RootProvider>, } -fn ethereum_test_fixture(block_no: u64) -> TestFixture { +fn setup_ethereum_test(block_no: u64) -> TestSetup { // Load ENV vars dotenvy::from_filename(&*ENV_FILE_PATH).expect("Could not load .env.test file."); @@ -80,16 +97,17 @@ fn ethereum_test_fixture(block_no: u64) -> TestFixture { let settings_provider: DefaultSettingsProvider = DefaultSettingsProvider {}; let ethereum_settlement_client = EthereumSettlementClient::with_test_settings(&settings_provider, provider.clone()); - TestFixture { ethereum_settlement_client, provider } + TestSetup { ethereum_settlement_client, provider } } #[rstest] #[tokio::test] #[case::basic(20468828)] -async fn update_state_blob_with_dummy_contract_works(#[case] block_no: u64) { +/// tests if the method is able to do a transaction with same function selector on a dummy contract. +async fn update_state_blob_with_dummy_contract_works(#[case] fork_block_no: u64) { env::set_var("TEST_IMPERSONATE_OPERATOR", "0"); - let TestFixture { ethereum_settlement_client, provider } = ethereum_test_fixture(block_no); + let TestSetup { ethereum_settlement_client, provider } = setup_ethereum_test(fork_block_no); // Deploying a dummy contract let contract = DummyCoreContract::deploy(&provider).await.expect("Unable to deploy address"); @@ -103,8 +121,8 @@ async fn update_state_blob_with_dummy_contract_works(#[case] block_no: u64) { let nonce = ethereum_settlement_client.get_nonce().await.expect("Unable to fetch nonce"); // generating program output and blob vector - let program_output = get_program_output(block_no); - let blob_data_vec = get_blob_data(block_no); + let program_output = get_program_output(fork_block_no); + let blob_data_vec = get_blob_data(fork_block_no); // Calling update_state_with_blobs let update_state_result = ethereum_settlement_client @@ -141,8 +159,9 @@ async fn update_state_blob_with_dummy_contract_works(#[case] block_no: u64) { #[rstest] #[tokio::test] #[case::basic(20468828)] -async fn update_state_blob_with_impersonation_works(#[case] block_no: u64) { - let TestFixture { ethereum_settlement_client, provider } = ethereum_test_fixture(block_no); +/// tests if the method is able to impersonate the`Starknet Operator` and do an `update_state` transaction. +async fn update_state_blob_with_impersonation_works(#[case] fork_block_no: u64) { + let TestSetup { ethereum_settlement_client, provider } = setup_ethereum_test(fork_block_no); provider.anvil_impersonate_account(*STARKNET_OPERATOR_ADDRESS).await.expect("Unable to impersonate account."); @@ -155,8 +174,8 @@ async fn update_state_blob_with_impersonation_works(#[case] block_no: u64) { let prev_block_number = contract.stateBlockNumber().call().await.unwrap(); // generating program output and blob vector - let program_output = get_program_output(block_no); - let blob_data_vec = get_blob_data(block_no); + let program_output = get_program_output(fork_block_no); + let blob_data_vec = get_blob_data(fork_block_no); // Calling update_state_with_blobs let update_state_result = ethereum_settlement_client @@ -170,8 +189,6 @@ async fn update_state_blob_with_impersonation_works(#[case] block_no: u64) { // Call the contract, retrieve the latest stateBlockNumber. let latest_block_number = contract.stateBlockNumber().call().await.unwrap(); - println!("PREVIOUS BLOCK NUMBER {}", prev_block_number._0); - println!("CURRENT BLOCK HASH {}", latest_block_number._0); assert_eq!(prev_block_number._0.as_u32() + 1, latest_block_number._0.as_u32()); // Testing verify_tx_inclusion @@ -190,8 +207,8 @@ async fn update_state_blob_with_impersonation_works(#[case] block_no: u64) { #[rstest] #[tokio::test] #[case::typical(20468828, 666039)] -async fn get_last_settled_block_typical_works(#[case] block_no: u64, #[case] expected: u64) { - let TestFixture { ethereum_settlement_client, provider: _ } = ethereum_test_fixture(block_no); +async fn get_last_settled_block_typical_works(#[case] fork_block_no: u64, #[case] expected: u64) { + let TestSetup { ethereum_settlement_client, provider: _ } = setup_ethereum_test(fork_block_no); let result = ethereum_settlement_client.get_last_settled_block().await.expect("Could not get last settled block."); assert_eq!(expected, result); @@ -200,42 +217,16 @@ async fn get_last_settled_block_typical_works(#[case] block_no: u64, #[case] exp #[rstest] #[tokio::test] #[case::basic(20468828)] -async fn creating_input_data_works(#[case] block_no: u64) { +async fn creating_input_data_works(#[case] fork_block_no: u64) { use c_kzg::Bytes32; - use crate::conversion::{get_input_data_for_eip_4844, to_padded_hex}; - - let current_path = std::env::current_dir().unwrap().to_str().unwrap().to_string(); - - let program_output_file_path = - format!("{}{}{}{}", current_path.clone(), "/src/test_data/program_output/", block_no, ".txt"); - - let mut program_output: Vec<[u8; 32]> = Vec::new(); - let file = File::open(program_output_file_path).expect("Failed to read program output file"); - let reader = BufReader::new(file); + use crate::conversion::get_input_data_for_eip_4844; - for line in reader.lines() { - let line = line.expect("can't read line"); - let trimmed = line.trim(); - if !trimmed.is_empty() { - let v_0 = U256::from_str(trimmed).expect("Unable to convert line").to_be_bytes_vec(); - let v_1 = v_0.as_slice(); - let v_2 = to_padded_hex(v_1); - // let v_3 = v_2.replace("0x", ""); - let v_4 = hex_string_to_u8_vec(&v_2).expect("unable to convert"); - let v_5: [u8; 32] = v_4.try_into().expect("Vector length must be 32"); - program_output.push(v_5) - } - } + let program_output = get_program_output(fork_block_no); + let blob_data_vec = get_blob_data(fork_block_no); let x_0_value_bytes32 = Bytes32::from(program_output[8]); - // Blob Data - let blob_data_file_path = format!("{}{}{}{}", current_path.clone(), "/src/test_data/blob_data/", block_no, ".txt"); - println!("{}", blob_data_file_path); - let blob_data = fs::read_to_string(blob_data_file_path).expect("Failed to read the blob data txt file"); - let blob_data_vec = vec![hex_string_to_u8_vec(&blob_data).unwrap()]; - let kzg_proof = EthereumSettlementClient::build_proof(blob_data_vec, x_0_value_bytes32) .expect("Unable to build KZG proof for given params.") .to_owned(); @@ -245,23 +236,6 @@ async fn creating_input_data_works(#[case] block_no: u64) { assert_eq!(input_bytes, expected); } -// SOLIDITY FUNCTIONS NEEDED -sol!( - #[allow(missing_docs)] - #[sol(rpc)] - STARKNET_CORE_CONTRACT, - "src/test_data/ABI/starknet_core_contract.json" -); - -sol! { - #[allow(missing_docs)] - #[sol(rpc, bytecode="6080604052348015600e575f80fd5b506101c18061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c8063b72d42a11461002d575b5f80fd5b6100476004803603810190610042919061010d565b610049565b005b50505050565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f8083601f84011261007857610077610057565b5b8235905067ffffffffffffffff8111156100955761009461005b565b5b6020830191508360208202830111156100b1576100b061005f565b5b9250929050565b5f8083601f8401126100cd576100cc610057565b5b8235905067ffffffffffffffff8111156100ea576100e961005b565b5b6020830191508360018202830111156101065761010561005f565b5b9250929050565b5f805f80604085870312156101255761012461004f565b5b5f85013567ffffffffffffffff81111561014257610141610053565b5b61014e87828801610063565b9450945050602085013567ffffffffffffffff81111561017157610170610053565b5b61017d878288016100b8565b92509250509295919450925056fea2646970667358221220fa7488d5a2a9e6c21e6f46145a831b0f04fdebab83868dc2b996c17f8cba4d8064736f6c634300081a0033")] - contract DummyCoreContract { - function updateStateKzgDA(uint256[] calldata programOutput, bytes calldata kzgProof) external { - } - } -} - // UTILITY FUNCTIONS NEEDED fn get_program_output(block_no: u64) -> Vec<[u8; 32]> { From bbecc38b2fcd197b8c634be40235b6ffe41a9dbb Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Fri, 16 Aug 2024 12:46:23 +0530 Subject: [PATCH 24/41] chore: fixing test cases for eth settlement client --- .../ethereum/src/conversion.rs | 2 +- .../ethereum/src/tests/mod.rs | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/crates/settlement-clients/ethereum/src/conversion.rs b/crates/settlement-clients/ethereum/src/conversion.rs index 79b1234b..509a8493 100644 --- a/crates/settlement-clients/ethereum/src/conversion.rs +++ b/crates/settlement-clients/ethereum/src/conversion.rs @@ -150,7 +150,7 @@ mod tests { #[should_panic(expected = "could not convert &[u8] to U256")] #[case::over(&[0xFF; 33])] fn slice_u8_to_u256_panics(#[case] slice: &[u8]) { - let _ = slice_u8_to_u256(slice); + let _ = slice_u8_to_u256(slice).unwrap(); } #[rstest] diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index fd654acd..a3910f69 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -1,3 +1,4 @@ +use alloy::node_bindings::AnvilInstance; use alloy::primitives::U256; use alloy::providers::{ext::AnvilApi, ProviderBuilder}; use alloy::{node_bindings::Anvil, sol}; @@ -74,6 +75,7 @@ sol! { } pub struct TestSetup { + pub anvil : AnvilInstance, pub ethereum_settlement_client: EthereumSettlementClient, pub provider: alloy::providers::RootProvider>, } @@ -97,7 +99,7 @@ fn setup_ethereum_test(block_no: u64) -> TestSetup { let settings_provider: DefaultSettingsProvider = DefaultSettingsProvider {}; let ethereum_settlement_client = EthereumSettlementClient::with_test_settings(&settings_provider, provider.clone()); - TestSetup { ethereum_settlement_client, provider } + TestSetup { anvil, ethereum_settlement_client, provider } } #[rstest] @@ -106,9 +108,9 @@ fn setup_ethereum_test(block_no: u64) -> TestSetup { /// tests if the method is able to do a transaction with same function selector on a dummy contract. async fn update_state_blob_with_dummy_contract_works(#[case] fork_block_no: u64) { env::set_var("TEST_IMPERSONATE_OPERATOR", "0"); + let TestSetup { anvil , ethereum_settlement_client, provider } = setup_ethereum_test(fork_block_no); - let TestSetup { ethereum_settlement_client, provider } = setup_ethereum_test(fork_block_no); - + println!("{:?}", anvil); // Deploying a dummy contract let contract = DummyCoreContract::deploy(&provider).await.expect("Unable to deploy address"); assert_eq!( @@ -161,7 +163,9 @@ async fn update_state_blob_with_dummy_contract_works(#[case] fork_block_no: u64) #[case::basic(20468828)] /// tests if the method is able to impersonate the`Starknet Operator` and do an `update_state` transaction. async fn update_state_blob_with_impersonation_works(#[case] fork_block_no: u64) { - let TestSetup { ethereum_settlement_client, provider } = setup_ethereum_test(fork_block_no); + let TestSetup { anvil, ethereum_settlement_client, provider } = setup_ethereum_test(fork_block_no); + + println!("{:?}", anvil); provider.anvil_impersonate_account(*STARKNET_OPERATOR_ADDRESS).await.expect("Unable to impersonate account."); @@ -206,9 +210,11 @@ async fn update_state_blob_with_impersonation_works(#[case] fork_block_no: u64) #[rstest] #[tokio::test] -#[case::typical(20468828, 666039)] +#[case::typical(20468828, 668656)] async fn get_last_settled_block_typical_works(#[case] fork_block_no: u64, #[case] expected: u64) { - let TestSetup { ethereum_settlement_client, provider: _ } = setup_ethereum_test(fork_block_no); + env::set_var("DEFAULT_SETTLEMENT_CLIENT_RPC", "https://eth.llamarpc.com"); + + let TestSetup { anvil : _, ethereum_settlement_client, provider: _ } = setup_ethereum_test(fork_block_no); let result = ethereum_settlement_client.get_last_settled_block().await.expect("Could not get last settled block."); assert_eq!(expected, result); From 01294a077c1671178cd679bb4c60a00580a9d796 Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Fri, 16 Aug 2024 13:43:07 +0530 Subject: [PATCH 25/41] update: tests fix --- crates/orchestrator/src/tests/jobs/state_update_job/mod.rs | 7 +++---- crates/settlement-clients/ethereum/src/config.rs | 5 ++--- crates/settlement-clients/ethereum/src/tests/mod.rs | 7 +++---- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/crates/orchestrator/src/tests/jobs/state_update_job/mod.rs b/crates/orchestrator/src/tests/jobs/state_update_job/mod.rs index 96734860..5380e06b 100644 --- a/crates/orchestrator/src/tests/jobs/state_update_job/mod.rs +++ b/crates/orchestrator/src/tests/jobs/state_update_job/mod.rs @@ -4,7 +4,7 @@ use std::path::PathBuf; use bytes::Bytes; use httpmock::prelude::*; -use mockall::predicate::eq; +use mockall::predicate::{always, eq}; use rstest::*; use settlement_client_interface::MockSettlementClient; @@ -86,7 +86,7 @@ async fn test_process_job_works( let blob_data = fetch_blob_data_for_block(block.to_u64().unwrap()).await.unwrap(); settlement_client .expect_update_state_with_blobs() - .with(eq(vec![]), eq(blob_data), eq(nonce)) + .with(eq(vec![]), eq(blob_data), always()) .times(1) .returning(|_, _, _| Ok("0xbeef".to_string())); } @@ -184,13 +184,12 @@ async fn process_job_works() { storage_client.expect_get_data().with(eq(x_0_key)).returning(move |_| Ok(Bytes::from(x_0.clone()))); // let nonce = settlement_client.get_nonce().await.expect("Unable to fetch nonce for settlement client."); - let nonce: u64 = 1; settlement_client.expect_get_nonce().returning(|| Ok(1)); settlement_client .expect_update_state_with_blobs() // TODO: vec![] is program_output - .with(eq(program_output), eq(state_diff), eq(nonce)) + .with(eq(program_output), eq(state_diff), always()) .returning(|_, _, _| Ok(String::from("0x5d17fac98d9454030426606019364f6e68d915b91f6210ef1e2628cd6987442"))); } diff --git a/crates/settlement-clients/ethereum/src/config.rs b/crates/settlement-clients/ethereum/src/config.rs index fc860be3..2e6d91ca 100644 --- a/crates/settlement-clients/ethereum/src/config.rs +++ b/crates/settlement-clients/ethereum/src/config.rs @@ -5,7 +5,6 @@ use settlement_client_interface::SettlementConfig; use url::Url; use utils::env_utils::get_env_var_or_panic; -pub const ENV_ETHEREUM_RPC_URL: &str = "ETHEREUM_RPC_URL"; pub const ENV_CORE_CONTRACT_ADDRESS: &str = "STARKNET_SOLIDITY_CORE_CONTRACT_ADDRESS"; pub const DEFAULT_SETTLEMENT_CLIENT_RPC: &str = "DEFAULT_SETTLEMENT_CLIENT_RPC"; pub const DEFAULT_L1_CORE_CONTRACT_ADDRESS: &str = "DEFAULT_L1_CORE_CONTRACT_ADDRESS"; @@ -18,8 +17,8 @@ pub struct EthereumSettlementConfig { impl SettlementConfig for EthereumSettlementConfig { fn new_from_env() -> Self { - let rpc_url = get_env_var_or_panic(ENV_ETHEREUM_RPC_URL); - let rpc_url = Url::from_str(&rpc_url).unwrap_or_else(|_| panic!("Failed to parse {}", ENV_ETHEREUM_RPC_URL)); + let rpc_url = get_env_var_or_panic(DEFAULT_SETTLEMENT_CLIENT_RPC); + let rpc_url = Url::from_str(&rpc_url).unwrap_or_else(|_| panic!("Failed to parse {}", DEFAULT_SETTLEMENT_CLIENT_RPC)); let core_contract_address = get_env_var_or_panic(ENV_CORE_CONTRACT_ADDRESS); Self { rpc_url, core_contract_address } } diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index a3910f69..a0fbb6e4 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -210,14 +210,13 @@ async fn update_state_blob_with_impersonation_works(#[case] fork_block_no: u64) #[rstest] #[tokio::test] -#[case::typical(20468828, 668656)] -async fn get_last_settled_block_typical_works(#[case] fork_block_no: u64, #[case] expected: u64) { +#[case::typical(20468828)] +async fn get_last_settled_block_typical_works(#[case] fork_block_no: u64) { env::set_var("DEFAULT_SETTLEMENT_CLIENT_RPC", "https://eth.llamarpc.com"); let TestSetup { anvil : _, ethereum_settlement_client, provider: _ } = setup_ethereum_test(fork_block_no); - let result = ethereum_settlement_client.get_last_settled_block().await.expect("Could not get last settled block."); - assert_eq!(expected, result); + let _ = ethereum_settlement_client.get_last_settled_block().await.expect("Could not get last settled block."); } #[rstest] From 1178e226aedb302491cf3804496bee32eccab5cc Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Fri, 16 Aug 2024 13:44:25 +0530 Subject: [PATCH 26/41] chore: lint fix --- crates/settlement-clients/ethereum/src/config.rs | 3 ++- crates/settlement-clients/ethereum/src/tests/mod.rs | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/settlement-clients/ethereum/src/config.rs b/crates/settlement-clients/ethereum/src/config.rs index 2e6d91ca..294c2a54 100644 --- a/crates/settlement-clients/ethereum/src/config.rs +++ b/crates/settlement-clients/ethereum/src/config.rs @@ -18,7 +18,8 @@ pub struct EthereumSettlementConfig { impl SettlementConfig for EthereumSettlementConfig { fn new_from_env() -> Self { let rpc_url = get_env_var_or_panic(DEFAULT_SETTLEMENT_CLIENT_RPC); - let rpc_url = Url::from_str(&rpc_url).unwrap_or_else(|_| panic!("Failed to parse {}", DEFAULT_SETTLEMENT_CLIENT_RPC)); + let rpc_url = + Url::from_str(&rpc_url).unwrap_or_else(|_| panic!("Failed to parse {}", DEFAULT_SETTLEMENT_CLIENT_RPC)); let core_contract_address = get_env_var_or_panic(ENV_CORE_CONTRACT_ADDRESS); Self { rpc_url, core_contract_address } } diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index a0fbb6e4..673201a1 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -75,7 +75,7 @@ sol! { } pub struct TestSetup { - pub anvil : AnvilInstance, + pub anvil: AnvilInstance, pub ethereum_settlement_client: EthereumSettlementClient, pub provider: alloy::providers::RootProvider>, } @@ -108,7 +108,7 @@ fn setup_ethereum_test(block_no: u64) -> TestSetup { /// tests if the method is able to do a transaction with same function selector on a dummy contract. async fn update_state_blob_with_dummy_contract_works(#[case] fork_block_no: u64) { env::set_var("TEST_IMPERSONATE_OPERATOR", "0"); - let TestSetup { anvil , ethereum_settlement_client, provider } = setup_ethereum_test(fork_block_no); + let TestSetup { anvil, ethereum_settlement_client, provider } = setup_ethereum_test(fork_block_no); println!("{:?}", anvil); // Deploying a dummy contract @@ -214,7 +214,7 @@ async fn update_state_blob_with_impersonation_works(#[case] fork_block_no: u64) async fn get_last_settled_block_typical_works(#[case] fork_block_no: u64) { env::set_var("DEFAULT_SETTLEMENT_CLIENT_RPC", "https://eth.llamarpc.com"); - let TestSetup { anvil : _, ethereum_settlement_client, provider: _ } = setup_ethereum_test(fork_block_no); + let TestSetup { anvil: _, ethereum_settlement_client, provider: _ } = setup_ethereum_test(fork_block_no); let _ = ethereum_settlement_client.get_last_settled_block().await.expect("Could not get last settled block."); } From 60fac37546384fc5b7976568f12cd9651002fa8f Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Fri, 16 Aug 2024 13:50:44 +0530 Subject: [PATCH 27/41] chore: lint fix --- crates/orchestrator/src/jobs/state_update_job/mod.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/crates/orchestrator/src/jobs/state_update_job/mod.rs b/crates/orchestrator/src/jobs/state_update_job/mod.rs index 22be1c97..48e4aeb9 100644 --- a/crates/orchestrator/src/jobs/state_update_job/mod.rs +++ b/crates/orchestrator/src/jobs/state_update_job/mod.rs @@ -109,7 +109,7 @@ impl Job for StateUpdateJob { block_numbers = block_numbers.into_iter().filter(|&block| block >= last_failed_block).collect::>(); } - let mut nonce = config.settlement_client().get_nonce().await?; + let mut nonce = config.settlement_client().get_nonce().await.map_err(|e| JobError::Other(OtherError(e)))?; let mut sent_tx_hashes: Vec = Vec::with_capacity(block_numbers.len()); for block_no in block_numbers.iter() { @@ -273,10 +273,8 @@ impl StateUpdateJob { let last_tx_hash_executed = if snos.use_kzg_da == Felt252::ZERO { unimplemented!("update_state_for_block not implemented as of now for calldata DA.") } else if snos.use_kzg_da == Felt252::ONE { - let blob_data = fetch_blob_data_for_block(block_no) - .await - .map_err(|e| JobError::Other(OtherError(e)))?; - + let blob_data = fetch_blob_data_for_block(block_no).await.map_err(|e| JobError::Other(OtherError(e)))?; + // Fetching nonce before the transaction is run // Sending update_state transaction from the settlement client settlement_client From 3a125adab0209f7fe6af3eb337e3dfa9fb2a6fdc Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Fri, 16 Aug 2024 13:54:00 +0530 Subject: [PATCH 28/41] update: path fix --- crates/settlement-clients/ethereum/src/tests/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index 673201a1..6a1194b5 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -62,7 +62,7 @@ sol!( #[allow(missing_docs)] #[sol(rpc)] STARKNET_CORE_CONTRACT, - "src/test_data/abi/starknet_core_contract.json" + "src/test_data/ABI/starknet_core_contract.json" ); sol! { From e2a0bed097ada261e29d27b328458d48fa9a4fee Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Fri, 16 Aug 2024 14:37:35 +0530 Subject: [PATCH 29/41] update: testing anvil install on gh --- .github/workflows/coverage.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 92598ab7..30713799 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -35,6 +35,20 @@ jobs: - uses: taiki-e/install-action@cargo-llvm-cov - uses: taiki-e/install-action@nextest + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + + - name: Check Anvil Installation + run: | + if command -v anvil &> /dev/null + then + echo "Anvil is installed. Version information:" + anvil --version + else + echo "Anvil is not installed or not in PATH" + exit 1 + fi + - name: Clean workspace run: | cargo llvm-cov clean --workspace From 92aa1451f8bf1bcda1529b67a1708579b63ec288 Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Fri, 16 Aug 2024 15:14:21 +0530 Subject: [PATCH 30/41] update: fixing path --- crates/settlement-clients/ethereum/src/lib.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index 13a1107b..374ef8f2 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -208,7 +208,13 @@ impl SettlementClient for EthereumSettlementClient { #[cfg(test)] use alloy::network::TransactionBuilder; - let trusted_setup = KzgSettings::load_trusted_setup_file(Path::new("/Users/dexterhv/Work/Karnot/madara-alliance/madara-orchestrator/crates/settlement-clients/ethereum/src/trusted_setup.txt"))?; + let trusted_setup_path: String = CURRENT_PATH + .join("src") + .join("trusted_setup.txt") + .to_str() + .expect("Path contains invalid Unicode") + .to_string(); + let trusted_setup = KzgSettings::load_trusted_setup_file(Path::new(trusted_setup_path.as_str()))?; let (sidecar_blobs, sidecar_commitments, sidecar_proofs) = prepare_sidecar(&state_diff, &trusted_setup).await?; let sidecar = BlobTransactionSidecar::new(sidecar_blobs, sidecar_commitments, sidecar_proofs); From 6c03f7b9a7f7a16d493dc2b32aa1d622d66f1ce7 Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Fri, 16 Aug 2024 15:58:02 +0530 Subject: [PATCH 31/41] update: added Blast rpc for eth --- .github/workflows/coverage.yml | 2 ++ crates/settlement-clients/ethereum/src/tests/mod.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 30713799..6aaa8442 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -54,6 +54,8 @@ jobs: cargo llvm-cov clean --workspace - name: Run llvm-cov + env: + ETHEREUM_BLAST_RPC_URL: ${{ secrets.ETHEREUM_BLAST_RPC_URL }} run: | cargo llvm-cov nextest --release --lcov --output-path lcov.info --test-threads=1 diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index 6a1194b5..a082d184 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -48,7 +48,7 @@ lazy_static! { .expect("Path contains invalid Unicode") .to_string(); static ref PORT: u16 = 3000_u16; - static ref ETH_RPC: String = "https://eth.llamarpc.com".to_string(); + static ref ETH_RPC: String = get_env_var_or_panic("ETHEREUM_BLAST_RPC_URL"); static ref SHOULD_IMPERSONATE_ACCOUNT: bool = get_env_var_or_panic("TEST_IMPERSONATE_OPERATOR") == *"1"; static ref TEST_DUMMY_CONTRACT_ADDRESS: String = get_env_var_or_panic("TEST_DUMMY_CONTRACT_ADDRESS"); static ref STARKNET_OPERATOR_ADDRESS: Address = From a3c0863e5cc46278823e4c748bb99c2854fa3b9d Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Sat, 17 Aug 2024 03:18:30 +0530 Subject: [PATCH 32/41] update: Coverage CI fixes --- .github/workflows/coverage.yml | 9 +++++++-- crates/settlement-clients/ethereum/src/tests/mod.rs | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 6aaa8442..b5e67475 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -2,8 +2,13 @@ name: Task - Rust Tests & Coverage on: - workflow_dispatch: - workflow_call: + pull_request_target: + branches: + - main + types: [opened, synchronize, reopened] + push: + branches-ignore: + - main jobs: coverage: diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index a082d184..274967c0 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -212,7 +212,8 @@ async fn update_state_blob_with_impersonation_works(#[case] fork_block_no: u64) #[tokio::test] #[case::typical(20468828)] async fn get_last_settled_block_typical_works(#[case] fork_block_no: u64) { - env::set_var("DEFAULT_SETTLEMENT_CLIENT_RPC", "https://eth.llamarpc.com"); + dotenvy::from_filename(&*ENV_FILE_PATH).expect("Could not load .env.test file."); + env::set_var("DEFAULT_SETTLEMENT_CLIENT_RPC", &*ETH_RPC); let TestSetup { anvil: _, ethereum_settlement_client, provider: _ } = setup_ethereum_test(fork_block_no); From d0ddfdeda4b6e7684b808bfef0bd7bb571dead02 Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Sat, 17 Aug 2024 05:17:14 +0530 Subject: [PATCH 33/41] update: correct Pr checks --- .github/workflows/coverage.yml | 2 ++ .github/workflows/linters-cargo.yml | 1 + .github/workflows/linters.yml | 1 + .github/workflows/pull-request.yml | 3 +-- .github/workflows/rust-build.yml | 1 + 5 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index b5e67475..e7aaac05 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -9,6 +9,8 @@ on: push: branches-ignore: - main + workflow_call: + workflow_dispatch: jobs: coverage: diff --git a/.github/workflows/linters-cargo.yml b/.github/workflows/linters-cargo.yml index 96172230..d3e5ff90 100644 --- a/.github/workflows/linters-cargo.yml +++ b/.github/workflows/linters-cargo.yml @@ -4,6 +4,7 @@ name: Task - Linters Cargo on: workflow_dispatch: workflow_call: + push: jobs: cargo-lint: diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index 42f8c8de..901b7f1f 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -4,6 +4,7 @@ name: Task - Linters on: workflow_dispatch: workflow_call: + push: jobs: prettier: diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 84d6c35c..2863c998 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -2,8 +2,7 @@ name: Workflow - Pull Request on: - workflow_dispatch: - pull_request: + pull_request_target: branches: [main] push: branches: [main] diff --git a/.github/workflows/rust-build.yml b/.github/workflows/rust-build.yml index f7cb3e08..1406606b 100644 --- a/.github/workflows/rust-build.yml +++ b/.github/workflows/rust-build.yml @@ -4,6 +4,7 @@ name: Task - Build Rust on: workflow_dispatch: workflow_call: + push: jobs: rust_build: From 4de6e8bc21a63d4173c4fa9a1f9d4b0c4fb32c8f Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Sat, 17 Aug 2024 10:45:20 +0530 Subject: [PATCH 34/41] update: PR reviews fixes --- .../ethereum/src/conversion.rs | 25 ++++++------------- crates/settlement-clients/ethereum/src/lib.rs | 2 +- .../starknet_core_contract.json | 0 .../ethereum/src/tests/mod.rs | 2 +- 4 files changed, 10 insertions(+), 19 deletions(-) rename crates/settlement-clients/ethereum/src/test_data/{ABI => contract_abi}/starknet_core_contract.json (100%) diff --git a/crates/settlement-clients/ethereum/src/conversion.rs b/crates/settlement-clients/ethereum/src/conversion.rs index 509a8493..04543c27 100644 --- a/crates/settlement-clients/ethereum/src/conversion.rs +++ b/crates/settlement-clients/ethereum/src/conversion.rs @@ -142,15 +142,10 @@ mod tests { #[case::maximum(&[0xFF; 32], U256::MAX)] #[case::short(&[0xFF; 16], U256::from_be_slice(&[0xFF; 16]))] #[case::empty(&[], U256::ZERO)] - fn slice_u8_to_u256_works(#[case] slice: &[u8], #[case] expected: U256) { - assert_eq!(slice_u8_to_u256(slice).expect("slice_u8_to_u256 failed"), expected) - } - - #[rstest] #[should_panic(expected = "could not convert &[u8] to U256")] - #[case::over(&[0xFF; 33])] - fn slice_u8_to_u256_panics(#[case] slice: &[u8]) { - let _ = slice_u8_to_u256(slice).unwrap(); + #[case::over(&[0xFF; 33],U256::from_be_slice(&[0xFF;32]))] + fn slice_u8_to_u256_all_working_and_failing_cases(#[case] slice: &[u8], #[case] expected: U256) { + assert_eq!(slice_u8_to_u256(slice).expect("slice_u8_to_u256 failed"), expected) } #[rstest] @@ -198,17 +193,11 @@ mod tests { #[case::empty(&[], "0".repeat(64))] #[case::typical(&[0xFF,0xFF,0xFF,0xFF], format!("{}{}", "ff".repeat(4), "0".repeat(56)))] #[case::big(&[0xFF; 32], format!("{}", "ff".repeat(32)))] - fn to_hex_string_works(#[case] slice: &[u8], #[case] expected: String) { - let result = to_padded_hex(slice); - assert_eq!(result, expected); - assert!(expected.len() == 64); - } - - #[rstest] #[should_panic(expected = "Slice length must not exceed 32")] #[case::exceeding(&[0xFF; 40], format!("{}", "ff".repeat(32)))] - fn to_hex_string_panics(#[case] slice: &[u8], #[case] expected: String) { - let _ = to_padded_hex(slice); + fn to_hex_string_working_and_failing_cases(#[case] slice: &[u8], #[case] expected: String) { + let result = to_padded_hex(slice); + assert_eq!(result, expected); assert!(expected.len() == 64); } @@ -250,6 +239,8 @@ mod tests { // block_no here are Ethereum(mainnet) blocks, we are creating sidecar and validating // the function by matching pre-existing commitments against computed. + // https://etherscan.io/tx/0x4e012b119391bdc192653bfee9758c432ea6f35ff23f8af60a7dca4664383dfc + // https://etherscan.io/tx/0x96470b890833c5ae51622bd6efca98d8eec3b4a66402c34be3cdcacf006eb9a0 #[rstest] #[case("20462788")] #[case("20462818")] diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index 374ef8f2..8978b6f7 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -42,7 +42,7 @@ pub mod conversion; // IMPORTANT to understand #[cfg(test)], #[cfg(not(test))] and SHOULD_IMPERSONATE_ACCOUNT // Two tests : `update_state_blob_with_dummy_contract_works` & `update_state_blob_with_impersonation_works` use a env var `TEST_IMPERSONATE_OPERATOR` to inform the function `update_state_with_blobs` about the kind of testing, // `TEST_IMPERSONATE_OPERATOR` can have any of "0" or "1" value : -// - if "0" then : Testing against Dummy Contract. +// - if "0" then : Testing via default Anvil address. // - if "1" then : Testing via impersonating `Starknet Operator Address`. // Note : changing between "0" and "1" is handled automatically by each test function, `no` manual change in `env.test` is needed. diff --git a/crates/settlement-clients/ethereum/src/test_data/ABI/starknet_core_contract.json b/crates/settlement-clients/ethereum/src/test_data/contract_abi/starknet_core_contract.json similarity index 100% rename from crates/settlement-clients/ethereum/src/test_data/ABI/starknet_core_contract.json rename to crates/settlement-clients/ethereum/src/test_data/contract_abi/starknet_core_contract.json diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index 274967c0..68c081b3 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -62,7 +62,7 @@ sol!( #[allow(missing_docs)] #[sol(rpc)] STARKNET_CORE_CONTRACT, - "src/test_data/ABI/starknet_core_contract.json" + "src/test_data/contract_abi/starknet_core_contract.json" ); sol! { From ebe967e1f66311e1454a59d9029076d789bc0079 Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Sat, 17 Aug 2024 11:10:18 +0530 Subject: [PATCH 35/41] update: PR reviews fixes --- .env.test | 2 +- crates/settlement-clients/ethereum/src/lib.rs | 6 +++--- crates/settlement-clients/ethereum/src/tests/mod.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.env.test b/.env.test index 4fdaae38..d3bd9c0c 100644 --- a/.env.test +++ b/.env.test @@ -30,5 +30,5 @@ MONGODB_CONNECTION_STRING="mongodb://localhost:27017" # Ethereum Settlement DEFAULT_SETTLEMENT_CLIENT_RPC="http://localhost:3000" DEFAULT_L1_CORE_CONTRACT_ADDRESS="0xc662c410C0ECf747543f5bA90660f6ABeBD9C8c4" -TEST_IMPERSONATE_OPERATOR="1" +SHOULD_IMPERSONATE_ACCOUNT="true" TEST_DUMMY_CONTRACT_ADDRESS="0xE5b6F5e695BA6E4aeD92B68c4CC8Df1160D69A81" \ No newline at end of file diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index 8978b6f7..03c05d8e 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -40,15 +40,15 @@ pub mod config; pub mod conversion; // IMPORTANT to understand #[cfg(test)], #[cfg(not(test))] and SHOULD_IMPERSONATE_ACCOUNT -// Two tests : `update_state_blob_with_dummy_contract_works` & `update_state_blob_with_impersonation_works` use a env var `TEST_IMPERSONATE_OPERATOR` to inform the function `update_state_with_blobs` about the kind of testing, -// `TEST_IMPERSONATE_OPERATOR` can have any of "0" or "1" value : +// Two tests : `update_state_blob_with_dummy_contract_works` & `update_state_blob_with_impersonation_works` use a env var `SHOULD_IMPERSONATE_ACCOUNT` to inform the function `update_state_with_blobs` about the kind of testing, +// `SHOULD_IMPERSONATE_ACCOUNT` can have any of "0" or "1" value : // - if "0" then : Testing via default Anvil address. // - if "1" then : Testing via impersonating `Starknet Operator Address`. // Note : changing between "0" and "1" is handled automatically by each test function, `no` manual change in `env.test` is needed. #[cfg(test)] lazy_static! { - static ref SHOULD_IMPERSONATE_ACCOUNT: bool = get_env_var_or_panic("TEST_IMPERSONATE_OPERATOR") == *"1"; + static ref SHOULD_IMPERSONATE_ACCOUNT: bool = get_env_var_or_panic("SHOULD_IMPERSONATE_ACCOUNT") == *"true"; static ref TEST_DUMMY_CONTRACT_ADDRESS: String = get_env_var_or_panic("TEST_DUMMY_CONTRACT_ADDRESS"); static ref ADDRESS_TO_IMPERSONATE: Address = Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("Unable to parse address"); diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index 68c081b3..b50d9b4c 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -49,7 +49,7 @@ lazy_static! { .to_string(); static ref PORT: u16 = 3000_u16; static ref ETH_RPC: String = get_env_var_or_panic("ETHEREUM_BLAST_RPC_URL"); - static ref SHOULD_IMPERSONATE_ACCOUNT: bool = get_env_var_or_panic("TEST_IMPERSONATE_OPERATOR") == *"1"; + static ref SHOULD_IMPERSONATE_ACCOUNT: bool = get_env_var_or_panic("SHOULD_IMPERSONATE_ACCOUNT") == *"true"; static ref TEST_DUMMY_CONTRACT_ADDRESS: String = get_env_var_or_panic("TEST_DUMMY_CONTRACT_ADDRESS"); static ref STARKNET_OPERATOR_ADDRESS: Address = Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("Could not impersonate account."); @@ -107,7 +107,7 @@ fn setup_ethereum_test(block_no: u64) -> TestSetup { #[case::basic(20468828)] /// tests if the method is able to do a transaction with same function selector on a dummy contract. async fn update_state_blob_with_dummy_contract_works(#[case] fork_block_no: u64) { - env::set_var("TEST_IMPERSONATE_OPERATOR", "0"); + env::set_var("SHOULD_IMPERSONATE_ACCOUNT", "false"); let TestSetup { anvil, ethereum_settlement_client, provider } = setup_ethereum_test(fork_block_no); println!("{:?}", anvil); From ef35e52617f34ac2b431043356c7dbd324b3fcb0 Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Sat, 17 Aug 2024 11:54:03 +0530 Subject: [PATCH 36/41] update: adding rationale for update_state_blob_with_impersonation_works & update_state_blob_with_dummy_contract_works --- crates/settlement-clients/ethereum/src/tests/mod.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index b50d9b4c..f0a42086 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -105,7 +105,11 @@ fn setup_ethereum_test(block_no: u64) -> TestSetup { #[rstest] #[tokio::test] #[case::basic(20468828)] -/// tests if the method is able to do a transaction with same function selector on a dummy contract. +/// Tests if the method is able to do a transaction with same function selector on a dummy contract. +/// If we impersonate starknet operator then we loose out on testing for validity of signature in the transaction. +/// Starknet core contract has a modifier `onlyOperator` that restricts anyone but the operator to send transaction to `updateStateKzgDa` method +/// And hence to test the signature and transaction via a dummy contract that has same function selector as `updateStateKzgDa`. +/// and anvil is for testing on fork Eth. async fn update_state_blob_with_dummy_contract_works(#[case] fork_block_no: u64) { env::set_var("SHOULD_IMPERSONATE_ACCOUNT", "false"); let TestSetup { anvil, ethereum_settlement_client, provider } = setup_ethereum_test(fork_block_no); @@ -162,6 +166,8 @@ async fn update_state_blob_with_dummy_contract_works(#[case] fork_block_no: u64) #[tokio::test] #[case::basic(20468828)] /// tests if the method is able to impersonate the`Starknet Operator` and do an `update_state` transaction. +/// We impersonate the Starknet Operator to send a transaction to the Core contract +/// Here signature checks are bypassed and anvil is for testing on fork Eth. async fn update_state_blob_with_impersonation_works(#[case] fork_block_no: u64) { let TestSetup { anvil, ethereum_settlement_client, provider } = setup_ethereum_test(fork_block_no); From a8f4bb16a3fc5b2c911c21c3a03e8bdbace8eaf7 Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Sun, 18 Aug 2024 14:18:57 +0530 Subject: [PATCH 37/41] update: cleaner test integration on update_state_with_blobs --- crates/settlement-clients/ethereum/src/lib.rs | 37 ++++++++++--------- .../ethereum/src/tests/mod.rs | 7 +++- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index 03c05d8e..431fbfea 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -46,15 +46,6 @@ pub mod conversion; // - if "1" then : Testing via impersonating `Starknet Operator Address`. // Note : changing between "0" and "1" is handled automatically by each test function, `no` manual change in `env.test` is needed. -#[cfg(test)] -lazy_static! { - static ref SHOULD_IMPERSONATE_ACCOUNT: bool = get_env_var_or_panic("SHOULD_IMPERSONATE_ACCOUNT") == *"true"; - static ref TEST_DUMMY_CONTRACT_ADDRESS: String = get_env_var_or_panic("TEST_DUMMY_CONTRACT_ADDRESS"); - static ref ADDRESS_TO_IMPERSONATE: Address = - Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("Unable to parse address"); - static ref TEST_NONCE: u64 = 666068; -} - #[cfg(test)] mod tests; pub mod types; @@ -112,6 +103,7 @@ impl EthereumSettlementClient { #[cfg(test)] pub fn with_test_settings(settings: &impl SettingsProvider, provider: RootProvider>) -> Self { + use tests::{SHOULD_IMPERSONATE_ACCOUNT, TEST_DUMMY_CONTRACT_ADDRESS}; let settlement_cfg: EthereumSettlementConfig = settings.get_settings(SETTLEMENT_SETTINGS_NAME).unwrap(); let private_key = get_env_var_or_panic(ENV_PRIVATE_KEY); @@ -205,8 +197,6 @@ impl SettlementClient for EthereumSettlementClient { nonce: u64, ) -> Result { //TODO: better file management - #[cfg(test)] - use alloy::network::TransactionBuilder; let trusted_setup_path: String = CURRENT_PATH .join("src") @@ -262,12 +252,7 @@ impl SettlementClient for EthereumSettlementClient { let txn_request: TransactionRequest = tx_envelope.into(); #[cfg(test)] - let mut txn_request: TransactionRequest = tx_envelope.into(); - #[cfg(test)] - if *SHOULD_IMPERSONATE_ACCOUNT { - txn_request.set_nonce(*TEST_NONCE); - txn_request = txn_request.with_from(*ADDRESS_TO_IMPERSONATE); - } + let txn_request = test_config::configure_transaction(tx_envelope); let pending_transaction = self.provider.send_transaction(txn_request).await?; return Ok(pending_transaction.tx_hash().to_string()); @@ -307,3 +292,21 @@ impl SettlementClient for EthereumSettlementClient { Ok(nonce) } } + +#[cfg(test)] +mod test_config { + use super::*; + use alloy::network::TransactionBuilder; + use tests::{ADDRESS_TO_IMPERSONATE, SHOULD_IMPERSONATE_ACCOUNT, TEST_NONCE}; + + pub fn configure_transaction(tx_envelope: TxEnvelope) -> TransactionRequest { + let mut txn_request: TransactionRequest = tx_envelope.into(); + + if *SHOULD_IMPERSONATE_ACCOUNT { + txn_request.set_nonce(*TEST_NONCE); + txn_request = txn_request.with_from(*ADDRESS_TO_IMPERSONATE); + } + + txn_request + } +} diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index f0a42086..d323a30f 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -49,12 +49,15 @@ lazy_static! { .to_string(); static ref PORT: u16 = 3000_u16; static ref ETH_RPC: String = get_env_var_or_panic("ETHEREUM_BLAST_RPC_URL"); - static ref SHOULD_IMPERSONATE_ACCOUNT: bool = get_env_var_or_panic("SHOULD_IMPERSONATE_ACCOUNT") == *"true"; - static ref TEST_DUMMY_CONTRACT_ADDRESS: String = get_env_var_or_panic("TEST_DUMMY_CONTRACT_ADDRESS"); static ref STARKNET_OPERATOR_ADDRESS: Address = Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("Could not impersonate account."); static ref STARKNET_CORE_CONTRACT_ADDRESS: Address = Address::from_str("0xc662c410c0ecf747543f5ba90660f6abebd9c8c4").expect("Could not impersonate account."); + pub static ref ADDRESS_TO_IMPERSONATE: Address = + Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("Unable to parse address"); + pub static ref TEST_DUMMY_CONTRACT_ADDRESS: String = get_env_var_or_panic("TEST_DUMMY_CONTRACT_ADDRESS"); + pub static ref SHOULD_IMPERSONATE_ACCOUNT: bool = get_env_var_or_panic("SHOULD_IMPERSONATE_ACCOUNT") == *"true"; + pub static ref TEST_NONCE: u64 = 666068; } // SOLIDITY FUNCTIONS NEEDED From 30e85844516d23438c236e35a940718777fe7d3f Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Sun, 18 Aug 2024 19:10:12 +0530 Subject: [PATCH 38/41] update: removing EthProvider --- crates/settlement-clients/ethereum/src/lib.rs | 18 +++++++----------- .../ethereum/src/tests/mod.rs | 4 +++- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index 431fbfea..f310111c 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -50,12 +50,8 @@ pub mod conversion; mod tests; pub mod types; -#[cfg(test)] use {alloy::providers::RootProvider, alloy::transports::http::Http, reqwest::Client}; -#[cfg(not(test))] -use types::EthHttpProvider; - pub const ENV_PRIVATE_KEY: &str = "ETHEREUM_PRIVATE_KEY"; lazy_static! { @@ -71,14 +67,10 @@ pub struct EthereumSettlementClient { core_contract_client: StarknetValidityContractClient, wallet: EthereumWallet, wallet_address: Address, - #[cfg(not(test))] - provider: Arc, - #[cfg(test)] provider: RootProvider>, } impl EthereumSettlementClient { - #[cfg(not(test))] pub fn with_settings(settings: &impl SettingsProvider) -> Self { let settlement_cfg: EthereumSettlementConfig = settings.get_settings(SETTLEMENT_SETTINGS_NAME).unwrap(); @@ -87,15 +79,19 @@ impl EthereumSettlementClient { let wallet_address = signer.address(); let wallet = EthereumWallet::from(signer); - let provider = Arc::new( - ProviderBuilder::new().with_recommended_fillers().wallet(wallet.clone()).on_http(settlement_cfg.rpc_url), + let fill_provider = Arc::new( + ProviderBuilder::new() + .with_recommended_fillers() + .wallet(wallet.clone()) + .on_http(settlement_cfg.rpc_url.clone()), ); + let provider = ProviderBuilder::new().on_http(settlement_cfg.rpc_url); let core_contract_client = StarknetValidityContractClient::new( Address::from_str(&settlement_cfg.core_contract_address) .expect("Failed to convert the validity contract address.") .0 .into(), - provider.clone(), + fill_provider.clone(), ); EthereumSettlementClient { provider, core_contract_client, wallet, wallet_address } diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index d323a30f..bad1f20c 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -224,7 +224,9 @@ async fn get_last_settled_block_typical_works(#[case] fork_block_no: u64) { dotenvy::from_filename(&*ENV_FILE_PATH).expect("Could not load .env.test file."); env::set_var("DEFAULT_SETTLEMENT_CLIENT_RPC", &*ETH_RPC); - let TestSetup { anvil: _, ethereum_settlement_client, provider: _ } = setup_ethereum_test(fork_block_no); + let TestSetup { anvil, ethereum_settlement_client, provider: _ } = setup_ethereum_test(fork_block_no); + + println!("{:?}", anvil); let _ = ethereum_settlement_client.get_last_settled_block().await.expect("Could not get last settled block."); } From ef5a5628b712fa9e7eaf039270b5d443e140a10e Mon Sep 17 00:00:00 2001 From: Apoorv Sadana <95699312+apoorvsadana@users.noreply.github.com> Date: Tue, 20 Aug 2024 09:23:51 +0530 Subject: [PATCH 39/41] Reworking Settlement Client Changes (#89) Reworking Settlement Client test cases to be independent of env vars and work in minimalism. --------- Co-authored-by: Heemank Verma --- .env.test | 3 +- crates/settlement-clients/ethereum/src/lib.rs | 112 ++++++------ .../ethereum/src/tests/mod.rs | 170 +++++++++++------- .../settlement-client-interface/src/lib.rs | 3 - crates/settlement-clients/starknet/src/lib.rs | 6 - 5 files changed, 164 insertions(+), 130 deletions(-) diff --git a/.env.test b/.env.test index d3bd9c0c..b7b73e2c 100644 --- a/.env.test +++ b/.env.test @@ -25,10 +25,9 @@ PROVER_SERVICE="sharp" SETTLEMENT_LAYER="ethereum" DATA_STORAGE="s3" MONGODB_CONNECTION_STRING="mongodb://localhost:27017" - +DEFAULT_SETTLEMENT_CLIENT_RPC="http://localhost:3000" # Ethereum Settlement -DEFAULT_SETTLEMENT_CLIENT_RPC="http://localhost:3000" DEFAULT_L1_CORE_CONTRACT_ADDRESS="0xc662c410C0ECf747543f5bA90660f6ABeBD9C8c4" SHOULD_IMPERSONATE_ACCOUNT="true" TEST_DUMMY_CONTRACT_ADDRESS="0xE5b6F5e695BA6E4aeD92B68c4CC8Df1160D69A81" \ No newline at end of file diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index f310111c..6fbc3c31 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -13,11 +13,9 @@ use alloy::{ signers::local::PrivateKeySigner, }; -// use eyre::Result; use alloy::eips::eip2930::AccessList; use alloy::eips::eip4844::BYTES_PER_BLOB; use alloy::hex; -// use alloy::node_bindings::Anvil; use alloy::rpc::types::TransactionRequest; use alloy_primitives::Bytes; use async_trait::async_trait; @@ -29,6 +27,8 @@ use mockall::{automock, lazy_static, predicate::*}; use alloy::providers::ProviderBuilder; use conversion::{get_input_data_for_eip_4844, prepare_sidecar}; use settlement_client_interface::{SettlementClient, SettlementVerificationStatus, SETTLEMENT_SETTINGS_NAME}; +#[cfg(test)] +use url::Url; use utils::{env_utils::get_env_var_or_panic, settings::SettingsProvider}; use crate::clients::interfaces::validity_interface::StarknetValidityContractTrait; @@ -39,13 +39,6 @@ pub mod clients; pub mod config; pub mod conversion; -// IMPORTANT to understand #[cfg(test)], #[cfg(not(test))] and SHOULD_IMPERSONATE_ACCOUNT -// Two tests : `update_state_blob_with_dummy_contract_works` & `update_state_blob_with_impersonation_works` use a env var `SHOULD_IMPERSONATE_ACCOUNT` to inform the function `update_state_with_blobs` about the kind of testing, -// `SHOULD_IMPERSONATE_ACCOUNT` can have any of "0" or "1" value : -// - if "0" then : Testing via default Anvil address. -// - if "1" then : Testing via impersonating `Starknet Operator Address`. -// Note : changing between "0" and "1" is handled automatically by each test function, `no` manual change in `env.test` is needed. - #[cfg(test)] mod tests; pub mod types; @@ -62,12 +55,13 @@ lazy_static! { .expect("Error loading trusted setup file"); } -#[allow(dead_code)] pub struct EthereumSettlementClient { core_contract_client: StarknetValidityContractClient, wallet: EthereumWallet, wallet_address: Address, - provider: RootProvider>, + provider: Arc>>, + #[cfg(test)] + impersonate_account: Option
, } impl EthereumSettlementClient { @@ -79,50 +73,56 @@ impl EthereumSettlementClient { let wallet_address = signer.address(); let wallet = EthereumWallet::from(signer); - let fill_provider = Arc::new( - ProviderBuilder::new() - .with_recommended_fillers() - .wallet(wallet.clone()) - .on_http(settlement_cfg.rpc_url.clone()), + // provider without wallet + let provider = Arc::new(ProviderBuilder::new().on_http(settlement_cfg.rpc_url.clone())); + + // provider with wallet + let filler_provider = Arc::new( + ProviderBuilder::new().with_recommended_fillers().wallet(wallet.clone()).on_http(settlement_cfg.rpc_url), ); - let provider = ProviderBuilder::new().on_http(settlement_cfg.rpc_url); + let core_contract_client = StarknetValidityContractClient::new( Address::from_str(&settlement_cfg.core_contract_address) .expect("Failed to convert the validity contract address.") .0 .into(), - fill_provider.clone(), + filler_provider, ); - EthereumSettlementClient { provider, core_contract_client, wallet, wallet_address } + EthereumSettlementClient { + provider, + core_contract_client, + wallet, + wallet_address, + #[cfg(test)] + impersonate_account: None, + } } #[cfg(test)] - pub fn with_test_settings(settings: &impl SettingsProvider, provider: RootProvider>) -> Self { - use tests::{SHOULD_IMPERSONATE_ACCOUNT, TEST_DUMMY_CONTRACT_ADDRESS}; - let settlement_cfg: EthereumSettlementConfig = settings.get_settings(SETTLEMENT_SETTINGS_NAME).unwrap(); - + pub fn with_test_settings( + provider: RootProvider>, + core_contract_address: Address, + rpc_url: Url, + impersonate_account: Option
, + ) -> Self { let private_key = get_env_var_or_panic(ENV_PRIVATE_KEY); let signer: PrivateKeySigner = private_key.parse().expect("Failed to parse private key"); let wallet_address = signer.address(); let wallet = EthereumWallet::from(signer); - let fill_provider = Arc::new( - ProviderBuilder::new().with_recommended_fillers().wallet(wallet.clone()).on_http(settlement_cfg.rpc_url), - ); + let fill_provider = + Arc::new(ProviderBuilder::new().with_recommended_fillers().wallet(wallet.clone()).on_http(rpc_url)); - let core_contract_address = if *SHOULD_IMPERSONATE_ACCOUNT { - &settlement_cfg.core_contract_address - } else { - &*TEST_DUMMY_CONTRACT_ADDRESS - }; + let core_contract_client = StarknetValidityContractClient::new(core_contract_address, fill_provider); - let core_contract_client = StarknetValidityContractClient::new( - Address::from_str(core_contract_address).unwrap().0.into(), - fill_provider, - ); - - EthereumSettlementClient { provider, core_contract_client, wallet, wallet_address } + EthereumSettlementClient { + provider: Arc::new(provider), + core_contract_client, + wallet, + wallet_address, + impersonate_account, + } } /// Build kzg proof for the x_0 point evaluation @@ -180,12 +180,6 @@ impl SettlementClient for EthereumSettlementClient { } /// Should be used to update state on core contract when DA is in blobs/alt DA - async fn update_state_blobs(&self, program_output: Vec<[u8; 32]>, kzg_proof: [u8; 48]) -> Result { - let program_output: Vec = vec_u8_32_to_vec_u256(&program_output)?; - let tx_receipt = self.core_contract_client.update_state_kzg(program_output, kzg_proof).await?; - Ok(format!("0x{:x}", tx_receipt.transaction_hash)) - } - async fn update_state_with_blobs( &self, program_output: Vec<[u8; 32]>, @@ -242,13 +236,19 @@ impl SettlementClient for EthereumSettlementClient { let signature = self.wallet.default_signer().sign_transaction(&mut variant).await?; let tx_signed = variant.into_signed(signature); let tx_envelope: TxEnvelope = tx_signed.into(); - // IMP: this conversion strips signature from the transaction + // IMP: this conversion strips signature from the transaction #[cfg(not(test))] let txn_request: TransactionRequest = tx_envelope.into(); #[cfg(test)] - let txn_request = test_config::configure_transaction(tx_envelope); + let txn_request = test_config::configure_transaction( + // self.provider.clone(), + tx_envelope, + self.impersonate_account, + nonce, + ) + .await; let pending_transaction = self.provider.send_transaction(txn_request).await?; return Ok(pending_transaction.tx_hash().to_string()); @@ -293,14 +293,26 @@ impl SettlementClient for EthereumSettlementClient { mod test_config { use super::*; use alloy::network::TransactionBuilder; - use tests::{ADDRESS_TO_IMPERSONATE, SHOULD_IMPERSONATE_ACCOUNT, TEST_NONCE}; - pub fn configure_transaction(tx_envelope: TxEnvelope) -> TransactionRequest { + pub async fn configure_transaction( + // provider: Arc>>, + tx_envelope: TxEnvelope, + impersonate_account: Option
, + nonce: u64, + ) -> TransactionRequest { let mut txn_request: TransactionRequest = tx_envelope.into(); - if *SHOULD_IMPERSONATE_ACCOUNT { - txn_request.set_nonce(*TEST_NONCE); - txn_request = txn_request.with_from(*ADDRESS_TO_IMPERSONATE); + // IMPORTANT to understand #[cfg(test)], #[cfg(not(test))] and SHOULD_IMPERSONATE_ACCOUNT + // Two tests : `update_state_blob_with_dummy_contract_works` & `update_state_blob_with_impersonation_works` use a env var `SHOULD_IMPERSONATE_ACCOUNT` to inform the function `update_state_with_blobs` about the kind of testing, + // `SHOULD_IMPERSONATE_ACCOUNT` can have any of "0" or "1" value : + // - if "0" then : Testing via default Anvil address. + // - if "1" then : Testing via impersonating `Starknet Operator Address`. + // Note : changing between "0" and "1" is handled automatically by each test function, `no` manual change in `env.test` is needed. + if let Some(impersonate_account) = impersonate_account { + // let nonce = + // provider.get_transaction_count(impersonate_account).await.unwrap().to_string().parse::().unwrap(); + txn_request.set_nonce(nonce); + txn_request = txn_request.with_from(impersonate_account); } txn_request diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index bad1f20c..65694e46 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -1,3 +1,4 @@ +use alloy::eips::eip4844::BYTES_PER_BLOB; use alloy::node_bindings::AnvilInstance; use alloy::primitives::U256; use alloy::providers::{ext::AnvilApi, ProviderBuilder}; @@ -19,7 +20,6 @@ use tokio::time::sleep; use utils::env_utils::get_env_var_or_panic; use settlement_client_interface::SettlementClient; -use utils::settings::default::DefaultSettingsProvider; use crate::conversion::to_padded_hex; use crate::EthereumSettlementClient; @@ -39,6 +39,7 @@ impl Pipe for S {} // TODO: betterment of file routes use lazy_static::lazy_static; +use url::Url; lazy_static! { static ref ENV_FILE_PATH: PathBuf = PathBuf::from(".env.test"); @@ -47,16 +48,11 @@ lazy_static! { .to_str() .expect("Path contains invalid Unicode") .to_string(); - static ref PORT: u16 = 3000_u16; static ref ETH_RPC: String = get_env_var_or_panic("ETHEREUM_BLAST_RPC_URL"); - static ref STARKNET_OPERATOR_ADDRESS: Address = - Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("Could not impersonate account."); + pub static ref STARKNET_OPERATOR_ADDRESS: Address = + Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("Unable to parse address"); static ref STARKNET_CORE_CONTRACT_ADDRESS: Address = Address::from_str("0xc662c410c0ecf747543f5ba90660f6abebd9c8c4").expect("Could not impersonate account."); - pub static ref ADDRESS_TO_IMPERSONATE: Address = - Address::from_str("0x2C169DFe5fBbA12957Bdd0Ba47d9CEDbFE260CA7").expect("Unable to parse address"); - pub static ref TEST_DUMMY_CONTRACT_ADDRESS: String = get_env_var_or_panic("TEST_DUMMY_CONTRACT_ADDRESS"); - pub static ref SHOULD_IMPERSONATE_ACCOUNT: bool = get_env_var_or_panic("SHOULD_IMPERSONATE_ACCOUNT") == *"true"; pub static ref TEST_NONCE: u64 = 666068; } @@ -77,61 +73,79 @@ sol! { } } -pub struct TestSetup { - pub anvil: AnvilInstance, - pub ethereum_settlement_client: EthereumSettlementClient, - pub provider: alloy::providers::RootProvider>, +struct EthereumTestBuilder { + fork_block: Option, + impersonator: Option
, +} + +struct EthereumTest { + _anvil: AnvilInstance, + provider: alloy::providers::RootProvider>, + rpc_url: Url, } -fn setup_ethereum_test(block_no: u64) -> TestSetup { - // Load ENV vars - dotenvy::from_filename(&*ENV_FILE_PATH).expect("Could not load .env.test file."); +impl EthereumTestBuilder { + fn new() -> Self { + EthereumTestBuilder { fork_block: None, impersonator: None } + } + + fn with_fork_block(mut self, block_no: u64) -> Self { + self.fork_block = Some(block_no); + self + } + + fn with_impersonator(mut self, impersonator: Address) -> Self { + self.impersonator = Some(impersonator); + self + } - // Setup Anvil - let anvil = Anvil::new() - .port(*PORT) - .fork(&*ETH_RPC) - .fork_block_number(block_no - 1) - .try_spawn() - .expect("Could not spawn Anvil."); + async fn build(&self) -> EthereumTest { + // Load ENV vars + dotenvy::from_filename(&*ENV_FILE_PATH).expect("Could not load .env.test file."); - // Setup Provider - let provider = ProviderBuilder::new().on_http(anvil.endpoint_url()); + // Setup Anvil + let anvil = match self.fork_block { + Some(fork_block) => { + Anvil::new().fork(&*ETH_RPC).fork_block_number(fork_block).try_spawn().expect("Could not spawn Anvil.") + } + None => Anvil::new().try_spawn().expect("Could not spawn Anvil."), + }; + + // Setup Provider + let provider = ProviderBuilder::new().on_http(anvil.endpoint_url()); + + if let Some(impersonator) = self.impersonator { + provider.anvil_impersonate_account(impersonator).await.expect("Unable to impersonate account."); + } - // Setup EthereumSettlementClient - let settings_provider: DefaultSettingsProvider = DefaultSettingsProvider {}; - let ethereum_settlement_client = EthereumSettlementClient::with_test_settings(&settings_provider, provider.clone()); + let rpc_url = anvil.endpoint_url(); - TestSetup { anvil, ethereum_settlement_client, provider } + EthereumTest { _anvil: anvil, provider, rpc_url } + } } #[rstest] #[tokio::test] -#[case::basic(20468828)] /// Tests if the method is able to do a transaction with same function selector on a dummy contract. /// If we impersonate starknet operator then we loose out on testing for validity of signature in the transaction. /// Starknet core contract has a modifier `onlyOperator` that restricts anyone but the operator to send transaction to `updateStateKzgDa` method /// And hence to test the signature and transaction via a dummy contract that has same function selector as `updateStateKzgDa`. /// and anvil is for testing on fork Eth. -async fn update_state_blob_with_dummy_contract_works(#[case] fork_block_no: u64) { - env::set_var("SHOULD_IMPERSONATE_ACCOUNT", "false"); - let TestSetup { anvil, ethereum_settlement_client, provider } = setup_ethereum_test(fork_block_no); +async fn update_state_blob_with_dummy_contract_works() { + let setup = EthereumTestBuilder::new().build().await; - println!("{:?}", anvil); // Deploying a dummy contract - let contract = DummyCoreContract::deploy(&provider).await.expect("Unable to deploy address"); - assert_eq!( - contract.address().to_string(), - *TEST_DUMMY_CONTRACT_ADDRESS, - "Dummy Contract got deployed on unexpected address" - ); + let contract = DummyCoreContract::deploy(&setup.provider).await.expect("Unable to deploy address"); + let ethereum_settlement_client = + EthereumSettlementClient::with_test_settings(setup.provider.clone(), *contract.address(), setup.rpc_url, None); // Getting latest nonce after deployment let nonce = ethereum_settlement_client.get_nonce().await.expect("Unable to fetch nonce"); - // generating program output and blob vector - let program_output = get_program_output(fork_block_no); - let blob_data_vec = get_blob_data(fork_block_no); + // keeping 9 elements because the code accesses 8th index as program output + let program_output = vec![[0; 32]; 9]; + // keeping one element as we've a check in build_proof + let blob_data_vec = vec![vec![0; BYTES_PER_BLOB]]; // Calling update_state_with_blobs let update_state_result = ethereum_settlement_client @@ -142,7 +156,8 @@ async fn update_state_blob_with_dummy_contract_works(#[case] fork_block_no: u64) // Asserting, Expected to receive transaction hash. assert!(!update_state_result.is_empty(), "No transaction Hash received."); - let txn = provider + let txn = setup + .provider .get_transaction_by_hash(FixedBytes::from_str(update_state_result.as_str()).expect("Unable to convert txn")) .await .expect("did not get txn from hash") @@ -150,7 +165,7 @@ async fn update_state_blob_with_dummy_contract_works(#[case] fork_block_no: u64) assert_eq!(txn.hash.to_string(), update_state_result.to_string()); assert!(txn.signature.is_some()); - assert_eq!(txn.to.unwrap().to_string(), *TEST_DUMMY_CONTRACT_ADDRESS); + assert_eq!(txn.to.unwrap(), *contract.address()); // Testing verify_tx_inclusion sleep(Duration::from_secs(2)).await; @@ -167,28 +182,42 @@ async fn update_state_blob_with_dummy_contract_works(#[case] fork_block_no: u64) #[rstest] #[tokio::test] -#[case::basic(20468828)] +#[case::basic(20468827)] /// tests if the method is able to impersonate the`Starknet Operator` and do an `update_state` transaction. /// We impersonate the Starknet Operator to send a transaction to the Core contract /// Here signature checks are bypassed and anvil is for testing on fork Eth. async fn update_state_blob_with_impersonation_works(#[case] fork_block_no: u64) { - let TestSetup { anvil, ethereum_settlement_client, provider } = setup_ethereum_test(fork_block_no); - - println!("{:?}", anvil); - - provider.anvil_impersonate_account(*STARKNET_OPERATOR_ADDRESS).await.expect("Unable to impersonate account."); + let setup = EthereumTestBuilder::new() + .with_fork_block(fork_block_no) + .with_impersonator(*STARKNET_OPERATOR_ADDRESS) + .build() + .await; + let ethereum_settlement_client = EthereumSettlementClient::with_test_settings( + setup.provider.clone(), + *STARKNET_CORE_CONTRACT_ADDRESS, + setup.rpc_url, + Some(*STARKNET_OPERATOR_ADDRESS), + ); - let nonce = ethereum_settlement_client.get_nonce().await.expect("Unable to fetch nonce"); + // let nonce = ethereum_settlement_client.get_nonce().await.expect("Unable to fetch nonce"); + let nonce = setup + .provider + .get_transaction_count(*STARKNET_OPERATOR_ADDRESS) + .await + .unwrap() + .to_string() + .parse::() + .unwrap(); // Create a contract instance. - let contract = STARKNET_CORE_CONTRACT::new(*STARKNET_CORE_CONTRACT_ADDRESS, provider.clone()); + let contract = STARKNET_CORE_CONTRACT::new(*STARKNET_CORE_CONTRACT_ADDRESS, setup.provider.clone()); // Call the contract, retrieve the current stateBlockNumber. let prev_block_number = contract.stateBlockNumber().call().await.unwrap(); // generating program output and blob vector - let program_output = get_program_output(fork_block_no); - let blob_data_vec = get_blob_data(fork_block_no); + let program_output = get_program_output(fork_block_no + 1); + let blob_data_vec = get_blob_data(fork_block_no + 1); // Calling update_state_with_blobs let update_state_result = ethereum_settlement_client @@ -199,36 +228,39 @@ async fn update_state_blob_with_impersonation_works(#[case] fork_block_no: u64) // Asserting, Expected to receive transaction hash. assert!(!update_state_result.is_empty(), "No transaction Hash received."); - // Call the contract, retrieve the latest stateBlockNumber. - let latest_block_number = contract.stateBlockNumber().call().await.unwrap(); - - assert_eq!(prev_block_number._0.as_u32() + 1, latest_block_number._0.as_u32()); - - // Testing verify_tx_inclusion sleep(Duration::from_secs(2)).await; ethereum_settlement_client .wait_for_tx_finality(update_state_result.as_str()) .await .expect("Could not wait for txn finality."); + let verified_inclusion = ethereum_settlement_client .verify_tx_inclusion(update_state_result.as_str()) .await .expect("Could not verify inclusion."); assert_eq!(verified_inclusion, SettlementVerificationStatus::Verified); + + // Call the contract, retrieve the latest stateBlockNumber. + let latest_block_number = contract.stateBlockNumber().call().await.unwrap(); + + assert_eq!(prev_block_number._0.as_u32() + 1, latest_block_number._0.as_u32()); } #[rstest] #[tokio::test] -#[case::typical(20468828)] +#[case::typical(20468827)] async fn get_last_settled_block_typical_works(#[case] fork_block_no: u64) { - dotenvy::from_filename(&*ENV_FILE_PATH).expect("Could not load .env.test file."); - env::set_var("DEFAULT_SETTLEMENT_CLIENT_RPC", &*ETH_RPC); - - let TestSetup { anvil, ethereum_settlement_client, provider: _ } = setup_ethereum_test(fork_block_no); - - println!("{:?}", anvil); - - let _ = ethereum_settlement_client.get_last_settled_block().await.expect("Could not get last settled block."); + let setup = EthereumTestBuilder::new().with_fork_block(fork_block_no).build().await; + let ethereum_settlement_client = EthereumSettlementClient::with_test_settings( + setup.provider.clone(), + *STARKNET_CORE_CONTRACT_ADDRESS, + setup.rpc_url, + None, + ); + assert_eq!( + ethereum_settlement_client.get_last_settled_block().await.expect("Could not get last settled block."), + 666039 + ); } #[rstest] diff --git a/crates/settlement-clients/settlement-client-interface/src/lib.rs b/crates/settlement-clients/settlement-client-interface/src/lib.rs index 655aa089..a827ad47 100644 --- a/crates/settlement-clients/settlement-client-interface/src/lib.rs +++ b/crates/settlement-clients/settlement-client-interface/src/lib.rs @@ -36,9 +36,6 @@ pub trait SettlementClient: Send + Sync { nonce: u64, ) -> Result; - /// Should be used to update state on core contract when DA is in blobs/alt DA - async fn update_state_blobs(&self, program_output: Vec<[u8; 32]>, kzg_proof: [u8; 48]) -> Result; - /// Should verify the inclusion of a tx in the settlement layer async fn verify_tx_inclusion(&self, tx_hash: &str) -> Result; diff --git a/crates/settlement-clients/starknet/src/lib.rs b/crates/settlement-clients/starknet/src/lib.rs index e40c2927..0730309e 100644 --- a/crates/settlement-clients/starknet/src/lib.rs +++ b/crates/settlement-clients/starknet/src/lib.rs @@ -127,12 +127,6 @@ impl SettlementClient for StarknetSettlementClient { Ok(format!("0x{:x}", invoke_result.transaction_hash)) } - /// Should be used to update state on core contract when DA is in blobs/alt DA - #[allow(unused)] - async fn update_state_blobs(&self, program_output: Vec<[u8; 32]>, kzg_proof: [u8; 48]) -> Result { - !unimplemented!("not available for starknet settlement layer") - } - /// Should verify the inclusion of a tx in the settlement layer async fn verify_tx_inclusion(&self, tx_hash: &str) -> Result { let tx_hash = FieldElement::from_hex_be(tx_hash)?; From b93372e68e7877d08b358f2e79d3b91cebbe2638 Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Fri, 23 Aug 2024 19:23:22 +0530 Subject: [PATCH 40/41] update PR reviews fixed --- .env.test | 1 + .../ethereum/src/conversion.rs | 18 +++++++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.env.test b/.env.test index 8ed22be9..08b22c73 100644 --- a/.env.test +++ b/.env.test @@ -17,6 +17,7 @@ MADARA_RPC_URL="http://localhost:3000" ETHEREUM_RPC_URL="http://localhost:3001" MEMORY_PAGES_CONTRACT_ADDRESS="0x000000000000000000000000000000000001dead" PRIVATE_KEY="0xdead" +# Private key of Test wallet provided by Anvil ETHEREUM_PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" STARKNET_SOLIDITY_CORE_CONTRACT_ADDRESS="0x000000000000000000000000000000000002dead" diff --git a/crates/settlement-clients/ethereum/src/conversion.rs b/crates/settlement-clients/ethereum/src/conversion.rs index 04543c27..73f2c63f 100644 --- a/crates/settlement-clients/ethereum/src/conversion.rs +++ b/crates/settlement-clients/ethereum/src/conversion.rs @@ -6,7 +6,7 @@ use c_kzg::{Blob, KzgCommitment, KzgProof, KzgSettings}; use color_eyre::{eyre::ContextCompat, Result as EyreResult}; use std::fmt::Write; -/// Converts a `&[Vec]` to `Vec`. Each inner slice is expected to be exactly 32 bytes long. +/// Converts a `&[[u8; 32]]` to `Vec`. /// Pads with zeros if any inner slice is shorter than 32 bytes. pub(crate) fn vec_u8_32_to_vec_u256(slices: &[[u8; 32]]) -> EyreResult> { slices.iter().map(|slice| slice_u8_to_u256(slice)).collect() @@ -17,9 +17,9 @@ pub(crate) fn slice_u8_to_u256(slice: &[u8]) -> EyreResult { U256::try_from_be_slice(slice).wrap_err_with(|| "could not convert &[u8] to U256".to_string()) } -// Function to convert a slice of u8 to a padded hex string -// Function only takes a slice of length up to 32 elements -// Pads the value on the right side with zeros only if the converted string has lesser than 64 characters. +/// Function to convert a slice of u8 to a padded hex string +/// Function only takes a slice of length up to 32 elements +/// Pads the value on the right side with zeros only if the converted string has lesser than 64 characters. pub(crate) fn to_padded_hex(slice: &[u8]) -> String { assert!(slice.len() <= 32, "Slice length must not exceed 32"); let hex = slice.iter().fold(String::new(), |mut output, byte| { @@ -46,7 +46,7 @@ pub fn get_input_data_for_eip_4844(program_output: Vec<[u8; 32]>, kzg_proof: [u8 // program_output let program_output_length = program_output.len(); - let program_output_hex = vec_u8_32_to_hex_string(program_output); + let program_output_hex = u8_32_slice_to_hex_string(&program_output); // length for program_output: 3*64 [offset, length, lines all have 64 char length] + length of program_output let length_program_output = (3 * 64 + program_output_hex.len()) / 2; @@ -72,8 +72,8 @@ pub fn get_input_data_for_eip_4844(program_output: Vec<[u8; 32]>, kzg_proof: [u8 Ok(input_data) } -pub(crate) fn vec_u8_32_to_hex_string(data: Vec<[u8; 32]>) -> String { - data.into_iter().fold(String::new(), |mut output, arr| { +pub(crate) fn u8_32_slice_to_hex_string(data: &[[u8; 32]]) -> String { + data.iter().fold(String::new(), |mut output, arr| { // Convert the array to a hex string let hex = arr.iter().fold(String::new(), |mut output, byte| { let _ = write!(output, "{byte:02x}"); @@ -232,8 +232,8 @@ mod tests { ], format!("{}{}", "ff".repeat(32), "f5".repeat(32)) )] - fn vec_u8_32_to_hex_string_works(#[case] slice: Vec<[u8; 32]>, #[case] expected: String) { - let result = vec_u8_32_to_hex_string(slice); + fn u8_32_slice_to_hex_string_works(#[case] slice: Vec<[u8; 32]>, #[case] expected: String) { + let result = u8_32_slice_to_hex_string(&slice); assert_eq!(result, expected); } From f9d36829517e00a8914842a4477b14ae4ac3d563 Mon Sep 17 00:00:00 2001 From: Heemank Verma Date: Fri, 23 Aug 2024 19:27:13 +0530 Subject: [PATCH 41/41] update PR reviews fixed --- .github/workflows/coverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 0351156c..b841c16b 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -43,7 +43,7 @@ jobs: profile: minimal toolchain: stable override: true - + - name: Rust Cache uses: Swatinem/rust-cache@v2