Compare commits

...

8 Commits

Author SHA1 Message Date
Jeremy Felder
b6dded89cd Release v1.2.0 (#364)
Release v1.2.0:

- [FEAT] Add Poseidon hash as primitive
- [FEAT] Add Merkle tree using poseidon hash
- [BUG] Fix NTT overflow when using large cosets
2024-02-07 17:01:09 +02:00
Jeremy Felder
5a138367f8 (chore): bump rust crate versions (#362)
bump rust crate versions
2024-02-07 14:49:54 +02:00
ChickenLover
a3fc01d88d Implement Poseidon and TreeBuilder (#352)
* BW scalar field is now the same as BLS base field

* add poseidon

* add merkle tree builder

* poseidon rust bindings

* implement rust bindings

* add doc comments

* remove global poseidon constants

* add custom constants API and script for generating new constants

* add the rest of the curves for poseidon

* add all the curves for real

* misname bls12-377

* typo

* partial rounds

* minor fixes

* small tweak for big performance boost

* add CHK_INIT_IF_RETURN

---------

Co-authored-by: DmytroTym <dmytrotym1@gmail.com>
2024-02-07 14:49:54 +02:00
DmytroTym
d84cab79fd Changed long to int64 2024-02-07 14:49:54 +02:00
DmytroTym
46d7b88f6e Fixed overflow in large coset NTTs 2024-02-07 14:49:54 +02:00
Jeremy Felder
77a7613aa2 Release v1.1.0 (#357)
Release v1.1.0:
- Updated examples to use the new API
- [c++] Curve specific functions using macros
- [c++] Consolidate MSM and Batch MSM to single function
- [CI] Add codespell in CI
- [FIX] Windows rust build
- [FIX] G2 on rust bindings
- [FIX] Bw6 using bls12377
2024-01-31 20:30:44 +02:00
Jeremy Felder
5a96f9937d Bump for release 2024-01-31 16:34:14 +02:00
Otsar
c1a32a9879 Update README.md (#339)
Added badge
2024-01-11 18:34:32 +02:00
58 changed files with 46654 additions and 7320 deletions

View File

@@ -80,7 +80,7 @@ jobs:
# Building from the root workspace will build all members of the workspace by default
run: cargo build --release --verbose
# TODO: Reenable once Golang bindings for v1+ is finished
# TODO: Re-enable once Golang bindings for v1+ is finished
# build-golang-linux:
# name: Build Golang on Linux
# runs-on: [self-hosted, Linux, X64, icicle]

View File

@@ -75,7 +75,7 @@ jobs:
if: needs.check-changed-files.outputs.cpp_cuda == 'true'
run: ctest
# TODO: Reenable once Golang bindings for v1+ is finished
# TODO: Re-enable once Golang bindings for v1+ is finished
# test-golang-linux:
# name: Test Golang on Linux
# runs-on: [self-hosted, Linux, X64, icicle]

View File

@@ -1,11 +1,10 @@
# ICICLE
**<div align="center">ICICLE is a library for ZK acceleration using CUDA-enabled GPUs.</div>**
**<div align="center">ICICLE is a library for ZK acceleration using CUDA-enabled GPUs.</div>**
<p align="center">
<img alt="ICICLE" width="300" height="300" src="https://user-images.githubusercontent.com/2446179/223707486-ed8eb5ab-0616-4601-8557-12050df8ccf7.png"/>
</p>
<p align="center">
<a href="https://discord.gg/EVVXTdt6DF">
<img src="https://img.shields.io/discord/1063033227788423299?logo=discord" alt="Chat with us on Discord">
@@ -13,6 +12,7 @@
<a href="https://twitter.com/intent/follow?screen_name=Ingo_zk">
<img src="https://img.shields.io/twitter/follow/Ingo_zk?style=social&logo=twitter" alt="Follow us on Twitter">
</a>
<img src="https://img.shields.io/badge/Machines%20running%20ICICLE-544-lightblue" alt="Machines running ICICLE">
</p>
## Background

View File

@@ -4,10 +4,10 @@ version = "1.0.0"
edition = "2018"
[dependencies]
icicle-cuda-runtime = { git = "https://github.com/ingonyama-zk/icicle.git", tag = "v1.0.0" }
icicle-core = { git = "https://github.com/ingonyama-zk/icicle.git", tag = "v1.0.0" }
icicle-bn254 = { git = "https://github.com/ingonyama-zk/icicle.git", tag = "v1.0.0", features = [ "g2" ] }
icicle-bls12-377 = { git = "https://github.com/ingonyama-zk/icicle.git", tag = "v1.0.0" }
icicle-cuda-runtime = { git = "https://github.com/ingonyama-zk/icicle.git", tag = "v1.1.0" }
icicle-core = { git = "https://github.com/ingonyama-zk/icicle.git", tag = "v1.1.0" }
icicle-bn254 = { git = "https://github.com/ingonyama-zk/icicle.git", tag = "v1.1.0", features = [ "g2" ] }
icicle-bls12-377 = { git = "https://github.com/ingonyama-zk/icicle.git", tag = "v1.1.0" }
ark-bn254 = { version = "0.4.0", optional = true}
ark-bls12-377 = { version = "0.4.0", optional = true}
ark-ec = { version = "0.4.0", optional = true}

View File

@@ -4,10 +4,10 @@ version = "1.0.0"
edition = "2018"
[dependencies]
icicle-cuda-runtime = { git = "https://github.com/ingonyama-zk/icicle.git", tag = "v1.0.0" }
icicle-core = { git = "https://github.com/ingonyama-zk/icicle.git", tag = "v1.0.0", features = ["arkworks"] }
icicle-bn254 = { git = "https://github.com/ingonyama-zk/icicle.git", tag = "v1.0.0", features = ["arkworks"] }
icicle-bls12-377 = { git = "https://github.com/ingonyama-zk/icicle.git", tag = "v1.0.0", features = ["arkworks"] }
icicle-cuda-runtime = { git = "https://github.com/ingonyama-zk/icicle.git", tag = "v1.1.0" }
icicle-core = { git = "https://github.com/ingonyama-zk/icicle.git", tag = "v1.1.0", features = ["arkworks"] }
icicle-bn254 = { git = "https://github.com/ingonyama-zk/icicle.git", tag = "v1.1.0", features = ["arkworks"] }
icicle-bls12-377 = { git = "https://github.com/ingonyama-zk/icicle.git", tag = "v1.1.0", features = ["arkworks"] }
ark-ff = { version = "0.4.0" }
ark-poly = "0.4.0"

View File

@@ -69,6 +69,7 @@ include_directories("${CMAKE_SOURCE_DIR}")
# when adding a new curve/field, append its name to the end of this list
set(SUPPORTED_CURVES bn254;bls12_381;bls12_377;bw6_761)
set(SUPPORTED_CURVES_WITH_POSEIDON bn254;bls12_381;bls12_377;bw6_761)
set(IS_CURVE_SUPPORTED FALSE)
set(I 0)
@@ -94,6 +95,11 @@ if (NOT BUILD_TESTS)
message(STATUS "Building without tests.")
if (CURVE IN_LIST SUPPORTED_CURVES_WITH_POSEIDON)
list(APPEND ICICLE_SOURCES appUtils/poseidon/poseidon.cu)
list(APPEND ICICLE_SOURCES appUtils/tree/merkle.cu)
endif()
add_library(
icicle
utils/vec_ops.cu
@@ -102,6 +108,7 @@ if (NOT BUILD_TESTS)
primitives/projective.cu
appUtils/msm/msm.cu
appUtils/ntt/ntt.cu
${ICICLE_SOURCES}
)
set_target_properties(icicle PROPERTIES OUTPUT_NAME "ingo_${CURVE}")
target_compile_definitions(icicle PRIVATE CURVE=${CURVE})

1
icicle/appUtils/poseidon/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
test_poseidon

View File

@@ -0,0 +1,3 @@
test_poseidon: test.cu poseidon.cu kernels.cu constants.cu
nvcc -o test_poseidon -I. -I../.. test.cu
./test_poseidon

View File

@@ -0,0 +1,115 @@
#include "poseidon.cuh"
/// These are pre-calculated constants for different curves
#if CURVE_ID == BN254
#include "appUtils/poseidon/constants/bn254_poseidon.h"
using namespace poseidon_constants_bn254;
#elif CURVE_ID == BLS12_381
#include "appUtils/poseidon/constants/bls12_381_poseidon.h"
using namespace poseidon_constants_bls12_381;
#elif CURVE_ID == BLS12_377
#include "appUtils/poseidon/constants/bls12_377_poseidon.h"
using namespace poseidon_constants_bls12_377;
#elif CURVE_ID == BW6_761
#include "appUtils/poseidon/constants/bw6_761_poseidon.h"
using namespace poseidon_constants_bw6_761;
#endif
namespace poseidon {
template <typename S>
cudaError_t create_optimized_poseidon_constants(
int arity,
int full_rounds_half,
int partial_rounds,
const S* constants,
device_context::DeviceContext& ctx,
PoseidonConstants<S>* poseidon_constants)
{
CHK_INIT_IF_RETURN();
cudaStream_t& stream = ctx.stream;
int width = arity + 1;
int round_constants_len = width * full_rounds_half * 2 + partial_rounds;
int mds_matrix_len = width * width;
int sparse_matrices_len = (width * 2 - 1) * partial_rounds;
int constants_len = round_constants_len + mds_matrix_len * 2 + sparse_matrices_len;
// Malloc memory for copying constants
S* d_constants;
CHK_IF_RETURN(cudaMallocAsync(&d_constants, sizeof(S) * constants_len, stream));
// Copy constants
CHK_IF_RETURN(cudaMemcpyAsync(d_constants, constants, sizeof(S) * constants_len, cudaMemcpyHostToDevice, stream));
S* round_constants = d_constants;
S* mds_matrix = round_constants + round_constants_len;
S* non_sparse_matrix = mds_matrix + mds_matrix_len;
S* sparse_matrices = non_sparse_matrix + mds_matrix_len;
// Pick the domain_tag accordinaly
// For now, we only support Merkle tree mode
uint32_t tree_domain_tag_value = 1;
tree_domain_tag_value = (tree_domain_tag_value << (width - 1)) - tree_domain_tag_value;
S domain_tag = S::from(tree_domain_tag_value);
// Make sure all the constants have been copied
CHK_IF_RETURN(cudaStreamSynchronize(stream));
*poseidon_constants = {arity, partial_rounds, full_rounds_half, round_constants,
mds_matrix, non_sparse_matrix, sparse_matrices, domain_tag};
return CHK_LAST();
}
template <typename S>
cudaError_t init_optimized_poseidon_constants(
int arity, device_context::DeviceContext& ctx, PoseidonConstants<S>* poseidon_constants)
{
CHK_INIT_IF_RETURN();
int full_rounds_half = FULL_ROUNDS_DEFAULT;
int partial_rounds;
unsigned char* constants;
switch (arity) {
case 2:
constants = poseidon_constants_2;
partial_rounds = partial_rounds_2;
break;
case 4:
constants = poseidon_constants_4;
partial_rounds = partial_rounds_4;
break;
case 8:
constants = poseidon_constants_8;
partial_rounds = partial_rounds_8;
break;
case 11:
constants = poseidon_constants_11;
partial_rounds = partial_rounds_11;
break;
default:
THROW_ICICLE_ERR(
IcicleError_t::InvalidArgument, "init_optimized_poseidon_constants: #arity must be one of [2, 4, 8, 11]");
}
S* h_constants = reinterpret_cast<S*>(constants);
create_optimized_poseidon_constants(arity, full_rounds_half, partial_rounds, h_constants, ctx, poseidon_constants);
return CHK_LAST();
}
extern "C" cudaError_t CONCAT_EXPAND(CURVE, CreateOptimizedPoseidonConstants)(
int arity,
int full_rounds_half,
int partial_rounds,
const curve_config::scalar_t* constants,
device_context::DeviceContext& ctx,
PoseidonConstants<curve_config::scalar_t>* poseidon_constants)
{
return create_optimized_poseidon_constants<curve_config::scalar_t>(
arity, full_rounds_half, partial_rounds, constants, ctx, poseidon_constants);
}
extern "C" cudaError_t CONCAT_EXPAND(CURVE, InitOptimizedPoseidonConstants)(
int arity, device_context::DeviceContext& ctx, PoseidonConstants<curve_config::scalar_t>* constants)
{
return init_optimized_poseidon_constants<curve_config::scalar_t>(arity, ctx, constants);
}
} // namespace poseidon

View File

@@ -1,54 +0,0 @@
#pragma once
#include "../../utils/error_handler.cuh"
#include "constants/constants_11.h"
#include "constants/constants_2.h"
#include "constants/constants_4.h"
#include "constants/constants_8.h"
#include <cassert>
#include <map>
const std::string UNSUPPORTED_ARITY_MESSAGE = "Unsupported arity";
uint32_t partial_rounds_number_from_arity(const uint32_t arity)
{
switch (arity) {
case 2:
return 55;
case 4:
return 56;
case 8:
return 57;
case 11:
return 57;
default:
THROW_ICICLE_ERR(IcicleError_t::InvalidArgument, UNSUPPORTED_ARITY_MESSAGE);
}
};
// TO-DO: change to mapping
const uint32_t FULL_ROUNDS_DEFAULT = 4;
// TO-DO: for now, the constants are only generated in bls12_381
template <typename S>
S* load_constants(const uint32_t arity)
{
unsigned char* constants;
switch (arity) {
case 2:
constants = constants_2;
break;
case 4:
constants = constants_4;
break;
case 8:
constants = constants_8;
break;
case 11:
constants = constants_11;
break;
default:
THROW_ICICLE_ERR(IcicleError_t::InvalidArgument, UNSUPPORTED_ARITY_MESSAGE);
}
return reinterpret_cast<S*>(constants);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,629 +0,0 @@
unsigned char constants_2[] = {
0xd8, 0xd3, 0x6e, 0x9d, 0x00, 0x0a, 0x32, 0xa7, 0x36, 0x8b, 0x75, 0xa2, 0x92, 0xac, 0x1e, 0x50, 0x24, 0x4a, 0xbb,
0x1d, 0x86, 0x51, 0xbd, 0x23, 0x7a, 0xe1, 0x3a, 0xfa, 0x4b, 0x06, 0x9f, 0x66, 0x15, 0x3f, 0x9d, 0x2b, 0x84, 0xab,
0x72, 0x6e, 0x34, 0x27, 0xac, 0x45, 0x96, 0x7c, 0xe6, 0xee, 0x6c, 0xa6, 0x4f, 0xc8, 0xf2, 0x7f, 0x53, 0xe4, 0x36,
0xca, 0xac, 0xfb, 0xde, 0xa8, 0x61, 0x0a, 0xd5, 0x65, 0x81, 0x12, 0x71, 0x47, 0x23, 0x1e, 0x30, 0x49, 0xaa, 0x1a,
0x4e, 0x2b, 0x29, 0x17, 0x5b, 0x27, 0xdf, 0x45, 0x8a, 0x1e, 0x1b, 0xf9, 0x09, 0x9d, 0xb8, 0x24, 0xfa, 0xce, 0xe9,
0x21, 0xd1, 0xa0, 0x12, 0x2f, 0xca, 0x56, 0x5f, 0x2f, 0x1b, 0x40, 0x6c, 0x31, 0x90, 0x55, 0x2f, 0x1f, 0x2b, 0xd0,
0xd2, 0xd2, 0xc9, 0x24, 0x26, 0x38, 0x05, 0x18, 0x53, 0x38, 0x1d, 0x42, 0xfc, 0x0b, 0xc8, 0xc5, 0x8b, 0x5a, 0xf3,
0x19, 0xca, 0xff, 0xf5, 0x3b, 0xef, 0x15, 0x4e, 0xf4, 0xcc, 0xbe, 0xe8, 0x42, 0x69, 0x68, 0xf4, 0xfc, 0xd3, 0xc3,
0xf0, 0x5d, 0x03, 0x89, 0x4a, 0xae, 0xb0, 0x13, 0x43, 0x39, 0xaa, 0x45, 0xb2, 0x41, 0x38, 0xf8, 0x20, 0x2d, 0xd1,
0x1f, 0x3c, 0xc4, 0xaa, 0xf1, 0x40, 0xd0, 0x26, 0xe4, 0x81, 0x74, 0x41, 0xc1, 0xb4, 0xd0, 0x64, 0x8d, 0xf9, 0xdd,
0x9a, 0x4b, 0x38, 0x45, 0x02, 0xcc, 0x01, 0x65, 0x25, 0x72, 0x24, 0x2b, 0xba, 0x3f, 0x2c, 0x1a, 0xbf, 0xc6, 0x3c,
0xcf, 0xa1, 0xef, 0x9c, 0xda, 0x2e, 0x9b, 0x14, 0xc7, 0x81, 0x65, 0x85, 0xc3, 0x24, 0x2c, 0x65, 0xc6, 0x51, 0x3a,
0xd3, 0xc1, 0xd1, 0xd5, 0x42, 0x8f, 0x2f, 0x3c, 0x0e, 0x61, 0xaf, 0xb8, 0xf6, 0x3c, 0x32, 0x1a, 0x9f, 0x28, 0x91,
0x9d, 0x02, 0x18, 0x97, 0x47, 0x79, 0x21, 0xf9, 0x61, 0x40, 0x5c, 0x16, 0xa9, 0xc5, 0x6e, 0xca, 0x9f, 0x37, 0xf1,
0x2a, 0x13, 0xf1, 0xf0, 0xf0, 0xef, 0xb4, 0x56, 0xf4, 0x08, 0x6a, 0x47, 0x52, 0x4a, 0x32, 0xbf, 0xb3, 0xab, 0xa5,
0xdf, 0x36, 0x12, 0x63, 0x5f, 0x2e, 0xc2, 0xf4, 0x17, 0xa4, 0x0c, 0xfd, 0xeb, 0x3d, 0xe9, 0xc7, 0x1d, 0x97, 0x5e,
0x52, 0x61, 0x75, 0x96, 0xfb, 0x11, 0x60, 0xcd, 0xf8, 0xca, 0xa8, 0x11, 0xdc, 0x6e, 0xcd, 0x59, 0xf3, 0x37, 0x41,
0xd6, 0x61, 0xb3, 0x74, 0xe5, 0xa8, 0xc1, 0x51, 0xf5, 0xa2, 0x57, 0x2e, 0x32, 0xe4, 0x0e, 0xd2, 0xed, 0x73, 0xca,
0x58, 0x7a, 0x81, 0x16, 0x9c, 0xa0, 0xa0, 0xc0, 0xaa, 0x65, 0xe0, 0x3f, 0x43, 0xb7, 0x03, 0xb0, 0x35, 0x84, 0x61,
0xf6, 0x60, 0x0e, 0x18, 0xb3, 0x0a, 0xc0, 0x59, 0x98, 0x57, 0x80, 0x7e, 0x26, 0x8b, 0x26, 0x0f, 0x94, 0x44, 0xbc,
0xc9, 0x71, 0xf8, 0x19, 0x9a, 0x3b, 0x0a, 0xea, 0x9a, 0xc0, 0x41, 0x26, 0x9b, 0x50, 0xe7, 0x5d, 0x1b, 0x59, 0x22,
0x26, 0x79, 0x3a, 0xae, 0x39, 0x61, 0x13, 0x9c, 0x8f, 0x8e, 0xd0, 0xbf, 0x84, 0xb8, 0xca, 0x3f, 0x71, 0x41, 0x70,
0x35, 0x88, 0x03, 0x63, 0x0d, 0xc5, 0x1a, 0xcb, 0x63, 0x11, 0x32, 0x90, 0xb6, 0xaa, 0xfb, 0xdc, 0xd9, 0xc3, 0xa1,
0x93, 0x41, 0xe8, 0xa1, 0xfb, 0x2d, 0x88, 0x9e, 0xe6, 0x37, 0x21, 0xb2, 0xbe, 0xfc, 0x64, 0x18, 0x37, 0x87, 0xbc,
0x36, 0xf2, 0xe4, 0x08, 0x5e, 0x87, 0x5f, 0x78, 0xbc, 0xbd, 0x4c, 0x91, 0x53, 0xb5, 0xf3, 0x3c, 0xe9, 0x8c, 0x1d,
0xa7, 0x0a, 0x95, 0x90, 0x55, 0xfd, 0xfd, 0x61, 0xd1, 0x38, 0x21, 0xca, 0x5c, 0x8f, 0xc0, 0xc9, 0x39, 0x81, 0x8e,
0x2d, 0x7c, 0xa7, 0xab, 0x84, 0xef, 0x09, 0xd3, 0x1f, 0xb2, 0xc8, 0xe7, 0x6a, 0x9c, 0xe5, 0x0d, 0xea, 0x15, 0x0f,
0xdf, 0x55, 0x7e, 0x25, 0x01, 0xad, 0x36, 0xdc, 0xfe, 0x2c, 0xc2, 0xf5, 0xd1, 0x57, 0xef, 0xf2, 0x1d, 0xdd, 0x82,
0xb0, 0x20, 0xbf, 0xfe, 0x8a, 0xa8, 0x4d, 0xb5, 0xd2, 0x03, 0x0d, 0x49, 0x43, 0xaf, 0x4a, 0xac, 0x95, 0x64, 0x6b,
0x62, 0x6e, 0x75, 0x84, 0x85, 0x56, 0x8f, 0x99, 0x6d, 0xfa, 0xb4, 0x37, 0x30, 0xc4, 0x06, 0x82, 0x32, 0xf0, 0x86,
0x6e, 0x5f, 0xde, 0x62, 0xa3, 0x61, 0xdc, 0x17, 0x37, 0x5c, 0xc8, 0x9b, 0x78, 0x6a, 0xf1, 0xa2, 0x77, 0x76, 0x44,
0x93, 0xbe, 0x6b, 0x71, 0x39, 0x0a, 0x35, 0x86, 0xa3, 0x4c, 0x84, 0x0f, 0xb1, 0xbf, 0x51, 0x88, 0x18, 0x88, 0x57,
0x09, 0x97, 0x55, 0xdf, 0x29, 0xe6, 0xff, 0xaa, 0xaf, 0x7b, 0x27, 0x29, 0xca, 0xf5, 0x11, 0x64, 0xa2, 0x2e, 0xb9,
0x99, 0xc5, 0xc4, 0x56, 0x1b, 0x03, 0x0c, 0xf1, 0x7e, 0x9b, 0xf1, 0x8b, 0x57, 0xc7, 0x4c, 0x4a, 0x05, 0x84, 0x78,
0x67, 0x3c, 0x82, 0xee, 0xe4, 0x55, 0xfb, 0xf2, 0x2e, 0xcb, 0x3a, 0x64, 0x88, 0x44, 0x15, 0x1b, 0x23, 0xaa, 0xe9,
0x9a, 0x04, 0xbd, 0xb4, 0xbd, 0x34, 0x28, 0x84, 0x34, 0x55, 0x13, 0x2c, 0xbd, 0x43, 0x1c, 0x3b, 0xaf, 0x1d, 0x95,
0x28, 0x18, 0x5f, 0xe7, 0x33, 0xa2, 0x4c, 0x58, 0xca, 0x42, 0xbe, 0x9e, 0x8e, 0x72, 0xae, 0xf1, 0x08, 0x40, 0x8f,
0x55, 0x61, 0x68, 0xa3, 0x2e, 0xff, 0x75, 0xe7, 0x38, 0x44, 0x68, 0x4a, 0x40, 0x05, 0x3f, 0x64, 0xf2, 0xf3, 0xd7,
0x8d, 0xd4, 0x3d, 0x69, 0x2e, 0xc9, 0x94, 0x3f, 0xc8, 0x75, 0xa1, 0xa1, 0xe5, 0x0b, 0x26, 0xec, 0x36, 0xe9, 0x29,
0x67, 0x4b, 0xc9, 0x2b, 0x0f, 0x4b, 0xa0, 0x56, 0xaf, 0x8b, 0x81, 0xea, 0x11, 0xf5, 0x42, 0xd3, 0xf2, 0x6e, 0x91,
0xf3, 0x35, 0x60, 0xe6, 0xa0, 0x80, 0x09, 0x45, 0xfe, 0x29, 0x4c, 0xde, 0x96, 0x76, 0x6e, 0x27, 0xfc, 0x64, 0xd3,
0xf7, 0xb4, 0xbf, 0xfa, 0x8c, 0x13, 0x68, 0x52, 0xf7, 0x9c, 0x86, 0x74, 0xe1, 0x8a, 0x01, 0x97, 0x73, 0x69, 0x29,
0x21, 0x0d, 0xae, 0xcf, 0xa7, 0x83, 0xf2, 0x8b, 0x93, 0x8d, 0xef, 0xf2, 0x7c, 0xc1, 0xfd, 0x50, 0xca, 0x95, 0x53,
0x77, 0x46, 0xa8, 0xe0, 0xb9, 0x6f, 0x4e, 0xe5, 0x55, 0x35, 0x2b, 0x6b, 0x67, 0x5e, 0x4e, 0x24, 0x51, 0x85, 0xf3,
0x19, 0x3d, 0x4c, 0x5c, 0x94, 0x7c, 0xb4, 0xe5, 0x49, 0xe8, 0xdf, 0xdd, 0x34, 0x4c, 0x64, 0x13, 0x6e, 0x67, 0x6a,
0xc6, 0x5e, 0x82, 0x1f, 0xdc, 0x0e, 0xf6, 0x15, 0x2a, 0x6f, 0xdd, 0x3a, 0x5c, 0x7d, 0x20, 0xbf, 0xd5, 0x89, 0xa1,
0x25, 0x2f, 0x59, 0xe7, 0xca, 0xa2, 0xb4, 0xde, 0x72, 0x2c, 0xe8, 0xe6, 0xc5, 0x3d, 0x93, 0xa5, 0xe0, 0x47, 0x7d,
0xe5, 0x65, 0x58, 0x59, 0xec, 0x62, 0x79, 0xc5, 0x69, 0x21, 0xfb, 0x12, 0x45, 0xe7, 0xb3, 0xa0, 0x5c, 0xba, 0xfb,
0x70, 0x38, 0x8b, 0x80, 0x95, 0x90, 0x72, 0x85, 0xf8, 0x61, 0xb3, 0x6f, 0x5f, 0x9d, 0x2d, 0x36, 0x9f, 0xe0, 0xeb,
0xc2, 0xd2, 0xcd, 0x33, 0x5a, 0x26, 0x78, 0xa7, 0x7f, 0x24, 0x52, 0x52, 0x3a, 0xe6, 0xf6, 0xf4, 0xa0, 0x9c, 0x52,
0x1d, 0xd5, 0x26, 0x5d, 0x9a, 0x7b, 0x9f, 0xba, 0x63, 0x6a, 0xda, 0xb9, 0xed, 0xec, 0x37, 0x8b, 0x24, 0x76, 0xcf,
0x1d, 0xa0, 0x3e, 0x1e, 0xc7, 0x60, 0x73, 0xc5, 0x5b, 0x7f, 0x93, 0x84, 0x62, 0x9b, 0xe8, 0x28, 0x07, 0xac, 0x77,
0xe7, 0xb3, 0x7d, 0x6f, 0x51, 0x91, 0xc7, 0xf3, 0x4d, 0x17, 0xeb, 0xe7, 0xc5, 0x31, 0x1e, 0x2d, 0x75, 0x2e, 0x30,
0xd8, 0xe8, 0x75, 0x4c, 0x37, 0x7a, 0xd6, 0x5c, 0x75, 0x1d, 0xc0, 0xb4, 0x99, 0xa2, 0x49, 0xe0, 0x72, 0xe2, 0xb3,
0x30, 0xed, 0x8b, 0xa7, 0x7e, 0x07, 0x79, 0x36, 0x77, 0xee, 0x15, 0x71, 0x1f, 0xe0, 0x0a, 0x98, 0x0a, 0xee, 0xcf,
0x0c, 0x59, 0xcc, 0xc7, 0x48, 0x50, 0xd3, 0xea, 0x41, 0xe1, 0x66, 0xd4, 0x3b, 0x24, 0xe9, 0x63, 0x4c, 0x16, 0xec,
0x51, 0x8e, 0x06, 0xc2, 0x11, 0x53, 0x58, 0x35, 0xd0, 0xd1, 0x77, 0x43, 0x59, 0x7f, 0xdb, 0x35, 0xe6, 0xea, 0x04,
0x1b, 0x69, 0x2e, 0x03, 0x2e, 0x5e, 0xa9, 0x67, 0xc7, 0x24, 0x52, 0xef, 0x5e, 0x1d, 0x8c, 0xe8, 0xa3, 0xa4, 0x8e,
0xc4, 0xcb, 0x5d, 0x8a, 0x57, 0x31, 0xdf, 0x3c, 0x38, 0xdf, 0xe6, 0xaf, 0x21, 0x77, 0x49, 0x02, 0xbc, 0x32, 0xde,
0x1e, 0x9f, 0x6a, 0x95, 0x9f, 0x94, 0x3b, 0x84, 0xdc, 0xea, 0x0b, 0x09, 0x76, 0x2f, 0x93, 0x70, 0x12, 0x8c, 0xb6,
0xd0, 0x20, 0xc3, 0xe2, 0x94, 0x8a, 0xb6, 0x2f, 0x9a, 0x03, 0xef, 0x5b, 0xc0, 0x47, 0xbf, 0xd0, 0xa7, 0x90, 0xe6,
0x13, 0xac, 0xc9, 0x2e, 0x10, 0xef, 0x10, 0xd1, 0x81, 0x65, 0x5d, 0xfa, 0x50, 0x65, 0xc0, 0xd6, 0x59, 0x3a, 0xe0,
0x5c, 0x94, 0xbd, 0xf8, 0xc6, 0x25, 0x85, 0x61, 0x2f, 0xa5, 0x5c, 0x0d, 0x7e, 0xe1, 0xa8, 0x04, 0x3b, 0x1f, 0x61,
0x34, 0x4b, 0x30, 0xf3, 0x84, 0x8e, 0x89, 0xb1, 0x58, 0xe2, 0x48, 0xf4, 0x79, 0x7f, 0x5f, 0x95, 0x1d, 0xe7, 0x71,
0x47, 0x5d, 0x43, 0x69, 0xd4, 0x7b, 0xe6, 0x87, 0x9e, 0x11, 0x12, 0x2a, 0x4f, 0xf7, 0x0c, 0xfb, 0x3c, 0x0b, 0x1d,
0xe7, 0xa3, 0x0b, 0xdf, 0xc7, 0xd1, 0x35, 0xdb, 0x7d, 0x58, 0x7b, 0x46, 0x40, 0x3e, 0xf6, 0xc1, 0xb6, 0x22, 0x99,
0x13, 0xd0, 0xd9, 0x3f, 0x28, 0xc5, 0xef, 0xeb, 0x6a, 0xda, 0xf5, 0xfb, 0x2d, 0x9d, 0x3c, 0x23, 0x23, 0x7d, 0x1f,
0x81, 0x55, 0xaf, 0xd4, 0xec, 0x7b, 0x09, 0x79, 0xe1, 0x90, 0xde, 0xe3, 0xff, 0x9a, 0x13, 0x2b, 0x4e, 0x70, 0x5c,
0x63, 0x72, 0x88, 0xfa, 0x74, 0x4f, 0xb7, 0xd1, 0x33, 0x3b, 0x8a, 0xec, 0x2e, 0x9b, 0x77, 0x0b, 0x8c, 0x3a, 0x91,
0x2c, 0x63, 0x3c, 0x03, 0x40, 0x1e, 0x78, 0x83, 0x4c, 0xcc, 0x0a, 0x3b, 0x99, 0x8d, 0x10, 0x54, 0x79, 0x3e, 0x85,
0x9d, 0xab, 0x2f, 0xd6, 0x9b, 0xab, 0x63, 0x85, 0x7a, 0x80, 0xe2, 0x43, 0xc0, 0x31, 0xa9, 0x77, 0x9a, 0x12, 0xf6,
0xcb, 0x8d, 0xfb, 0x65, 0xed, 0xb7, 0x11, 0xff, 0x5c, 0xe0, 0x8f, 0x16, 0xc6, 0x9b, 0x36, 0x56, 0x2b, 0x8a, 0xe1,
0x9b, 0xe1, 0xfc, 0x01, 0x3f, 0xa4, 0x49, 0x5d, 0x59, 0x19, 0xbd, 0xbe, 0x17, 0x49, 0xe5, 0xa1, 0xa7, 0xf7, 0x26,
0x19, 0xa4, 0x0f, 0xd3, 0x5b, 0x74, 0xa9, 0xfe, 0x53, 0x88, 0x51, 0xa8, 0x9c, 0x3f, 0xde, 0xbd, 0x19, 0xa0, 0x40,
0x31, 0x50, 0x1f, 0x8b, 0x92, 0x97, 0xb2, 0x1c, 0xc7, 0xb0, 0xdd, 0xd5, 0xae, 0x88, 0x92, 0x00, 0x4a, 0xd7, 0xb7,
0xf8, 0x02, 0xaa, 0x25, 0xbb, 0x05, 0x89, 0x78, 0xda, 0x9c, 0x00, 0xb5, 0x48, 0x2c, 0x0d, 0xf3, 0xfa, 0xfc, 0x4e,
0x6f, 0x3d, 0x96, 0x74, 0x92, 0xb5, 0x16, 0x01, 0x88, 0xb2, 0x4a, 0x9c, 0x43, 0x35, 0x75, 0xef, 0x3d, 0x6e, 0xd0,
0x92, 0xc0, 0x24, 0xf6, 0xd6, 0xc0, 0x01, 0xef, 0x23, 0xb0, 0x6e, 0x27, 0x21, 0x5e, 0xa1, 0x8c, 0x0f, 0x69, 0xbc,
0x09, 0x47, 0x2c, 0x13, 0x5d, 0xba, 0x32, 0x3c, 0x37, 0x62, 0x3a, 0xdf, 0x38, 0x5a, 0x17, 0xe2, 0xfc, 0xe3, 0x8e,
0xe2, 0xd6, 0x6d, 0x50, 0x1b, 0xd1, 0xcc, 0x4b, 0x9d, 0x66, 0x0a, 0x90, 0x85, 0x01, 0x3b, 0xa2, 0x77, 0xd4, 0x95,
0x90, 0x63, 0x49, 0x5e, 0x27, 0xe7, 0xab, 0xc5, 0xf1, 0xf9, 0xa8, 0xf2, 0x40, 0xb1, 0x14, 0x35, 0x4d, 0x69, 0x4c,
0x51, 0x3b, 0x9b, 0x10, 0x50, 0x70, 0x34, 0xf4, 0xbe, 0x14, 0x88, 0xb5, 0x40, 0x1a, 0x68, 0x74, 0x40, 0x4c, 0xa3,
0xa7, 0x0d, 0x32, 0x64, 0xaa, 0xef, 0xf5, 0x7b, 0x1a, 0x60, 0x1d, 0xfc, 0x33, 0xf2, 0x50, 0xc6, 0x39, 0x28, 0x53,
0xe7, 0x98, 0xbf, 0xbd, 0x1e, 0xac, 0x80, 0x35, 0x5d, 0x7a, 0x18, 0x96, 0x8f, 0xb1, 0x41, 0xc2, 0xcb, 0x7d, 0xd0,
0x75, 0xd4, 0xc2, 0x11, 0x78, 0xd8, 0xa1, 0x98, 0x53, 0x1c, 0x59, 0x72, 0xac, 0xc1, 0x37, 0x0f, 0x42, 0x13, 0x0b,
0x98, 0xf9, 0x6e, 0x6f, 0x36, 0x53, 0x8d, 0x66, 0x46, 0x65, 0xf0, 0x27, 0xd3, 0xe3, 0xf0, 0x10, 0x5d, 0x1b, 0xae,
0x8d, 0x49, 0xec, 0xe6, 0x40, 0xfc, 0xfa, 0xbe, 0x55, 0x60, 0x4b, 0xfe, 0xd0, 0xca, 0x6a, 0x45, 0xd0, 0xd5, 0xe1,
0x5f, 0x20, 0x67, 0x09, 0x4e, 0x6d, 0x59, 0xef, 0xba, 0xec, 0x57, 0x41, 0xfa, 0x62, 0x1c, 0x54, 0xa4, 0x74, 0x46,
0xd1, 0x91, 0x48, 0xc9, 0xa6, 0x07, 0x01, 0xd1, 0x43, 0xa0, 0xe7, 0x7f, 0x35, 0xa0, 0x6f, 0xe4, 0x57, 0xb0, 0xb8,
0x99, 0x7c, 0x93, 0x4a, 0x0d, 0x4b, 0x0a, 0xd6, 0x24, 0xb2, 0x27, 0xd1, 0xa8, 0x2e, 0x5b, 0x3c, 0xcc, 0x17, 0xb2,
0x8a, 0x70, 0x93, 0x2b, 0x00, 0x96, 0x2d, 0x90, 0x4d, 0x67, 0x62, 0xb8, 0xc6, 0xd1, 0x46, 0xda, 0x3b, 0x6d, 0xdf,
0xd6, 0x03, 0xf2, 0x01, 0xa2, 0x89, 0x6c, 0x50, 0xd5, 0xf0, 0xb1, 0xd2, 0x24, 0xdd, 0x02, 0x42, 0xde, 0x1d, 0x5b,
0x00, 0xe0, 0x5f, 0x5f, 0x31, 0xf8, 0x59, 0x9d, 0xc8, 0xa4, 0x70, 0x4d, 0x49, 0x54, 0xc3, 0x94, 0xbc, 0x58, 0x2e,
0x03, 0x02, 0xba, 0x43, 0x2b, 0xfd, 0x0f, 0x9c, 0x0f, 0x91, 0x28, 0xf4, 0x3b, 0xe7, 0xb1, 0x3b, 0x69, 0xbd, 0x6a,
0x8f, 0x20, 0xab, 0x8f, 0xd2, 0x5a, 0xf4, 0x00, 0x92, 0xcd, 0x45, 0xd5, 0x96, 0x37, 0x31, 0x0e, 0xfd, 0x75, 0xda,
0xa4, 0x0c, 0x57, 0xcf, 0x7b, 0x1b, 0xf5, 0xa9, 0xcd, 0xff, 0xaf, 0xe8, 0x54, 0x52, 0x8a, 0x9e, 0x03, 0x97, 0x5e,
0x62, 0x3f, 0x09, 0x6d, 0x54, 0x61, 0x7d, 0xfc, 0x7a, 0x33, 0x85, 0x38, 0x9a, 0x67, 0x4d, 0xb2, 0x24, 0xa7, 0x7d,
0x33, 0xff, 0x3d, 0xe5, 0x7f, 0x7d, 0x09, 0x60, 0x87, 0xa6, 0xe4, 0x96, 0x2d, 0x3d, 0x1a, 0xa4, 0x3d, 0x2e, 0x49,
0xcd, 0xb3, 0x62, 0x45, 0xa9, 0x84, 0xb3, 0xd8, 0xa5, 0x94, 0x07, 0xf0, 0x67, 0x39, 0xbc, 0x85, 0x9d, 0x3f, 0x14,
0xd2, 0x53, 0x83, 0x2e, 0x85, 0x89, 0x69, 0xc7, 0xe7, 0x88, 0xbf, 0x3e, 0x1d, 0x40, 0x53, 0x95, 0xc8, 0x78, 0x03,
0x87, 0x80, 0x93, 0x9c, 0x88, 0x32, 0x70, 0x2e, 0x91, 0x7b, 0x8f, 0x2b, 0x83, 0xd7, 0x32, 0x88, 0x5c, 0x94, 0x65,
0x4b, 0x1a, 0x31, 0xe1, 0x16, 0x25, 0x03, 0x6e, 0xfd, 0x91, 0x7c, 0x33, 0x81, 0xcd, 0x36, 0xbb, 0xf5, 0xd2, 0x7a,
0x65, 0x29, 0x34, 0xd7, 0x0e, 0x58, 0x75, 0xef, 0xda, 0x5e, 0xc0, 0x38, 0x16, 0x02, 0xff, 0x42, 0x1a, 0xad, 0xc5,
0x17, 0x61, 0x89, 0x83, 0xe1, 0xc0, 0x2c, 0x5f, 0xae, 0xaa, 0x4e, 0x0f, 0x4c, 0xd6, 0xe6, 0x14, 0xf2, 0xe9, 0x06,
0xc0, 0x16, 0x05, 0x9d, 0xd4, 0xa3, 0x32, 0x69, 0xa8, 0x8f, 0x51, 0x8c, 0x23, 0xfe, 0x66, 0x8b, 0x79, 0x79, 0xc2,
0x6c, 0xe8, 0xff, 0x1f, 0x24, 0xf9, 0x7e, 0xe1, 0x17, 0x23, 0x65, 0xf1, 0x53, 0x8e, 0x74, 0xd5, 0xb8, 0xc8, 0x95,
0x65, 0x00, 0xf3, 0x5f, 0x88, 0x99, 0x77, 0x8f, 0x71, 0xe5, 0xac, 0xee, 0x85, 0x4a, 0x22, 0x8b, 0x3b, 0xb9, 0xa6,
0x71, 0x54, 0x0a, 0x03, 0x60, 0x21, 0x82, 0x2f, 0xd6, 0x20, 0x91, 0x5d, 0xd9, 0x33, 0x5e, 0x54, 0x48, 0xf7, 0xfb,
0x6b, 0xd1, 0xef, 0x89, 0x0e, 0xd6, 0x4a, 0x18, 0x7d, 0x89, 0x19, 0x45, 0xae, 0x60, 0x2c, 0x91, 0x0a, 0x2e, 0x9c,
0xae, 0x8b, 0xd4, 0xd8, 0x03, 0x3d, 0x33, 0xc1, 0x31, 0x68, 0x7e, 0xed, 0xa8, 0xe3, 0xa8, 0x13, 0x65, 0x64, 0xc6,
0x5c, 0x5d, 0x60, 0xef, 0xfa, 0xf1, 0x2d, 0x33, 0x32, 0xcb, 0xc5, 0x4d, 0x0b, 0x48, 0xec, 0x84, 0x39, 0x92, 0x9a,
0xdc, 0x60, 0x0b, 0x0a, 0x66, 0xd9, 0xaa, 0x04, 0x53, 0x84, 0xe4, 0xeb, 0x71, 0x40, 0x82, 0xc6, 0x6c, 0xb8, 0xc0,
0xd1, 0x44, 0x9b, 0xb2, 0xb8, 0xa1, 0x5b, 0x14, 0x3d, 0xc7, 0x13, 0x1b, 0xee, 0x19, 0xb0, 0x60, 0xc5, 0x0a, 0xc6,
0x40, 0x7f, 0x0c, 0x2e, 0x75, 0x3c, 0x6f, 0x49, 0xba, 0x38, 0x0d, 0x03, 0x54, 0x04, 0xa3, 0x51, 0x0e, 0xaf, 0x3e,
0x34, 0xcb, 0x13, 0x22, 0x15, 0xfa, 0xc1, 0x16, 0x9e, 0x58, 0x78, 0x47, 0x9c, 0xad, 0x06, 0xcd, 0xf4, 0x10, 0xee,
0x0d, 0x78, 0x64, 0x8d, 0x70, 0x45, 0x55, 0xc2, 0x76, 0x45, 0x40, 0xf0, 0xe2, 0xfc, 0x0e, 0x8a, 0xc4, 0x78, 0x56,
0x7f, 0x8f, 0x05, 0x16, 0x31, 0xd3, 0x42, 0xd7, 0xfe, 0xbb, 0xaa, 0x89, 0x3c, 0x53, 0xd8, 0xa6, 0x95, 0x43, 0x72,
0xa2, 0x00, 0x15, 0xe7, 0x83, 0xea, 0x9e, 0xad, 0x69, 0x93, 0xc5, 0x9d, 0xfc, 0xc2, 0x8d, 0x3c, 0x58, 0xd8, 0x70,
0xd7, 0xcb, 0x65, 0x53, 0xc0, 0x9e, 0x83, 0x7e, 0x02, 0xbf, 0x6a, 0x90, 0x2f, 0xa2, 0xbe, 0x5d, 0x68, 0xeb, 0xbd,
0xad, 0xaf, 0xee, 0x91, 0x8a, 0xeb, 0x4e, 0x98, 0xc9, 0xed, 0x24, 0x0d, 0x0d, 0x8d, 0xdf, 0x16, 0xf6, 0x8e, 0x10,
0x59, 0x38, 0xa4, 0xd1, 0x0c, 0x09, 0x61, 0x06, 0x63, 0xcc, 0x5a, 0x84, 0x63, 0x1d, 0x90, 0xc8, 0xfb, 0x7b, 0x49,
0xcb, 0xcf, 0x5b, 0x34, 0x47, 0x96, 0x4e, 0xb1, 0x13, 0x66, 0xd5, 0x1f, 0x18, 0x30, 0xdd, 0xd7, 0x9d, 0xb8, 0x98,
0x41, 0xf2, 0x13, 0x1d, 0xc0, 0xbc, 0xf9, 0x03, 0x37, 0x48, 0x06, 0x89, 0xcf, 0xa8, 0x2e, 0x68, 0x3f, 0x16, 0xf3,
0x41, 0x22, 0xfc, 0x11, 0x2d, 0xb2, 0x29, 0x5c, 0x56, 0x0f, 0xf9, 0x84, 0x22, 0xee, 0x59, 0xb3, 0x95, 0x0b, 0xc7,
0xf0, 0x3a, 0xe4, 0x08, 0xce, 0x91, 0xdc, 0xa4, 0x1e, 0xad, 0x5e, 0x30, 0x9a, 0x0e, 0xe3, 0xb2, 0x81, 0x13, 0x97,
0x57, 0x31, 0x8f, 0x9c, 0xe1, 0x92, 0xa3, 0xab, 0x69, 0x94, 0x02, 0x5c, 0x68, 0xe9, 0x41, 0x1a, 0xc7, 0x17, 0xde,
0xaf, 0x77, 0xc5, 0x44, 0x2d, 0x6a, 0x2f, 0xd0, 0x16, 0x41, 0x40, 0x74, 0x57, 0x01, 0xd0, 0xaa, 0xe6, 0x70, 0x13,
0xf5, 0x3f, 0x0a, 0x39, 0x4d, 0xb1, 0x82, 0x87, 0x9b, 0xd6, 0x2b, 0xbd, 0xec, 0xa7, 0xcb, 0xdb, 0x57, 0x4e, 0x49,
0xa5, 0x8a, 0xa4, 0xbe, 0x83, 0xe6, 0x7c, 0x8b, 0x79, 0x4a, 0x7f, 0x39, 0x23, 0x07, 0xa8, 0xa9, 0x4e, 0xbd, 0xc3,
0xbb, 0xcf, 0xbc, 0xe2, 0x48, 0xa3, 0x60, 0xc0, 0x2c, 0x8d, 0x60, 0x26, 0x49, 0x07, 0x92, 0x78, 0xdb, 0x99, 0x94,
0x0e, 0x02, 0x27, 0x08, 0x9c, 0xc8, 0x23, 0x03, 0xfb, 0x6d, 0x18, 0x89, 0xe7, 0x3c, 0x08, 0xe5, 0x93, 0x31, 0xaa,
0x06, 0xa9, 0x86, 0x70, 0x40, 0x9e, 0x08, 0x5d, 0x7d, 0x8d, 0xa8, 0xee, 0xa0, 0x31, 0x49, 0x35, 0x99, 0x78, 0xf3,
0x97, 0x77, 0x05, 0x5f, 0xb2, 0xc8, 0xdc, 0xd8, 0xec, 0x9d, 0x48, 0xb5, 0xa4, 0x13, 0x01, 0x45, 0xe9, 0xe3, 0x84,
0x1c, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5,
0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x01, 0x00, 0x00, 0x40, 0xff,
0xff, 0xff, 0x3f, 0xff, 0xc4, 0xfe, 0x3f, 0x02, 0x3b, 0xce, 0xfe, 0x03, 0x62, 0x39, 0x07, 0x06, 0x62, 0x6b, 0x26,
0xf6, 0x1d, 0x36, 0x5f, 0x7e, 0x3d, 0xf2, 0x56, 0x34, 0x33, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xcc, 0x65, 0x6a, 0x65,
0x66, 0x9b, 0x95, 0x3e, 0x32, 0x03, 0xe8, 0x2d, 0x6c, 0x9e, 0x81, 0xef, 0x51, 0x2b, 0x4b, 0x2b, 0x4c, 0x98, 0x97,
0x8e, 0x45, 0x01, 0x00, 0x00, 0x40, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xc4, 0xfe, 0x3f, 0x02, 0x3b, 0xce, 0xfe, 0x03,
0x62, 0x39, 0x07, 0x06, 0x62, 0x6b, 0x26, 0xf6, 0x1d, 0x36, 0x5f, 0x7e, 0x3d, 0xf2, 0x56, 0x34, 0x33, 0x33, 0x33,
0xcc, 0xcc, 0xcc, 0xcc, 0x65, 0x6a, 0x65, 0x66, 0x9b, 0x95, 0x3e, 0x32, 0x03, 0xe8, 0x2d, 0x6c, 0x9e, 0x81, 0xef,
0x51, 0x2b, 0x4b, 0x2b, 0x4c, 0x98, 0x97, 0x8e, 0x45, 0x01, 0x00, 0x00, 0x80, 0x54, 0x55, 0x55, 0xd5, 0xa9, 0x4c,
0xa9, 0x2a, 0xad, 0x08, 0x1e, 0x1b, 0xaf, 0xde, 0x06, 0x08, 0x5c, 0x89, 0x05, 0x80, 0x11, 0x93, 0x58, 0x4d, 0xc5,
0x60, 0x9b, 0x60, 0x34, 0x33, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xcc, 0x65, 0x6a, 0x65, 0x66, 0x9b, 0x95, 0x3e, 0x32,
0x03, 0xe8, 0x2d, 0x6c, 0x9e, 0x81, 0xef, 0x51, 0x2b, 0x4b, 0x2b, 0x4c, 0x98, 0x97, 0x8e, 0x45, 0x01, 0x00, 0x00,
0x80, 0x54, 0x55, 0x55, 0xd5, 0xa9, 0x4c, 0xa9, 0x2a, 0xad, 0x08, 0x1e, 0x1b, 0xaf, 0xde, 0x06, 0x08, 0x5c, 0x89,
0x05, 0x80, 0x11, 0x93, 0x58, 0x4d, 0xc5, 0x60, 0x9b, 0x60, 0x25, 0x49, 0x92, 0x24, 0xdb, 0xb6, 0x6d, 0xdb, 0x48,
0x1a, 0x24, 0x49, 0xdc, 0x2e, 0x36, 0xaa, 0x4a, 0x62, 0x77, 0x70, 0x4b, 0x62, 0xc7, 0x57, 0x82, 0x91, 0x51, 0xe7,
0x60, 0x54, 0x1f, 0x21, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e,
0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x3e, 0x2c,
0xe4, 0xf5, 0x63, 0xa7, 0xf2, 0x0e, 0x51, 0x85, 0x27, 0x20, 0x86, 0xec, 0x5b, 0x36, 0xe4, 0xea, 0xf3, 0x0c, 0x61,
0xaa, 0xc6, 0x06, 0x74, 0xa3, 0xcc, 0xc1, 0x24, 0x1c, 0x32, 0x69, 0x95, 0x82, 0xd1, 0xb0, 0xf9, 0x5d, 0x48, 0x7e,
0x90, 0xf2, 0x18, 0x4e, 0xad, 0x05, 0x11, 0x3b, 0xc9, 0xfa, 0xfe, 0xf4, 0xe4, 0x3a, 0xfc, 0xd1, 0xb8, 0x4b, 0xee,
0xab, 0x1c, 0x80, 0x90, 0x0d, 0x01, 0x00, 0x00, 0x40, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xc4, 0xfe, 0x3f, 0x02, 0x3b,
0xce, 0xfe, 0x03, 0x62, 0x39, 0x07, 0x06, 0x62, 0x6b, 0x26, 0xf6, 0x1d, 0x36, 0x5f, 0x7e, 0x3d, 0xf2, 0x56, 0xa5,
0xd2, 0xd6, 0x97, 0x8d, 0x8a, 0xf9, 0x2a, 0x81, 0x39, 0xfd, 0xef, 0xe5, 0xd1, 0x41, 0x7d, 0xb2, 0xa6, 0x20, 0x46,
0x9a, 0xc4, 0x02, 0xcc, 0x8e, 0x38, 0xd8, 0xa2, 0x2d, 0x8f, 0xa7, 0x5e, 0xc2, 0x91, 0x1b, 0xf6, 0x2c, 0xb3, 0x21,
0xed, 0x91, 0x8c, 0x3e, 0xb5, 0x8e, 0x10, 0x26, 0x1f, 0x86, 0x4d, 0xae, 0x08, 0x65, 0x72, 0x2b, 0x5d, 0x7c, 0x64,
0x10, 0xfa, 0xc1, 0xa6, 0x20, 0x55, 0x34, 0x33, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xcc, 0x65, 0x6a, 0x65, 0x66, 0x9b,
0x95, 0x3e, 0x32, 0x03, 0xe8, 0x2d, 0x6c, 0x9e, 0x81, 0xef, 0x51, 0x2b, 0x4b, 0x2b, 0x4c, 0x98, 0x97, 0x8e, 0x45,
0xc2, 0x91, 0x1b, 0xf6, 0x2c, 0xb3, 0x21, 0xed, 0x91, 0x8c, 0x3e, 0xb5, 0x8e, 0x10, 0x26, 0x1f, 0x86, 0x4d, 0xae,
0x08, 0x65, 0x72, 0x2b, 0x5d, 0x7c, 0x64, 0x10, 0xfa, 0xc1, 0xa6, 0x20, 0x55, 0x54, 0x24, 0x25, 0x3c, 0xb1, 0x71,
0x3e, 0xfe, 0x4e, 0xcd, 0x04, 0x70, 0xb5, 0xcd, 0x65, 0x35, 0x9b, 0xb9, 0x69, 0x70, 0xfc, 0x9e, 0xc1, 0xa4, 0x2c,
0xe6, 0xb5, 0xd6, 0x38, 0x75, 0x07, 0x52, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55,
0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49,
0x4d, 0xa2, 0x92, 0xa6, 0xd6, 0xd4, 0x73, 0x2f, 0x8d, 0x27, 0x96, 0x8a, 0xc6, 0xc2, 0xbe, 0x74, 0x03, 0x5f, 0x90,
0x8e, 0xac, 0x46, 0x55, 0x68, 0x45, 0x37, 0x1a, 0xb9, 0xf9, 0x97, 0x93, 0xd3, 0x59, 0x64, 0xe7, 0x4f, 0xee, 0x6e,
0x02, 0x20, 0x8d, 0xbb, 0xe5, 0x84, 0x23, 0xf2, 0x41, 0x5f, 0x9f, 0xb2, 0xcf, 0xe4, 0x7d, 0xa9, 0x3a, 0xde, 0xdf,
0xd5, 0xb6, 0x90, 0xd5, 0x24, 0xa0, 0xe0, 0x47, 0xd4, 0xfc, 0x91, 0xd8, 0xff, 0x9b, 0x40, 0xeb, 0x00, 0x2b, 0x35,
0x8c, 0x86, 0x3c, 0x71, 0xa0, 0x84, 0xfc, 0x18, 0xf1, 0x16, 0x08, 0x99, 0xe5, 0x0c, 0x47, 0x83, 0xcb, 0x7e, 0x7f,
0x96, 0x58, 0x72, 0x40, 0x10, 0x59, 0x28, 0x31, 0x2f, 0x0c, 0x1c, 0xa1, 0x19, 0x7f, 0x78, 0x5a, 0x5b, 0x8b, 0x40,
0xe2, 0x52, 0xa3, 0xda, 0xea, 0xec, 0x8f, 0x71, 0xbc, 0x5d, 0x9a, 0xb2, 0xe8, 0x86, 0x6b, 0x01, 0x00, 0x00, 0x00,
0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1,
0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0xf6, 0xd4, 0x61, 0x0e, 0xe7, 0x6c, 0x28, 0x1a, 0x40, 0x1b,
0x06, 0x94, 0x15, 0xb8, 0x36, 0x75, 0xd6, 0x24, 0xc8, 0x9b, 0x0d, 0xde, 0x46, 0xc4, 0x4d, 0x88, 0x8c, 0xb5, 0x17,
0xfd, 0x3a, 0x6b, 0x8b, 0xfc, 0x1a, 0xd8, 0x20, 0x32, 0xfa, 0x3d, 0xe4, 0xac, 0xa3, 0xc4, 0x8c, 0xce, 0xac, 0xbf,
0x42, 0x77, 0x63, 0x24, 0x8d, 0x33, 0x7f, 0xc1, 0x04, 0x61, 0x88, 0x6e, 0xae, 0x24, 0x52, 0x2f, 0xc6, 0xe7, 0x50,
0x37, 0x91, 0x5c, 0xb7, 0x6a, 0x69, 0xeb, 0xfc, 0x51, 0x70, 0x80, 0xa5, 0x77, 0x83, 0xe5, 0x3e, 0xba, 0x99, 0xfa,
0x5b, 0xfa, 0x44, 0xc2, 0x17, 0xe5, 0x7b, 0xab, 0xfe, 0x58, 0x07, 0x0a, 0x3e, 0x04, 0x21, 0x6c, 0x74, 0x03, 0x24,
0x15, 0x15, 0x1a, 0x63, 0xa3, 0xf5, 0xaf, 0x38, 0xf9, 0xf0, 0x3f, 0x1c, 0xf2, 0x2f, 0xba, 0x9e, 0x7d, 0xac, 0x24,
0x16, 0xc3, 0x93, 0x1e, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e,
0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x7c, 0x21,
0x29, 0xeb, 0xdd, 0xd9, 0xc2, 0xc2, 0x87, 0x30, 0x82, 0xe0, 0xe9, 0xa7, 0x35, 0x64, 0xc9, 0x67, 0xa9, 0xed, 0xd0,
0xc5, 0x91, 0x3b, 0xc9, 0xf2, 0xd4, 0xd9, 0xc5, 0x89, 0x91, 0x4f, 0xc3, 0xd3, 0xbe, 0x3f, 0xb1, 0x31, 0x98, 0x25,
0x25, 0x83, 0x24, 0xcd, 0x54, 0x99, 0xdb, 0x6f, 0xa7, 0x2d, 0x31, 0xc4, 0x53, 0xe1, 0x69, 0xa6, 0x35, 0xd5, 0x8d,
0x11, 0x70, 0xfa, 0x26, 0x1e, 0x28, 0xbd, 0xfe, 0x69, 0x57, 0x63, 0x6c, 0x33, 0xe6, 0xb6, 0x10, 0x41, 0xb8, 0xbe,
0x1f, 0xf6, 0x3a, 0xbe, 0xb5, 0x6a, 0x57, 0x66, 0xd0, 0xe4, 0x2a, 0x6b, 0xc3, 0xaa, 0x4f, 0xf2, 0xba, 0x5b, 0xd7,
0xbe, 0xb3, 0xcc, 0x01, 0x81, 0x30, 0xdc, 0x9a, 0xd6, 0xab, 0x6a, 0x87, 0x56, 0x69, 0x23, 0xef, 0x37, 0xac, 0xbc,
0xaf, 0xec, 0x35, 0xea, 0x74, 0xc9, 0xbf, 0x9d, 0x06, 0x76, 0xbc, 0x1d, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa,
0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8,
0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0xcb, 0xf6, 0x96, 0x28, 0xa7, 0x00, 0x6b, 0x60, 0x4b, 0xd1, 0x5a, 0x13, 0x03,
0xa3, 0xda, 0x7d, 0xb6, 0x2d, 0xaa, 0x2b, 0x12, 0xf8, 0x5b, 0x0c, 0x81, 0xb4, 0x61, 0x51, 0x55, 0x40, 0x5c, 0x5e,
0xb1, 0x5a, 0x71, 0xf9, 0x06, 0x13, 0xed, 0x2b, 0x89, 0xc0, 0x01, 0x55, 0xa8, 0xaa, 0x45, 0x36, 0xc8, 0x84, 0xfc,
0x78, 0x69, 0x40, 0x55, 0x89, 0x88, 0xbf, 0x02, 0xd3, 0xa7, 0xd9, 0xc5, 0x48, 0x6a, 0x73, 0xaa, 0x9b, 0x54, 0x06,
0x6c, 0x88, 0x2b, 0xef, 0x5a, 0x3f, 0x70, 0xbd, 0xb8, 0x38, 0x75, 0x9d, 0xe1, 0xf2, 0x8a, 0x8a, 0x69, 0x26, 0x6f,
0x07, 0xf1, 0x5b, 0x00, 0x0a, 0x28, 0x41, 0x3a, 0xb1, 0xf8, 0x8d, 0xaa, 0x7f, 0xd5, 0x90, 0x34, 0xdf, 0xc9, 0xa0,
0x59, 0x13, 0x36, 0x53, 0x28, 0x15, 0xed, 0x06, 0x7c, 0x1c, 0x2d, 0x43, 0xab, 0x50, 0x3c, 0xc9, 0x4b, 0x14, 0xda,
0x36, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5,
0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x7a, 0xc0, 0x5b, 0x5c, 0x19,
0x50, 0x23, 0xa3, 0x8b, 0x3d, 0x80, 0x09, 0x63, 0x38, 0x5e, 0x65, 0x16, 0xd0, 0xa3, 0xa3, 0xe7, 0x28, 0x5a, 0x5f,
0xa7, 0x0a, 0x4f, 0x0c, 0x34, 0x95, 0x26, 0x70, 0x85, 0x11, 0x97, 0x36, 0xa2, 0x76, 0x64, 0xd9, 0x7a, 0x8e, 0xa6,
0xa1, 0x51, 0x9a, 0x89, 0xda, 0x38, 0x46, 0xd0, 0x68, 0x30, 0xde, 0x70, 0xf8, 0x88, 0x7d, 0xa9, 0x19, 0x45, 0x62,
0xb0, 0x6f, 0xf3, 0xc4, 0xd7, 0xfd, 0x95, 0xb9, 0xd1, 0x1c, 0x7e, 0xb5, 0x58, 0xa6, 0x63, 0xaf, 0xcb, 0x4b, 0x52,
0x83, 0x85, 0x0e, 0xed, 0x33, 0xfa, 0xb3, 0x61, 0x90, 0x61, 0x68, 0xc2, 0xba, 0x54, 0x5d, 0x23, 0xc8, 0xfb, 0x0e,
0x7d, 0x7a, 0x8c, 0xa9, 0x09, 0x2a, 0x21, 0x4e, 0x23, 0x02, 0xf2, 0x0d, 0x84, 0xd1, 0xab, 0x8a, 0x13, 0x8c, 0x9a,
0x6a, 0x20, 0xd2, 0x09, 0x82, 0xfb, 0x1e, 0xe6, 0x17, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d,
0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37,
0x1a, 0x49, 0x4d, 0xd1, 0x0f, 0xb4, 0xbd, 0x0e, 0x68, 0x31, 0x1a, 0x30, 0xc8, 0x92, 0xd8, 0xaa, 0xcd, 0x04, 0x05,
0x87, 0x5c, 0x49, 0x68, 0xd2, 0xba, 0x3e, 0xb2, 0x09, 0xa3, 0xb0, 0x8a, 0x59, 0xcf, 0xd2, 0x4b, 0xeb, 0xf7, 0xaa,
0x35, 0xf6, 0x48, 0x57, 0x40, 0x6b, 0x0e, 0x26, 0x37, 0x2a, 0x91, 0x0c, 0xea, 0x3f, 0x26, 0x97, 0xc8, 0xc4, 0x4a,
0x18, 0xd3, 0x06, 0x12, 0x93, 0xa5, 0xad, 0x9a, 0x69, 0x6a, 0xc1, 0x4d, 0x2c, 0x31, 0x45, 0x03, 0x3a, 0x2e, 0x24,
0xd5, 0xd0, 0xdf, 0xeb, 0xdb, 0xdf, 0xd0, 0x6f, 0x3d, 0x14, 0xa8, 0x7a, 0x5f, 0x53, 0xe3, 0x9e, 0xb8, 0x68, 0x4d,
0x91, 0xe2, 0x0a, 0x48, 0x94, 0x73, 0xd9, 0x84, 0x9a, 0x55, 0x3e, 0x42, 0x99, 0xe7, 0x1e, 0x73, 0x64, 0xd4, 0x8d,
0x26, 0xf0, 0x16, 0x36, 0x08, 0x7b, 0xde, 0x8c, 0xe9, 0x0a, 0x27, 0x3a, 0x90, 0x6f, 0x6c, 0x90, 0x66, 0x01, 0x00,
0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0,
0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0xd1, 0x81, 0xc0, 0x8c, 0xc5, 0x6d, 0x18, 0x32,
0xd7, 0x82, 0x81, 0xb4, 0x0f, 0x0b, 0x34, 0x91, 0xe1, 0xec, 0x57, 0xf2, 0x17, 0xca, 0x56, 0x15, 0x1f, 0x7d, 0xa7,
0x27, 0x3a, 0xaf, 0xc3, 0x24, 0x0e, 0x26, 0x24, 0x5e, 0x31, 0x77, 0x45, 0xfb, 0x9c, 0x71, 0xaf, 0x19, 0x73, 0xd0,
0x33, 0x0b, 0x22, 0xd8, 0xde, 0xd0, 0x42, 0x79, 0xc0, 0x40, 0x23, 0x44, 0x1e, 0xa3, 0xdf, 0x65, 0x48, 0x50, 0x96,
0xdc, 0xc5, 0x98, 0x9b, 0x13, 0xa8, 0x29, 0x6e, 0x79, 0x02, 0xef, 0x50, 0xd0, 0xdf, 0x71, 0x29, 0xd3, 0xa4, 0x3a,
0xa4, 0x13, 0xc9, 0x0f, 0xa3, 0xff, 0x73, 0x25, 0xb0, 0xb8, 0xe0, 0x07, 0x02, 0xfd, 0xb5, 0x6f, 0xb4, 0x95, 0xc5,
0x49, 0x8b, 0x13, 0xb4, 0xb9, 0x4f, 0xba, 0xd3, 0x83, 0xc2, 0x8c, 0x7a, 0xbe, 0x9a, 0x0b, 0x7d, 0x03, 0x75, 0xf6,
0xbd, 0x84, 0xbd, 0xc0, 0xd4, 0x33, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57,
0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d,
0x60, 0x31, 0xd7, 0xab, 0x48, 0x21, 0x3b, 0x89, 0xc4, 0x3f, 0xe9, 0x30, 0xf6, 0xbc, 0x55, 0xf0, 0xcf, 0x49, 0x4d,
0xf3, 0x3f, 0x66, 0xbe, 0x39, 0x3c, 0xcd, 0x53, 0x36, 0xc6, 0xd6, 0x04, 0x62, 0x37, 0x64, 0x6e, 0x86, 0x83, 0x2f,
0x1a, 0xe3, 0xd8, 0xcb, 0x6b, 0xcc, 0x18, 0x8c, 0xbc, 0x89, 0x97, 0x69, 0xa7, 0xe9, 0x61, 0x1f, 0xe6, 0x92, 0x3e,
0x34, 0x7b, 0xfa, 0xee, 0x9d, 0xcb, 0x03, 0x26, 0x8e, 0xd5, 0xc7, 0x11, 0xfb, 0x18, 0xc6, 0xe0, 0xd0, 0x7e, 0xb7,
0x77, 0x2c, 0x6e, 0xc0, 0x48, 0x33, 0x34, 0x11, 0x1c, 0x7d, 0x55, 0xa5, 0xca, 0xb3, 0x2d, 0xc6, 0x06, 0x59, 0x9b,
0x27, 0x8a, 0x1a, 0xd6, 0xa4, 0x5c, 0x48, 0x9f, 0x72, 0x20, 0x69, 0xdc, 0xbd, 0xf0, 0xba, 0x39, 0x4c, 0x70, 0xa5,
0x78, 0xb5, 0x87, 0x9c, 0x00, 0xe0, 0xc8, 0xf1, 0x8c, 0x03, 0x3a, 0x2c, 0x1c, 0x2e, 0x01, 0x00, 0x00, 0x00, 0xaa,
0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc,
0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x83, 0x09, 0xd7, 0x49, 0xb5, 0x30, 0x90, 0x05, 0x98, 0x2a, 0x2f,
0x01, 0x25, 0x9f, 0x29, 0xf4, 0xa1, 0x30, 0x62, 0x62, 0x05, 0xbb, 0xa6, 0xda, 0x2f, 0x82, 0x41, 0xad, 0x2f, 0x4a,
0x49, 0x2f, 0x06, 0x35, 0xd8, 0x2f, 0x0c, 0xfa, 0xa5, 0x8c, 0x8e, 0xe7, 0x8a, 0x31, 0x83, 0x67, 0xf4, 0x34, 0xa2,
0xa2, 0x88, 0x6c, 0x71, 0xc7, 0xf1, 0x4c, 0xca, 0xba, 0x0d, 0x57, 0xc8, 0xef, 0x8f, 0x42, 0x9b, 0x2d, 0x86, 0x4a,
0x6a, 0x2c, 0xe7, 0x42, 0x56, 0xe5, 0x36, 0x46, 0xf6, 0xa6, 0x25, 0x4c, 0x83, 0xc1, 0x46, 0x19, 0x22, 0xf9, 0xcd,
0x19, 0x31, 0x55, 0xaa, 0x2b, 0xa5, 0x59, 0x78, 0x70, 0x90, 0x84, 0x93, 0x55, 0xb8, 0x46, 0x4d, 0x54, 0xaa, 0x13,
0x1e, 0x5f, 0x72, 0x9a, 0xb5, 0x05, 0x48, 0x28, 0x3d, 0x78, 0xaf, 0xd3, 0x25, 0x46, 0x9b, 0xd1, 0x06, 0x60, 0x74,
0x34, 0x4e, 0x38, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2,
0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x79, 0xc6, 0x8a,
0xe3, 0xae, 0xf7, 0xaf, 0x3a, 0xb6, 0xe4, 0xd3, 0xdd, 0x69, 0x6a, 0x24, 0x45, 0x6e, 0x16, 0x97, 0x1c, 0x3e, 0xb8,
0x34, 0x46, 0xf3, 0xd2, 0x73, 0x3f, 0x83, 0x89, 0xd2, 0x0c, 0x5a, 0x95, 0x79, 0x68, 0x8d, 0x99, 0x66, 0xad, 0xfc,
0x2d, 0x15, 0xfb, 0x2b, 0xce, 0x6d, 0x15, 0x95, 0x82, 0x9d, 0xae, 0x31, 0x31, 0x3b, 0xd5, 0x7a, 0xe3, 0x66, 0x40,
0x34, 0x8b, 0xc0, 0x2f, 0x94, 0x52, 0x55, 0x33, 0xce, 0x37, 0x27, 0xe3, 0x35, 0x3f, 0x63, 0x58, 0x7f, 0x92, 0x2a,
0x4e, 0xbd, 0x43, 0x10, 0x6e, 0xc6, 0xc3, 0x86, 0x31, 0xd8, 0xb8, 0xe0, 0x39, 0x48, 0xf1, 0xa0, 0x49, 0xec, 0x14,
0x25, 0x1b, 0xf1, 0x2d, 0x6f, 0x1a, 0x96, 0xb2, 0x0c, 0x08, 0x86, 0x9b, 0x9f, 0xfa, 0xe5, 0x1a, 0x00, 0xb6, 0x54,
0x35, 0xcd, 0x4a, 0xb2, 0x93, 0x6e, 0x09, 0xb4, 0xb1, 0x61, 0x4c, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa,
0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13,
0x71, 0x37, 0x1a, 0x49, 0x4d, 0xdb, 0x04, 0x30, 0x03, 0xcb, 0xf5, 0x5e, 0xe4, 0xbc, 0x0e, 0xcf, 0x6f, 0x40, 0x4d,
0xfa, 0x18, 0x32, 0x71, 0x77, 0x86, 0x99, 0x1d, 0xb7, 0xb6, 0x34, 0x8a, 0x42, 0x0e, 0x08, 0x86, 0x14, 0x50, 0x2e,
0x67, 0x22, 0x47, 0x1e, 0x39, 0xfb, 0x7d, 0x35, 0xc4, 0x12, 0x18, 0x0e, 0x30, 0x86, 0x19, 0xfb, 0x76, 0x16, 0x19,
0x6d, 0x8c, 0x75, 0x95, 0x23, 0x69, 0x48, 0x28, 0xfd, 0x9b, 0x0b, 0x64, 0x91, 0xe6, 0x92, 0xd3, 0x1a, 0x8d, 0x5f,
0x08, 0xa9, 0xee, 0x34, 0x8b, 0xe2, 0x71, 0x86, 0x8c, 0xf8, 0xa8, 0x27, 0x08, 0xd1, 0x00, 0x12, 0x77, 0xce, 0x0b,
0xae, 0x05, 0x38, 0x38, 0x3b, 0x6f, 0xc8, 0xda, 0x82, 0x9c, 0x50, 0x72, 0x45, 0xaf, 0xad, 0x71, 0x4a, 0x6b, 0x19,
0x6b, 0x7c, 0x1e, 0x6e, 0xe8, 0x87, 0xaa, 0x9b, 0xe5, 0x38, 0x9a, 0x22, 0x19, 0xd2, 0x9a, 0x94, 0x15, 0x70, 0x4c,
0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b,
0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0xd6, 0xbc, 0xf5, 0x83, 0xf1, 0xfe,
0xde, 0xf5, 0xd8, 0xae, 0xf8, 0xb3, 0x54, 0x9d, 0x69, 0x55, 0x8b, 0x2b, 0xa9, 0x5e, 0x78, 0xaf, 0x24, 0x05, 0x58,
0x70, 0x69, 0xcd, 0x88, 0xc4, 0x0f, 0x4f, 0x68, 0xc6, 0x43, 0x2f, 0xa6, 0x92, 0xea, 0x6e, 0xb9, 0x77, 0x74, 0xae,
0x8c, 0xfd, 0x9f, 0x79, 0xc4, 0xe1, 0x7a, 0x07, 0x6c, 0x38, 0x40, 0xdd, 0xf9, 0x1c, 0x6d, 0x19, 0xc8, 0xf1, 0xe0,
0x18, 0xc2, 0xa5, 0xf2, 0x5f, 0xde, 0x70, 0x37, 0x1c, 0x82, 0x56, 0x5e, 0xde, 0x09, 0x70, 0x48, 0xad, 0xb8, 0x73,
0xe7, 0x90, 0x36, 0x88, 0x4d, 0x68, 0x32, 0x0b, 0x1d, 0x77, 0x71, 0x9a, 0x21, 0x1c, 0x12, 0x3a, 0x4e, 0x82, 0x34,
0xc7, 0xfa, 0xa9, 0x2b, 0x10, 0xa1, 0x6b, 0x9b, 0x11, 0xdb, 0x82, 0x42, 0x91, 0x02, 0x88, 0xe4, 0xba, 0x5f, 0x57,
0xd9, 0xac, 0x30, 0x98, 0x05, 0xa8, 0x2c, 0x4d, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54,
0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a,
0x49, 0x4d, 0xc5, 0xb5, 0x00, 0xde, 0x7e, 0x5c, 0xfe, 0x18, 0xec, 0xaa, 0x55, 0x85, 0xcb, 0x66, 0x55, 0x52, 0xad,
0xcb, 0x74, 0x28, 0x93, 0x85, 0xd8, 0x94, 0x97, 0x4d, 0x64, 0x6e, 0xd8, 0xfe, 0x99, 0x3a, 0x57, 0x1d, 0x51, 0xc1,
0xea, 0xdd, 0xd2, 0x38, 0xdd, 0x3d, 0xba, 0x2a, 0x71, 0x96, 0xa2, 0x97, 0xa6, 0x00, 0xc9, 0xc6, 0x65, 0xbb, 0xc6,
0x27, 0xd6, 0x04, 0x49, 0x3a, 0x5c, 0xb3, 0xb0, 0x05, 0x05, 0xea, 0x70, 0x92, 0xe7, 0xf2, 0x43, 0x67, 0x49, 0x6b,
0x96, 0x0f, 0x95, 0x7a, 0x15, 0x3a, 0x4f, 0x0f, 0xf5, 0xf6, 0xf1, 0x51, 0xe7, 0x12, 0x83, 0x8d, 0x2a, 0xad, 0xb3,
0x29, 0x0b, 0x66, 0x20, 0x30, 0xf5, 0x8f, 0xb9, 0x81, 0x61, 0x2d, 0xb4, 0x44, 0xe7, 0x89, 0x89, 0x1b, 0x4b, 0xb8,
0x90, 0x2d, 0x54, 0xa7, 0x8e, 0x58, 0x6c, 0x3c, 0xe1, 0x31, 0xba, 0x3e, 0xbf, 0xe2, 0xb4, 0x5c, 0x01, 0x00, 0x00,
0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a,
0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x72, 0xdc, 0xc0, 0xee, 0x8c, 0x48, 0x9a, 0x30, 0x4b,
0x5f, 0xfc, 0x54, 0xe7, 0x0b, 0xc7, 0x1d, 0xfc, 0x66, 0x27, 0x13, 0x6d, 0x4d, 0x0c, 0x15, 0x76, 0x71, 0x51, 0xf3,
0x25, 0xfd, 0x2c, 0x37, 0xf7, 0xc0, 0xce, 0xcc, 0xa3, 0x90, 0x0b, 0xbd, 0x5e, 0x6e, 0x09, 0x8a, 0xde, 0x6f, 0x9c,
0x6d, 0xdc, 0xf1, 0xeb, 0x6b, 0xd0, 0x3d, 0x38, 0x8c, 0xd0, 0xbf, 0xaf, 0xbf, 0xe2, 0xb3, 0x95, 0x4f, 0x4e, 0x11,
0x43, 0xe0, 0x90, 0x60, 0xca, 0x61, 0xc5, 0xab, 0xfc, 0xd5, 0x19, 0x02, 0xe6, 0xee, 0x32, 0xd3, 0x93, 0xc1, 0x6c,
0x69, 0x07, 0xba, 0x37, 0x7a, 0x55, 0xed, 0xb3, 0xda, 0xbf, 0x3a, 0x56, 0x67, 0xbb, 0x94, 0xc3, 0x70, 0x3e, 0xf6,
0x35, 0xdf, 0x5d, 0xf6, 0xec, 0x4c, 0x76, 0x88, 0x22, 0xcb, 0xb8, 0x87, 0x6f, 0x73, 0x48, 0xc5, 0xae, 0x2f, 0x89,
0x18, 0xab, 0x89, 0x0c, 0x4e, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d,
0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x62,
0x6e, 0xaf, 0x1e, 0xc0, 0xc8, 0xa0, 0x0f, 0x41, 0x9c, 0x9a, 0x41, 0x06, 0xeb, 0x2e, 0xe5, 0x1a, 0xf4, 0x0d, 0x48,
0x40, 0x95, 0x0e, 0xb0, 0xbb, 0xc3, 0x0c, 0x66, 0x7a, 0xd9, 0xb4, 0x0c, 0x5b, 0x03, 0x93, 0xa6, 0xa8, 0x9a, 0xea,
0x96, 0x44, 0xcb, 0x12, 0xae, 0x40, 0x60, 0x03, 0xfc, 0xb7, 0x1b, 0x2d, 0xa0, 0x12, 0xd3, 0x30, 0x74, 0x66, 0xcc,
0xa4, 0xfa, 0xca, 0x5b, 0x20, 0x25, 0x7f, 0x66, 0x6d, 0xad, 0x3a, 0x65, 0x13, 0xc3, 0x51, 0x00, 0x54, 0x5c, 0x61,
0x0c, 0x76, 0xb7, 0x8b, 0xe6, 0x97, 0xb1, 0x94, 0x78, 0x4d, 0x2c, 0x33, 0xc7, 0xf0, 0x09, 0xba, 0x2d, 0xf5, 0x60,
0x6d, 0x86, 0x75, 0x71, 0xed, 0xc9, 0x73, 0x2d, 0x73, 0x2d, 0x8a, 0xfb, 0x53, 0xa3, 0x1a, 0xc0, 0xe6, 0x8c, 0xd0,
0x6f, 0x98, 0xfc, 0x00, 0xfe, 0x8e, 0xd9, 0x2a, 0x39, 0x5c, 0xef, 0x79, 0x1b, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa,
0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda,
0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x1b, 0x0e, 0xb3, 0x6e, 0xec, 0x41, 0x47, 0x9c, 0x86, 0x65, 0xba, 0x43,
0xfd, 0xef, 0xb4, 0x42, 0xca, 0x96, 0x39, 0xec, 0x62, 0x22, 0x73, 0xf7, 0xed, 0xd3, 0x27, 0xef, 0x57, 0x7f, 0x9b,
0x61, 0x98, 0x00, 0x63, 0xbb, 0x4f, 0x68, 0x42, 0x1e, 0xe3, 0xea, 0x08, 0xf3, 0xa4, 0xe2, 0x9e, 0x71, 0x0a, 0x45,
0x90, 0x7f, 0x93, 0x18, 0x1d, 0x1e, 0xf1, 0x47, 0x79, 0x91, 0xe4, 0x73, 0xbf, 0x5f, 0x7e, 0xcb, 0x83, 0xde, 0xde,
0x88, 0xa7, 0xe0, 0x65, 0x01, 0x5b, 0x94, 0x64, 0xa8, 0x12, 0xb3, 0xde, 0x22, 0x82, 0x62, 0x5d, 0x30, 0x11, 0xc7,
0x7a, 0xa8, 0xa0, 0xd6, 0xab, 0x70, 0x88, 0x59, 0x92, 0x3f, 0xf2, 0x6b, 0x25, 0xbb, 0x11, 0xb8, 0xda, 0x29, 0x5e,
0xde, 0x29, 0xf0, 0x50, 0x8d, 0xf2, 0x76, 0x05, 0xc0, 0xb8, 0x9f, 0xa1, 0xe7, 0x7e, 0x85, 0x07, 0xfa, 0xda, 0xed,
0x97, 0x0a, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58,
0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x0e, 0xa5, 0x09, 0x60,
0x5e, 0xeb, 0xb2, 0x0a, 0xda, 0x15, 0x84, 0xad, 0xd7, 0xf2, 0x0e, 0x1a, 0xce, 0x93, 0xea, 0xfa, 0x1d, 0xf5, 0xfb,
0x2d, 0x45, 0x02, 0x26, 0x89, 0xf5, 0x4a, 0x1f, 0x4a, 0x4b, 0x39, 0xfb, 0xdd, 0x38, 0xef, 0x39, 0x55, 0x55, 0xdf,
0xc4, 0x5e, 0x23, 0x83, 0x02, 0x48, 0x60, 0xf7, 0x1b, 0xcc, 0xe8, 0x6f, 0x86, 0xdf, 0x0e, 0xcf, 0xa1, 0x3d, 0x6e,
0xd4, 0xb4, 0x20, 0x5d, 0xee, 0xf2, 0xe7, 0x78, 0xb8, 0x40, 0xf7, 0xad, 0xe2, 0x81, 0xe7, 0xfa, 0x26, 0x92, 0x0f,
0x24, 0x87, 0xc9, 0x44, 0x5f, 0x90, 0x9d, 0x25, 0xaa, 0xb7, 0x95, 0x76, 0x89, 0x53, 0xd5, 0x5f, 0xe8, 0xa6, 0x59,
0xba, 0x77, 0xf1, 0x05, 0xe8, 0x2e, 0x5d, 0x24, 0x31, 0x59, 0xf8, 0x13, 0x02, 0xfe, 0x9e, 0x22, 0x7b, 0xa7, 0xb1,
0xa1, 0xba, 0xb0, 0xb7, 0xbd, 0xd1, 0x12, 0x38, 0x99, 0x61, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54,
0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71,
0x37, 0x1a, 0x49, 0x4d, 0xb2, 0xcc, 0xab, 0x2f, 0x02, 0xe8, 0x24, 0xdd, 0x52, 0xf7, 0xe4, 0x98, 0x90, 0x56, 0x66,
0x7c, 0xef, 0x7c, 0x42, 0xf3, 0x4a, 0x71, 0x47, 0x18, 0x17, 0x8c, 0x37, 0xac, 0x03, 0xde, 0xf7, 0x55, 0x75, 0xd4,
0x7a, 0x60, 0x4e, 0xc3, 0xd0, 0xc4, 0xd4, 0x29, 0xd6, 0xc2, 0x5a, 0x10, 0x42, 0x98, 0x1f, 0xcd, 0x91, 0xe2, 0xe5,
0xc3, 0x62, 0x26, 0x01, 0xda, 0x4b, 0xde, 0xf2, 0x37, 0xab, 0x37, 0x30, 0xaf, 0x44, 0x39, 0xb0, 0xa0, 0xa1, 0x84,
0xbb, 0x01, 0x51, 0x77, 0x94, 0x9f, 0x1f, 0x67, 0x92, 0x39, 0x00, 0x69, 0x7a, 0x68, 0x79, 0xf4, 0x8e, 0x28, 0xde,
0xe0, 0x4f, 0x0a, 0x4d, 0x00, 0xf0, 0xc3, 0xf8, 0x2a, 0xd3, 0x5e, 0xc2, 0x92, 0x9d, 0x1f, 0x7e, 0x77, 0x88, 0x25,
0x9e, 0xd7, 0xaa, 0x95, 0xbd, 0x5a, 0x85, 0x61, 0x54, 0x41, 0xad, 0x8f, 0x8f, 0x02, 0x6b, 0xd5, 0x8d, 0x40, 0x01,
0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06,
0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0xc0, 0xb2, 0x14, 0x11, 0x7c, 0xd7, 0xd4,
0x99, 0xa2, 0xd1, 0x3f, 0xf2, 0xf9, 0x9c, 0x7e, 0xd9, 0x72, 0x9b, 0x8b, 0x73, 0xeb, 0x6c, 0x2b, 0xc0, 0x16, 0x3b,
0x8d, 0xa3, 0x36, 0x95, 0xfa, 0x44, 0xd9, 0xd8, 0x58, 0xfd, 0x23, 0x67, 0x7f, 0xa2, 0xc4, 0x67, 0x69, 0xbb, 0x18,
0x52, 0xc8, 0x38, 0xef, 0xad, 0x36, 0x79, 0x0e, 0x43, 0x17, 0x87, 0x3d, 0x1e, 0x6e, 0xf7, 0x06, 0xa5, 0xc2, 0x10,
0x55, 0x73, 0x3a, 0x04, 0x3a, 0x32, 0x33, 0xde, 0x21, 0x54, 0xbf, 0xde, 0xd0, 0x5f, 0x2d, 0xe8, 0x3a, 0x6f, 0x9b,
0xcb, 0x59, 0x32, 0x95, 0xb7, 0x63, 0xea, 0x6a, 0x07, 0x64, 0xa7, 0x6f, 0x3d, 0x55, 0x2a, 0x89, 0x52, 0xda, 0x87,
0xbf, 0xaa, 0xd4, 0xbf, 0x97, 0xc0, 0xea, 0xfc, 0xc3, 0x2f, 0x2f, 0xcf, 0x8f, 0xf5, 0x7d, 0xfe, 0x0f, 0xf3, 0x13,
0x23, 0x91, 0x76, 0xa8, 0xc5, 0x61, 0x5a, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55,
0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49,
0x4d, 0x5c, 0xbf, 0x5f, 0xe9, 0x15, 0x89, 0xfb, 0xdb, 0xaf, 0x98, 0x7b, 0x9c, 0x9d, 0x4f, 0x11, 0xba, 0xaf, 0x71,
0x71, 0xc8, 0x09, 0x4e, 0xaa, 0xbe, 0x20, 0x14, 0x24, 0xc8, 0x5d, 0xa1, 0x18, 0x3b, 0xf6, 0x48, 0xd9, 0x1a, 0x75,
0x26, 0x54, 0xcf, 0xe7, 0xbe, 0xab, 0x24, 0x8f, 0x0c, 0x9c, 0xca, 0x23, 0x33, 0xb6, 0xd6, 0x42, 0x65, 0x37, 0x5f,
0x35, 0xe9, 0x06, 0xe3, 0x0f, 0xb6, 0x73, 0x1e, 0x4f, 0x5e, 0x94, 0x44, 0x6e, 0xdf, 0xe1, 0x2a, 0x17, 0x97, 0x9a,
0xa1, 0x19, 0x1b, 0x3f, 0xa3, 0x25, 0x7a, 0xf1, 0x51, 0xfa, 0xdd, 0x5f, 0x80, 0x35, 0xcc, 0x90, 0x2b, 0x8e, 0xa7,
0x5b, 0x4d, 0x9d, 0x0f, 0x13, 0xc7, 0xa6, 0x87, 0x8a, 0xce, 0xe4, 0x45, 0xf9, 0xdc, 0xbf, 0xe8, 0xbc, 0xc1, 0x5b,
0xfa, 0x51, 0x81, 0xa9, 0x7b, 0x26, 0xa9, 0xdd, 0xa8, 0xdf, 0xcf, 0x29, 0xb3, 0xbe, 0x14, 0x01, 0x00, 0x00, 0x00,
0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1,
0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x4d, 0x90, 0x6a, 0xf7, 0xb0, 0xff, 0x6a, 0xac, 0xaf, 0x33,
0xf0, 0x2d, 0x6c, 0x58, 0x3e, 0x8d, 0x45, 0x5f, 0x8b, 0xd9, 0x08, 0x69, 0x0e, 0x8d, 0x4f, 0x07, 0x87, 0x5b, 0xdb,
0x17, 0xb9, 0x4e, 0xcb, 0x48, 0x41, 0x0c, 0x88, 0xa8, 0x23, 0x79, 0x4a, 0x9d, 0x9a, 0x31, 0x72, 0xe3, 0x1b, 0xd4,
0x4d, 0xc5, 0xb1, 0x90, 0xdb, 0x9b, 0xef, 0x03, 0xff, 0x11, 0x42, 0x29, 0xb6, 0xc6, 0xc2, 0x1e, 0x70, 0x80, 0xaf,
0xea, 0x0a, 0xd3, 0x2d, 0xb9, 0xea, 0x6e, 0xbd, 0x5c, 0xac, 0x97, 0xa9, 0x08, 0x7d, 0x51, 0xb3, 0x72, 0x62, 0x70,
0x4f, 0x19, 0xa9, 0xee, 0xe4, 0xc7, 0x06, 0x3f, 0xe3, 0x3d, 0x1e, 0x02, 0x0e, 0xb5, 0x57, 0x3f, 0x14, 0x23, 0xda,
0x5b, 0xf3, 0xf3, 0xf6, 0x62, 0x4e, 0x33, 0x72, 0x41, 0x4d, 0xb1, 0x87, 0xa6, 0xee, 0x68, 0x62, 0xb1, 0x71, 0x7e,
0xb8, 0xa9, 0xa7, 0x6f, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e,
0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0xa9, 0x53,
0x42, 0x6f, 0xe4, 0x79, 0xbd, 0xc7, 0x37, 0x51, 0xc9, 0xe6, 0xfe, 0x9b, 0x63, 0x03, 0x25, 0xdb, 0xb6, 0xf9, 0xdb,
0x80, 0x91, 0x01, 0x3f, 0xdd, 0xee, 0xe9, 0x9e, 0x60, 0xa6, 0x37, 0x43, 0x16, 0x9b, 0x92, 0x8b, 0xf9, 0xd9, 0x21,
0xa7, 0xf9, 0x05, 0x21, 0x00, 0xaa, 0x35, 0x9c, 0x08, 0xa5, 0x66, 0xba, 0xcb, 0xe3, 0x45, 0xfd, 0x8e, 0xbb, 0x5e,
0x97, 0x2a, 0xf9, 0x99, 0x6c, 0x8f, 0x92, 0xe8, 0x7d, 0x4f, 0x6d, 0x9c, 0x6d, 0xae, 0x17, 0xe1, 0x16, 0xde, 0x03,
0x35, 0x01, 0x76, 0xe6, 0x5a, 0x3b, 0x45, 0xb5, 0x43, 0x21, 0x59, 0xa9, 0x87, 0x38, 0x2f, 0x07, 0x94, 0x60, 0x26,
0xb9, 0xd2, 0xb5, 0xe3, 0x3b, 0x57, 0xa0, 0xb0, 0xb6, 0xfe, 0x10, 0x2e, 0xb6, 0xbd, 0xdf, 0x24, 0xe1, 0xc3, 0x8c,
0xbb, 0x08, 0x62, 0x84, 0x7e, 0x37, 0x25, 0x5c, 0xd1, 0x65, 0xf2, 0x45, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa,
0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8,
0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x4f, 0x72, 0x7c, 0xfb, 0xb3, 0x7d, 0x0e, 0xa5, 0x2f, 0x39, 0x76, 0x80, 0xff,
0x08, 0xac, 0xe7, 0x21, 0x18, 0x3f, 0x03, 0xab, 0xb2, 0xde, 0x9c, 0x83, 0x47, 0xa7, 0x90, 0x1e, 0x2c, 0xa3, 0x11,
0x14, 0x4e, 0x27, 0x52, 0x70, 0x56, 0xa6, 0x87, 0x1a, 0x45, 0x52, 0xf4, 0x9f, 0xed, 0x73, 0xb2, 0xa2, 0x01, 0x55,
0xbd, 0x6a, 0x32, 0xd4, 0x15, 0x2e, 0x7e, 0x05, 0xbb, 0xb5, 0x27, 0x46, 0x58, 0x49, 0x2b, 0xab, 0x60, 0x30, 0xbb,
0x6d, 0x8a, 0xd8, 0x85, 0x78, 0x25, 0x19, 0x8a, 0x9d, 0xc9, 0x3e, 0xf2, 0xb0, 0x65, 0x1b, 0xe2, 0xfd, 0x30, 0x66,
0x4b, 0xb5, 0x6b, 0xfd, 0x7e, 0xe3, 0x1c, 0x31, 0x08, 0xa6, 0xb2, 0xda, 0x48, 0x8d, 0xeb, 0xb0, 0xac, 0xf0, 0xf5,
0x6e, 0xe5, 0x4b, 0xc7, 0xf9, 0xfb, 0xca, 0xbd, 0x8c, 0x4e, 0x28, 0xa1, 0xe9, 0xf6, 0x7f, 0x12, 0xd3, 0x5d, 0x88,
0x0b, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5,
0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x8b, 0x4e, 0xfd, 0x3f, 0x25,
0xc1, 0xc0, 0x59, 0x5a, 0xd5, 0x46, 0xa0, 0x79, 0x62, 0x4e, 0x29, 0xc6, 0x1f, 0x75, 0x8a, 0x8c, 0x82, 0x5e, 0x15,
0x72, 0xd5, 0x58, 0x66, 0x81, 0x74, 0x9c, 0x40, 0x1d, 0x98, 0x69, 0x66, 0xf6, 0xcb, 0x81, 0x16, 0xd4, 0xa4, 0xbc,
0x13, 0xd6, 0x85, 0x54, 0x5d, 0xb3, 0x1b, 0x28, 0xa3, 0x56, 0x36, 0x46, 0xf6, 0xc2, 0x98, 0x24, 0xbb, 0x35, 0xfe,
0xa2, 0x6a, 0x0b, 0xb7, 0x27, 0x6c, 0xb4, 0xda, 0x41, 0x1b, 0x37, 0x2f, 0x76, 0x99, 0x63, 0x28, 0x79, 0xc0, 0xc5,
0x38, 0x5d, 0xf2, 0x16, 0x23, 0x40, 0xa5, 0xb7, 0x36, 0x5a, 0xd1, 0x91, 0x4a, 0xbe, 0x6f, 0x76, 0x8e, 0x06, 0x8f,
0x7c, 0x29, 0x10, 0x51, 0x6a, 0x42, 0xde, 0x5b, 0xed, 0x80, 0x38, 0xf4, 0xf3, 0xf2, 0x5c, 0x76, 0xf2, 0x78, 0xbd,
0x0d, 0x0c, 0xe8, 0x78, 0x68, 0xfc, 0x7c, 0x9c, 0x55, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d,
0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37,
0x1a, 0x49, 0x4d, 0x65, 0xfe, 0xff, 0xbf, 0x05, 0x2f, 0x3c, 0x1e, 0x02, 0xfa, 0x35, 0x81, 0xef, 0x42, 0xb9, 0xa8,
0x5f, 0x6d, 0x50, 0xaf, 0x80, 0x74, 0xb5, 0x76, 0xad, 0xd0, 0x5a, 0xc0, 0x44, 0xb0, 0x49, 0x2a, 0x1c, 0x35, 0x33,
0x93, 0x5f, 0x00, 0x08, 0x49, 0x94, 0xb7, 0xab, 0x66, 0xd3, 0xd7, 0xcc, 0xfe, 0x68, 0x9e, 0xf0, 0xae, 0x7e, 0x26,
0x1d, 0x4a, 0x85, 0xf7, 0x0c, 0xaa, 0xd6, 0x4f, 0x0c, 0x6a, 0xde, 0xd6, 0x8b, 0xb8, 0xc0, 0xbe, 0x0b, 0xab, 0x9a,
0x3f, 0xe4, 0x8a, 0x1c, 0x1b, 0x81, 0x1b, 0x8d, 0x6e, 0xeb, 0xa3, 0xac, 0x44, 0x9a, 0x51, 0x29, 0x50, 0x2d, 0x93,
0xa0, 0x23, 0xb8, 0x30, 0x33, 0x15, 0xe0, 0x11, 0x8b, 0x6e, 0xe3, 0x6f, 0x62, 0xc1, 0xba, 0x8b, 0x25, 0xac, 0xd3,
0x13, 0x47, 0xee, 0xc5, 0x56, 0x62, 0x9e, 0xbd, 0x5e, 0x03, 0xdb, 0x78, 0xde, 0x5e, 0xf9, 0xb9, 0x4e, 0x01, 0x00,
0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0,
0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x00, 0x00, 0x00, 0xd0, 0x87, 0x1a, 0x49, 0x23,
0x54, 0x58, 0x48, 0x00, 0x80, 0x87, 0x16, 0x01, 0xa0, 0xbd, 0x8c, 0x8b, 0x01, 0x1a, 0xdc, 0xfc, 0x0f, 0xe2, 0x98,
0xa5, 0x10, 0x78, 0x25, 0x6f, 0x9b, 0x99, 0x99, 0x59, 0x82, 0xef, 0x3b, 0xc0, 0xa7, 0xd6, 0x7c, 0xb6, 0x5e, 0xe0,
0xd5, 0x9b, 0x31, 0x32, 0xf2, 0xf9, 0xde, 0x89, 0xa6, 0x89, 0x90, 0x6d, 0xa8, 0x10, 0xb0, 0x71, 0x7a, 0x1e, 0x85,
0xb0, 0x81, 0x18, 0x2a, 0x71, 0xb1, 0x7b, 0xbd, 0x29, 0xdc, 0x08, 0x0e, 0xc6, 0xc5, 0xfd, 0x4d, 0xce, 0x87, 0x98,
0xd2, 0xe6, 0x0f, 0x11, 0x2f, 0x7e, 0xf0, 0xe0, 0xd8, 0x47, 0x1d, 0x04, 0xc6, 0x9b, 0xde, 0x35, 0x20, 0x57, 0x3e,
0xd4, 0x57, 0xb8, 0x20, 0x9d, 0x95, 0xae, 0xb6, 0x21, 0x82, 0xac, 0xde, 0xdd, 0x9b, 0x60, 0x94, 0xe9, 0x4b, 0xb4,
0x59, 0xef, 0x06, 0xa7, 0xe4, 0x65, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57,
0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d,
0x00, 0x00, 0x00, 0x30, 0xc2, 0x35, 0xb2, 0xc0, 0xe8, 0x0e, 0x8a, 0xaf, 0x28, 0x09, 0x1e, 0x7e, 0x9e, 0x37, 0x96,
0x87, 0x92, 0x36, 0x29, 0xd7, 0x70, 0xd5, 0x84, 0xef, 0xb7, 0x50, 0x42, 0x00, 0xcd, 0xcc, 0xcc, 0x04, 0x5a, 0x14,
0xea, 0x45, 0xb8, 0x82, 0x05, 0x94, 0x10, 0x12, 0xd3, 0x2e, 0x7d, 0x94, 0x1b, 0x04, 0x9a, 0x3c, 0x6d, 0xa5, 0x6c,
0xbb, 0xbd, 0x4e, 0x34, 0xe1, 0xe0, 0x16, 0xcb, 0x55, 0x94, 0x0a, 0x7a, 0x18, 0xa0, 0xdd, 0x7b, 0x74, 0xd0, 0x54,
0x3c, 0xb9, 0xcd, 0x54, 0xd0, 0x97, 0xfd, 0xfa, 0xd5, 0x19, 0xe9, 0x34, 0x4e, 0x6c, 0xa1, 0xea, 0xf3, 0x7f, 0x31,
0x5e, 0xf8, 0x88, 0xbe, 0xec, 0x02, 0xf0, 0xce, 0x75, 0xdb, 0x51, 0xfa, 0x68, 0x41, 0x49, 0x10, 0x03, 0xab, 0xff,
0x7f, 0x3a, 0xf5, 0x29, 0xa9, 0x1c, 0xc3, 0x3d, 0x5f, 0x4d, 0x66, 0x9b, 0x65, 0x04, 0x01, 0x00, 0x00, 0x00, 0xaa,
0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc,
0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x00, 0x00, 0x00, 0x24, 0x7a, 0x63, 0x86, 0x07, 0x09, 0xeb, 0x71,
0x66, 0xa4, 0x43, 0x09, 0xeb, 0xbc, 0x2d, 0x8c, 0x2c, 0xae, 0xeb, 0x7e, 0xde, 0xbf, 0x45, 0x0a, 0x22, 0x91, 0x27,
0x00, 0x00, 0x67, 0x66, 0x66, 0x76, 0xa0, 0x3c, 0xc5, 0xd4, 0xd2, 0xcf, 0xd3, 0x0d, 0x97, 0x92, 0x52, 0x7c, 0xf8,
0x5c, 0x73, 0x4d, 0xc5, 0x31, 0x62, 0x40, 0x76, 0xe7, 0xb5, 0xfb, 0xaa, 0x68, 0x8e, 0x45, 0xc2, 0xf6, 0xff, 0x3b,
0xf3, 0x53, 0xd0, 0x30, 0x9b, 0x29, 0x1d, 0x86, 0x25, 0xbd, 0x0f, 0xf9, 0x53, 0x91, 0x10, 0x4c, 0xf5, 0x4a, 0xf3,
0x13, 0x04, 0xf4, 0x79, 0x12, 0xc1, 0x4a, 0x58, 0x21, 0x3c, 0x74, 0xe0, 0x5d, 0x22, 0x2e, 0x60, 0x2a, 0xca, 0x3d,
0x5f, 0xc2, 0xcb, 0xf2, 0x47, 0x71, 0xd0, 0xd6, 0x63, 0xca, 0xf3, 0xd0, 0xe8, 0x39, 0x99, 0x3b, 0xff, 0x06, 0x3e,
0xe5, 0xcd, 0x6b, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2,
0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x00, 0x00, 0x00,
0xac, 0x99, 0x37, 0xb3, 0x60, 0xfa, 0x50, 0xcb, 0x16, 0x8f, 0x95, 0x6b, 0xcc, 0x70, 0x69, 0x74, 0x2e, 0x07, 0xd5,
0x81, 0x98, 0xee, 0x16, 0x88, 0x9b, 0x17, 0x00, 0x00, 0x00, 0x34, 0x33, 0x33, 0xa9, 0x30, 0x56, 0xe3, 0x6e, 0x00,
0xe2, 0x23, 0x99, 0x51, 0x52, 0x76, 0xe2, 0x4c, 0x35, 0xcc, 0xf4, 0xbe, 0x8b, 0x0f, 0xdf, 0xbf, 0xaa, 0x2a, 0xbb,
0x59, 0x1f, 0xbe, 0x5c, 0xc0, 0x27, 0xda, 0x2d, 0x45, 0x2e, 0x95, 0x8f, 0xb7, 0x50, 0x74, 0xd9, 0xec, 0x14, 0xb8,
0x4c, 0x0d, 0x7b, 0xc9, 0xe0, 0x02, 0xd1, 0x9e, 0x56, 0xc9, 0x27, 0xf7, 0xa8, 0xa6, 0xa1, 0xdc, 0x38, 0x3f, 0xff,
0xf9, 0x30, 0xfa, 0x8c, 0xc8, 0xab, 0x60, 0x86, 0xf0, 0x52, 0xc4, 0x01, 0xc2, 0x9c, 0xb3, 0x42, 0x51, 0x52, 0x54,
0x35, 0x83, 0x05, 0xb2, 0xd5, 0xc6, 0x9b, 0x7a, 0x43, 0xa6, 0x6c, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa,
0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13,
0x71, 0x37, 0x1a, 0x49, 0x4d, 0x00, 0x00, 0x00, 0xa5, 0xd1, 0xee, 0x7c, 0xd0, 0xba, 0xcb, 0x9d, 0x26, 0x7f, 0xd1,
0xed, 0xba, 0xb3, 0x89, 0xf0, 0x52, 0xc2, 0x4f, 0x0c, 0xf0, 0x23, 0xdd, 0x15, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x9a,
0x99, 0x99, 0xe5, 0x87, 0xd2, 0xdc, 0x99, 0xaf, 0xaf, 0x6f, 0xca, 0x7f, 0x10, 0x1c, 0x8b, 0x6e, 0xb8, 0xd8, 0xc5,
0x9e, 0xd0, 0x03, 0xce, 0x57, 0x95, 0xbd, 0xcc, 0xba, 0x0f, 0x5f, 0x2e, 0xbf, 0x91, 0x50, 0x10, 0xfb, 0xe2, 0x60,
0x0a, 0x57, 0xb1, 0xc7, 0xdb, 0xc3, 0x18, 0x08, 0x02, 0x48, 0xc8, 0xea, 0xa0, 0x46, 0x32, 0xaa, 0x31, 0x46, 0x05,
0xbb, 0xbc, 0x03, 0x8d, 0xee, 0x36, 0xff, 0xa5, 0x22, 0x99, 0x0d, 0xd2, 0x0a, 0x51, 0x1a, 0x45, 0x34, 0x44, 0x68,
0x5d, 0x72, 0xde, 0x20, 0x7d, 0xa1, 0xa1, 0x77, 0xce, 0xd3, 0xc1, 0x91, 0xa1, 0x29, 0x1e, 0xc6, 0xe3, 0x35, 0x14,
0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b,
0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x00, 0x00, 0x00, 0x83, 0x1e, 0x97,
0x8a, 0xf6, 0x06, 0xe2, 0xcb, 0xfe, 0xa5, 0x10, 0x1f, 0x59, 0x8b, 0xd6, 0x2f, 0x75, 0xeb, 0x3d, 0xff, 0xdf, 0x6e,
0x67, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcd, 0xcc, 0x4c, 0xbc, 0x97, 0xb3, 0x89, 0x2c, 0x3e, 0x8b, 0x8b, 0xaf,
0x9b, 0x36, 0xe5, 0x38, 0x1c, 0x8d, 0x76, 0x2b, 0xf5, 0x49, 0xe6, 0x15, 0x6c, 0x21, 0xaf, 0x6e, 0xdd, 0x87, 0x2f,
0x17, 0xee, 0x4e, 0x3a, 0xda, 0x8f, 0x49, 0x3f, 0xd1, 0xcc, 0xc1, 0xd6, 0x95, 0x9e, 0x09, 0x52, 0x40, 0xab, 0xac,
0x43, 0xc0, 0x04, 0x7a, 0x13, 0x51, 0x40, 0xae, 0xd1, 0x27, 0x84, 0xee, 0xf6, 0x4e, 0x8e, 0xd8, 0x03, 0x5c, 0x37,
0x93, 0x2c, 0x43, 0x49, 0xac, 0x48, 0xd9, 0xa0, 0x82, 0x6b, 0x9f, 0x73, 0x22, 0xc6, 0x35, 0x95, 0x41, 0x89, 0x43,
0xb1, 0x1f, 0x04, 0xfa, 0x25, 0x24, 0x59, 0x31, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54,
0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a,
0x49, 0x4d, 0x00, 0x00, 0x40, 0xe4, 0x16, 0xab, 0x32, 0x30, 0xf2, 0x5a, 0x04, 0x1d, 0x4c, 0x5c, 0xea, 0xfe, 0x39,
0xbb, 0xe4, 0x26, 0x4f, 0x5e, 0x35, 0xa6, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x66, 0x66, 0xdb,
0x44, 0x0e, 0xbd, 0xc1, 0x9b, 0x8c, 0x59, 0xb7, 0xb9, 0xc3, 0xb0, 0x7d, 0x2a, 0x27, 0x26, 0x19, 0x9e, 0xa2, 0x35,
0xe4, 0x38, 0x45, 0x2b, 0x4c, 0x98, 0x97, 0x8e, 0x45, 0xbb, 0xda, 0xd7, 0xf5, 0xdc, 0xf5, 0xee, 0x05, 0x0b, 0x14,
0x99, 0x1d, 0x2c, 0x77, 0x0b, 0xa8, 0xe0, 0x4c, 0x30, 0xd5, 0xe7, 0x25, 0xa5, 0x00, 0x81, 0x5f, 0xbf, 0x8a, 0x0c,
0x7c, 0xdd, 0x18, 0xb0, 0x6c, 0xf4, 0x8f, 0x59, 0x92, 0xf6, 0x79, 0xf6, 0x8d, 0x23, 0xf9, 0xaa, 0xc2, 0x33, 0xf1,
0x15, 0xa6, 0x48, 0x92, 0x1c, 0xd8, 0x44, 0x2d, 0xe3, 0xdf, 0x33, 0x4c, 0xab, 0x74, 0x9b, 0x1a, 0x01, 0x00, 0x00,
0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a,
0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x00, 0x00, 0xc0, 0x5a, 0x43, 0x4f, 0x54, 0x90, 0x5b,
0x8f, 0x29, 0xf1, 0xbe, 0xb3, 0x8e, 0xbf, 0x2b, 0x63, 0x9b, 0xa4, 0x6d, 0x29, 0xe3, 0xfd, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x34, 0x33, 0x93, 0x51, 0xa5, 0xa4, 0x2f, 0xc2, 0xf4, 0x64, 0x4f, 0xb1, 0x7e, 0xa0, 0x7e,
0xc7, 0x62, 0x51, 0xe8, 0xcd, 0xbb, 0xdf, 0x39, 0x36, 0x36, 0x64, 0xe4, 0xba, 0x75, 0x1f, 0xbe, 0x5c, 0x26, 0xa3,
0x44, 0xc5, 0xda, 0xd1, 0xc0, 0x1f, 0x86, 0x3b, 0x67, 0x70, 0xe2, 0x82, 0x02, 0x91, 0x2b, 0x40, 0xb0, 0x46, 0xe2,
0xcf, 0xe7, 0x51, 0x2b, 0x63, 0x28, 0xac, 0x8c, 0x0d, 0x7e, 0x2f, 0xce, 0x8f, 0x89, 0x09, 0xf4, 0x60, 0xb2, 0x30,
0xa1, 0xfd, 0x80, 0xf7, 0xf8, 0x2d, 0xa8, 0xa8, 0x0f, 0x4d, 0x4e, 0xaa, 0x92, 0x83, 0x95, 0xa1, 0x6b, 0x2c, 0xb2,
0x3e, 0x6f, 0x3e, 0x33, 0x5c, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d,
0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x00,
0x00, 0xd0, 0xb0, 0xb1, 0x11, 0x5d, 0x5f, 0x5d, 0xc3, 0x95, 0x1a, 0x6e, 0xc2, 0xfe, 0xd5, 0xed, 0x59, 0xd2, 0x9f,
0x2b, 0xf7, 0xc8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x99, 0x59, 0xbf, 0xae, 0x61, 0x76,
0x5e, 0x8d, 0xfd, 0x9d, 0xc7, 0xb2, 0xa0, 0x04, 0x7f, 0xbb, 0x90, 0x38, 0xa3, 0x7e, 0x5e, 0x2c, 0xdf, 0x1c, 0x32,
0x72, 0xdd, 0xba, 0x0f, 0x5f, 0x2e, 0x1a, 0xc0, 0x6a, 0xe1, 0x44, 0x05, 0x40, 0x61, 0xb6, 0x93, 0x2b, 0x00, 0x16,
0x8a, 0x70, 0x5b, 0xbe, 0x39, 0x1f, 0x89, 0xbd, 0x8f, 0x95, 0xd9, 0xad, 0x4a, 0x11, 0xe6, 0x30, 0xc0, 0xdb, 0x53,
0x2a, 0xb9, 0x1b, 0x91, 0x35, 0x4b, 0x6b, 0xe3, 0x17, 0xdf, 0x05, 0x6f, 0xfd, 0xd9, 0x0a, 0xc7, 0x4d, 0x05, 0x6d,
0x5e, 0x2d, 0xc9, 0x42, 0x0f, 0x43, 0x96, 0x20, 0xf6, 0x13, 0xd5, 0x35, 0x2d, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa,
0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda,
0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x00, 0x00, 0x30, 0x32, 0x38, 0x7a, 0x48, 0x1a, 0x77, 0x0f, 0x63, 0xf3,
0x88, 0x03, 0x96, 0xed, 0xc5, 0x2c, 0x20, 0xd4, 0xa5, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xcd, 0xcc, 0x84, 0x26, 0x24, 0x60, 0xdd, 0x3b, 0x8b, 0xb9, 0x0a, 0xa7, 0x81, 0xbc, 0x27, 0xda, 0x6d, 0xbc,
0x60, 0x76, 0xd7, 0xe7, 0xa3, 0x70, 0x0e, 0x19, 0xb9, 0x6e, 0xdd, 0x87, 0x2f, 0x17, 0xa4, 0x2d, 0x51, 0x76, 0xad,
0x02, 0x00, 0xd3, 0xd3, 0x9a, 0x21, 0x0c, 0x03, 0x85, 0xab, 0x50, 0x0b, 0xbf, 0x2d, 0x26, 0x8b, 0x88, 0x87, 0xdd,
0xb0, 0xc1, 0xe5, 0x0f, 0x6a, 0xf8, 0xca, 0x23, 0x3a, 0xb0, 0xb8, 0xed, 0x03, 0xb6, 0x19, 0x17, 0xb2, 0xd7, 0xaf,
0x25, 0x0f, 0xe8, 0x5d, 0x96, 0x88, 0x6b, 0xd7, 0x7f, 0xe0, 0xb0, 0x41, 0x57, 0xa1, 0x14, 0x1d, 0x55, 0x14, 0x63,
0xdb, 0x2a, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58,
0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x00, 0x00, 0x64, 0x7f,
0x44, 0x75, 0xc4, 0x97, 0x37, 0x63, 0x51, 0x19, 0x64, 0xd6, 0x26, 0x62, 0xe4, 0x5c, 0xc4, 0xac, 0xa2, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x66, 0xf6, 0x93, 0xf5, 0xd8, 0x74, 0xee, 0xd7, 0xdd,
0xc1, 0x95, 0x0c, 0x6d, 0x23, 0x58, 0x1b, 0x30, 0xd1, 0x7c, 0xdd, 0x80, 0xef, 0x51, 0x2b, 0x4b, 0x2b, 0x4c, 0x98,
0x97, 0x8e, 0x45, 0xd3, 0x87, 0x93, 0x2f, 0xa7, 0x92, 0xea, 0x68, 0xc2, 0x30, 0x3c, 0x9d, 0xfc, 0xe5, 0x9a, 0xe5,
0x63, 0x86, 0xfe, 0xd6, 0x66, 0x52, 0x9b, 0xc9, 0x7e, 0x85, 0xd7, 0x18, 0x5e, 0x6c, 0x19, 0x6d, 0x74, 0x08, 0x36,
0xf3, 0xae, 0xf9, 0x4c, 0xa7, 0xa7, 0x41, 0xb5, 0x25, 0x15, 0x62, 0xff, 0xae, 0x96, 0x82, 0xd3, 0x78, 0xf7, 0x79,
0x28, 0x91, 0xd6, 0xb7, 0xed, 0x30, 0xea, 0x73, 0x77, 0x25, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54,
0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71,
0x37, 0x1a, 0x49, 0x4d, 0x00, 0x00, 0xac, 0xba, 0x36, 0xd5, 0x4b, 0x79, 0xf3, 0x04, 0xf5, 0x8c, 0x51, 0xd7, 0x9c,
0xc4, 0x42, 0x49, 0x0f, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x33,
0x09, 0xd5, 0x70, 0xac, 0x80, 0x04, 0x8f, 0xd9, 0xd8, 0xe8, 0xa6, 0xb4, 0x00, 0x28, 0x46, 0x98, 0xca, 0xc7, 0xd2,
0xac, 0x94, 0xc2, 0x39, 0x64, 0xe4, 0xba, 0x75, 0x1f, 0xbe, 0x5c, 0xcf, 0x90, 0x68, 0xf1, 0x6e, 0x4c, 0xca, 0x87,
0x8f, 0x21, 0x48, 0xa0, 0x16, 0x8b, 0x99, 0xd9, 0x87, 0x24, 0x9b, 0x40, 0x22, 0xa8, 0x2f, 0x78, 0xa6, 0xa1, 0x66,
0xe9, 0x3d, 0x18, 0x20, 0x3d, 0x8f, 0xe7, 0x2a, 0x62, 0x9d, 0x7a, 0x59, 0x9f, 0x19, 0xad, 0x4c, 0x22, 0xf7, 0xd8,
0xff, 0x2f, 0xd1, 0x08, 0x62, 0xfa, 0xea, 0x32, 0xde, 0xeb, 0x34, 0xb7, 0x2f, 0xce, 0x59, 0xae, 0x58, 0x5e, 0x01,
0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06,
0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x00, 0x00, 0xb5, 0x26, 0x62, 0x47, 0xcf,
0x65, 0x84, 0xa1, 0x99, 0xdc, 0x4d, 0x33, 0x06, 0x56, 0x18, 0xe9, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x99, 0x05, 0xdc, 0x12, 0x36, 0xab, 0x88, 0x30, 0xba, 0x5f, 0xa0, 0xfb,
0x47, 0x07, 0x26, 0x35, 0x41, 0x2f, 0x9d, 0x69, 0x56, 0x4a, 0xe1, 0x1c, 0x32, 0x72, 0xdd, 0xba, 0x0f, 0x5f, 0x2e,
0x8a, 0xde, 0x53, 0x36, 0x9b, 0x28, 0xd7, 0xc2, 0x9a, 0xe9, 0x54, 0x8f, 0x12, 0x82, 0x62, 0x7c, 0x80, 0x95, 0xb6,
0xe4, 0xb6, 0x95, 0x68, 0x3a, 0x12, 0x91, 0xc6, 0x53, 0x8d, 0xb2, 0x5f, 0x00, 0x98, 0x26, 0x75, 0x9a, 0x48, 0x00,
0x22, 0x78, 0xd9, 0x15, 0xe9, 0x48, 0x9c, 0x41, 0x43, 0x00, 0x86, 0x64, 0xcf, 0xef, 0x41, 0xe0, 0x64, 0xc8, 0x06,
0xbd, 0xf3, 0x38, 0x14, 0x4d, 0xb4, 0x29, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55,
0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49,
0x4d, 0x00, 0x00, 0x03, 0xc6, 0x05, 0x53, 0x9b, 0xa2, 0x22, 0xa8, 0x0c, 0xfa, 0x63, 0x9c, 0xc2, 0x5e, 0x8d, 0x22,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcd, 0x4c, 0x74, 0x4a, 0xab,
0x56, 0x10, 0x49, 0x78, 0xb6, 0xd1, 0x04, 0x5c, 0xb0, 0x7e, 0x50, 0x06, 0xcf, 0xb9, 0xce, 0x34, 0x2b, 0xa5, 0x70,
0x0e, 0x19, 0xb9, 0x6e, 0xdd, 0x87, 0x2f, 0x17, 0xa3, 0xd0, 0xde, 0x7e, 0x48, 0x51, 0x64, 0x73, 0x21, 0x48, 0x05,
0xf8, 0xb9, 0xaf, 0xa6, 0xc6, 0xfa, 0xf1, 0xaa, 0xea, 0xdc, 0x98, 0xbb, 0xb0, 0x67, 0xa5, 0xd0, 0x60, 0x25, 0x56,
0x02, 0x04, 0x13, 0x70, 0x85, 0x7a, 0x7e, 0xf8, 0xf8, 0x6c, 0x77, 0xe5, 0x5b, 0x32, 0x93, 0xdd, 0x77, 0x1c, 0xa8,
0x36, 0x1c, 0x19, 0x15, 0xd6, 0x65, 0x8a, 0x9f, 0x83, 0x4a, 0xdf, 0x0f, 0x3c, 0x5c, 0x71, 0x01, 0x00, 0x00, 0x00,
0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1,
0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x00, 0x40, 0x58, 0x89, 0xea, 0xfc, 0xd4, 0xce, 0xe7, 0xab,
0x47, 0x2a, 0x12, 0x95, 0x93, 0x9d, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x67, 0x66, 0xa3, 0xab, 0x8c, 0xef, 0x89, 0x25, 0x47, 0x52, 0x2b, 0x9e, 0xa5, 0x3b, 0xf4, 0xbe,
0xea, 0xe7, 0x2d, 0x6c, 0x9e, 0x81, 0xef, 0x51, 0x2b, 0x4b, 0x2b, 0x4c, 0x98, 0x97, 0x8e, 0x45, 0xce, 0xc3, 0x34,
0xb3, 0x70, 0x4e, 0x4d, 0xbe, 0xc5, 0x5a, 0x0c, 0x13, 0x5b, 0xee, 0x07, 0xbf, 0x95, 0x12, 0x7e, 0xed, 0x4d, 0xa4,
0x8e, 0xc1, 0x70, 0x34, 0x9e, 0x6a, 0x3b, 0x06, 0xa6, 0x65, 0x0f, 0x2c, 0x93, 0x88, 0xf7, 0xef, 0xc9, 0x9c, 0x0d,
0xd3, 0xa8, 0x1f, 0x8b, 0xd7, 0x35, 0xe3, 0xcb, 0xa0, 0x72, 0x39, 0x0b, 0xa8, 0x5a, 0x6f, 0x95, 0xeb, 0x29, 0x88,
0x52, 0x3e, 0x01, 0x12, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e,
0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x00, 0xc0,
0x8a, 0x2f, 0x82, 0xda, 0x05, 0x96, 0x7e, 0x40, 0x9e, 0xef, 0x9e, 0xdb, 0x4c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x93, 0x07, 0x17, 0x22, 0x7d, 0xc9, 0xe4,
0xa3, 0x4b, 0x66, 0x4e, 0x3b, 0x84, 0x67, 0x34, 0x04, 0xe0, 0xe7, 0x3a, 0xd3, 0xac, 0x94, 0xc2, 0x39, 0x64, 0xe4,
0xba, 0x75, 0x1f, 0xbe, 0x5c, 0x0d, 0x88, 0x5f, 0xf8, 0xac, 0x4e, 0xb4, 0x30, 0x5c, 0xc6, 0xff, 0x6c, 0x72, 0xb2,
0xcf, 0x0b, 0x70, 0xaf, 0x96, 0x28, 0xa9, 0x31, 0xaf, 0xbc, 0x71, 0x72, 0xf3, 0xf2, 0x77, 0x8e, 0xac, 0x18, 0x59,
0xc0, 0x96, 0x6f, 0xa6, 0xde, 0x5d, 0x3b, 0x9f, 0x66, 0x1f, 0x89, 0xf0, 0x8b, 0xe8, 0x62, 0xe1, 0x64, 0x16, 0x30,
0xad, 0xff, 0xfd, 0xd9, 0x7f, 0xd8, 0xb0, 0x25, 0x9b, 0x02, 0xa4, 0x00, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa,
0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8,
0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x00, 0xd0, 0x69, 0x1e, 0xe4, 0x15, 0x78, 0x19, 0x0a, 0xda, 0x1f, 0xdf, 0xc2,
0x56, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x9a, 0x59, 0xe1, 0xb5, 0xf5, 0x60, 0x05, 0x8f, 0x81, 0xda, 0xfd, 0xe9, 0x29, 0x5a, 0x76, 0x21, 0x02, 0xf0, 0x73,
0x9d, 0x69, 0x56, 0x4a, 0xe1, 0x1c, 0x32, 0x72, 0xdd, 0xba, 0x0f, 0x5f, 0x2e, 0xfd, 0xdf, 0x81, 0x19, 0x3c, 0xad,
0xc6, 0x8b, 0x58, 0xfe, 0x51, 0x28, 0xb5, 0xf8, 0xdd, 0x10, 0x20, 0x61, 0x30, 0xb3, 0xb5, 0xf5, 0x76, 0x71, 0xa8,
0xc3, 0x57, 0xe4, 0x0c, 0xda, 0x70, 0x11, 0x1e, 0xbd, 0x07, 0x1a, 0xc6, 0xa1, 0x7e, 0x7c, 0xf2, 0x1c, 0xd0, 0x90,
0x6d, 0x8f, 0x21, 0x11, 0xde, 0x84, 0x40, 0xb2, 0x19, 0x13, 0x42, 0x9c, 0xf6, 0xc4, 0x72, 0xf5, 0x9e, 0x74, 0xbe,
0x50, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5,
0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x00, 0x30, 0x32, 0x71, 0x23,
0x2e, 0xdd, 0x8b, 0xf5, 0xda, 0xc1, 0xf5, 0x60, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcd, 0x04, 0x3e, 0x2b, 0xd5, 0x66, 0x7c, 0x0c, 0x14, 0xe9, 0xcd,
0x52, 0x02, 0x82, 0xbf, 0x10, 0x01, 0xf8, 0xb9, 0xce, 0x34, 0x2b, 0xa5, 0x70, 0x0e, 0x19, 0xb9, 0x6e, 0xdd, 0x87,
0x2f, 0x17, 0xc0, 0x5b, 0xf8, 0x94, 0x48, 0x19, 0xd1, 0x27, 0xe0, 0x36, 0x2a, 0x8e, 0xe7, 0x18, 0x70, 0x0f, 0xe1,
0x8e, 0xf4, 0x48, 0x72, 0x5c, 0x8e, 0x9c, 0xfb, 0xb9, 0x62, 0xb7, 0xa6, 0x15, 0x84, 0x5e, 0x6f, 0x6b, 0x14, 0xb3,
0x43, 0x24, 0xad, 0xe3, 0x9e, 0x76, 0x84, 0x7a, 0x92, 0x8d, 0x04, 0x40, 0x51, 0x73, 0x6b, 0x18, 0xc1, 0x91, 0x25,
0x20, 0x40, 0x67, 0x02, 0x0e, 0x72, 0x76, 0x27, 0x6b, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d,
0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37,
0x1a, 0x49, 0x4d, 0x00, 0xa4, 0xd8, 0x1d, 0xa2, 0x5e, 0xa7, 0x37, 0x70, 0xad, 0xd1, 0x9c, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x76, 0x60,
0xee, 0x6c, 0x59, 0x24, 0x5c, 0x74, 0x80, 0x2a, 0x4d, 0x98, 0x95, 0x3e, 0x32, 0x03, 0xe8, 0x2d, 0x6c, 0x9e, 0x81,
0xef, 0x51, 0x2b, 0x4b, 0x2b, 0x4c, 0x98, 0x97, 0x8e, 0x45, 0x22, 0x62, 0xe0, 0xd7, 0x4f, 0x07, 0xfe, 0x3a, 0x15,
0x87, 0xb6, 0x74, 0x37, 0x21, 0xa4, 0x0a, 0x98, 0x5e, 0x28, 0x12, 0x77, 0xf1, 0x1f, 0x50, 0xd9, 0x8f, 0xf9, 0x40,
0x41, 0x6f, 0x0f, 0x4b, 0x87, 0x7d, 0x5e, 0x14, 0x57, 0xf5, 0xe8, 0x74, 0xc4, 0xd4, 0x27, 0xfc, 0x86, 0xd9, 0x56,
0x66, 0x17, 0xd6, 0x97, 0xe5, 0x69, 0x2a, 0x6b, 0x03, 0x85, 0x38, 0xda, 0x70, 0xb2, 0xf6, 0xdd, 0x52, 0x01, 0x00,
0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0,
0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x00, 0xac, 0x7f, 0x7a, 0x7f, 0xcc, 0x6b, 0x89,
0xc3, 0x0c, 0x8f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x69, 0x13, 0xea, 0xec, 0xb4, 0x88, 0x9c, 0xa2, 0x9b, 0x58, 0x31, 0xcf, 0x1c,
0xfe, 0x42, 0x04, 0xe0, 0xe7, 0x3a, 0xd3, 0xac, 0x94, 0xc2, 0x39, 0x64, 0xe4, 0xba, 0x75, 0x1f, 0xbe, 0x5c, 0x4c,
0x07, 0x42, 0x14, 0x93, 0xe2, 0x83, 0x93, 0xa3, 0xa9, 0x72, 0x37, 0xa9, 0xb2, 0x58, 0xa7, 0x6a, 0x94, 0x53, 0xb5,
0xeb, 0xad, 0x3b, 0x56, 0x46, 0x6b, 0x61, 0xa3, 0x8c, 0x14, 0x56, 0x32, 0xd4, 0x10, 0xf3, 0xf6, 0x64, 0x82, 0xef,
0xb0, 0x24, 0xd2, 0xc2, 0x79, 0xde, 0xb0, 0x42, 0x9e, 0x65, 0x05, 0x88, 0x74, 0x2f, 0x8b, 0x58, 0xd5, 0x42, 0x1a,
0x06, 0xa3, 0xd4, 0xd1, 0xfc, 0x12, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57,
0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d,
0x00, 0xc5, 0x1c, 0xd9, 0xfc, 0x35, 0x0a, 0xad, 0x17, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x25, 0x9e, 0xe6, 0xbc, 0xaf,
0xb1, 0x88, 0x37, 0xd7, 0x97, 0x99, 0x67, 0x0e, 0x7f, 0x21, 0x02, 0xf0, 0x73, 0x9d, 0x69, 0x56, 0x4a, 0xe1, 0x1c,
0x32, 0x72, 0xdd, 0xba, 0x0f, 0x5f, 0x2e, 0x5d, 0x53, 0x09, 0xab, 0xc2, 0x58, 0x42, 0x94, 0x15, 0x71, 0x15, 0x1b,
0xfa, 0xfb, 0xcd, 0xc6, 0xbc, 0xa4, 0x00, 0x49, 0x74, 0xd3, 0x9c, 0x04, 0x81, 0xd7, 0x09, 0x28, 0x7d, 0x37, 0x37,
0x58, 0x8a, 0x94, 0x1a, 0x79, 0x8a, 0x7e, 0x94, 0x29, 0xbd, 0xab, 0x65, 0x5f, 0xc6, 0x94, 0xed, 0xdd, 0x94, 0xc1,
0xf1, 0xd8, 0xed, 0x88, 0x80, 0x47, 0x31, 0x52, 0xda, 0x94, 0x30, 0x60, 0xac, 0x0a, 0x01, 0x00, 0x00, 0x00, 0xaa,
0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc,
0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x00, 0x83, 0x86, 0xfc, 0xaf, 0x41, 0xb9, 0x0e, 0x8e, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x4d, 0xac, 0x2d, 0x08, 0x88, 0xfd, 0x14, 0x1e, 0x24, 0x78, 0xcc, 0xcc, 0x33, 0x87, 0xbf, 0x10, 0x01,
0xf8, 0xb9, 0xce, 0x34, 0x2b, 0xa5, 0x70, 0x0e, 0x19, 0xb9, 0x6e, 0xdd, 0x87, 0x2f, 0x17, 0xbb, 0xc3, 0xc0, 0xea,
0xbd, 0x9b, 0xd6, 0x22, 0x75, 0x96, 0x6c, 0xca, 0xc1, 0x2b, 0x45, 0x76, 0x2e, 0x4e, 0x2b, 0xf7, 0xfa, 0x9e, 0x64,
0x2d, 0x4b, 0xd8, 0xd9, 0x06, 0x24, 0xa3, 0xce, 0x49, 0x17, 0xd6, 0x83, 0xe8, 0x15, 0xc0, 0xbf, 0x4f, 0xf8, 0x85,
0x9d, 0xaf, 0xb6, 0x85, 0x62, 0x31, 0x89, 0x06, 0x10, 0xd3, 0x0a, 0xf1, 0xd5, 0x03, 0x50, 0x2c, 0x1e, 0xf4, 0xa8,
0xb6, 0x91, 0x2e, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2,
0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x40, 0x0c, 0xab,
0x80, 0x53, 0x26, 0xc2, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6b, 0xbb, 0x49, 0x36, 0x72, 0x46, 0x68, 0x65,
0x6a, 0x65, 0x66, 0x9b, 0x95, 0x3e, 0x32, 0x03, 0xe8, 0x2d, 0x6c, 0x9e, 0x81, 0xef, 0x51, 0x2b, 0x4b, 0x2b, 0x4c,
0x98, 0x97, 0x8e, 0x45, 0x80, 0x00, 0x64, 0x53, 0xae, 0xef, 0x99, 0x13, 0xc9, 0x1c, 0x53, 0x45, 0x0c, 0xdc, 0x97,
0xd5, 0x80, 0x4b, 0x56, 0x86, 0xa3, 0xbc, 0x78, 0xc4, 0xc2, 0x0c, 0xf7, 0xaf, 0xfd, 0x42, 0xe9, 0x1b, 0x5f, 0x2f,
0xa6, 0x4f, 0xa5, 0x53, 0xb5, 0xec, 0x36, 0x50, 0xc6, 0xd1, 0x3f, 0xdf, 0xaf, 0x63, 0x9f, 0x25, 0x1d, 0x40, 0xd2,
0xb5, 0x83, 0x24, 0x67, 0x57, 0x4d, 0x0c, 0x7b, 0x8c, 0x90, 0x3d, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa,
0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13,
0x71, 0x37, 0x1a, 0x49, 0x4d, 0xc0, 0xfa, 0x49, 0xea, 0x2a, 0x92, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94,
0x9d, 0x8c, 0xc4, 0x08, 0x6c, 0x2a, 0x66, 0x32, 0xe3, 0x31, 0x33, 0xcf, 0x1c, 0xfe, 0x42, 0x04, 0xe0, 0xe7, 0x3a,
0xd3, 0xac, 0x94, 0xc2, 0x39, 0x64, 0xe4, 0xba, 0x75, 0x1f, 0xbe, 0x5c, 0x57, 0x7e, 0xfd, 0x4b, 0xb2, 0xe8, 0x7d,
0x9b, 0x0e, 0x3c, 0x51, 0xa9, 0x15, 0xcb, 0x9e, 0xfa, 0x45, 0x73, 0x63, 0x75, 0xac, 0xb6, 0x70, 0xff, 0xcb, 0x0a,
0xb6, 0xd9, 0xd9, 0xf6, 0x7d, 0x5e, 0x31, 0x38, 0x8e, 0xff, 0x0c, 0xba, 0x37, 0xee, 0x71, 0x85, 0x91, 0xa1, 0xc8,
0x13, 0x11, 0xc4, 0x7c, 0x0f, 0x6e, 0xca, 0x37, 0x32, 0xdb, 0x2a, 0x5a, 0xf3, 0x69, 0x45, 0x0f, 0x15, 0xe4, 0x1e,
0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b,
0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0xd0, 0xb2, 0x9d, 0x4f, 0x2c, 0x1e,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x83, 0xa6, 0xa4, 0x69, 0x0f, 0x33, 0x33, 0x99, 0xf1, 0x98, 0x99,
0x67, 0x0e, 0x7f, 0x21, 0x02, 0xf0, 0x73, 0x9d, 0x69, 0x56, 0x4a, 0xe1, 0x1c, 0x32, 0x72, 0xdd, 0xba, 0x0f, 0x5f,
0x2e, 0xa0, 0xbc, 0x92, 0xcb, 0x0d, 0xff, 0xd0, 0xea, 0x29, 0x6b, 0x52, 0x84, 0x98, 0xe7, 0xf8, 0xf0, 0xfb, 0x66,
0xbe, 0x18, 0xab, 0x4c, 0x1b, 0x71, 0x16, 0xec, 0xb7, 0x81, 0x69, 0x77, 0xeb, 0x57, 0x47, 0xad, 0x0d, 0x3a, 0x20,
0x42, 0x92, 0xf1, 0xc1, 0xd3, 0xef, 0x25, 0xf5, 0x26, 0x55, 0xc3, 0x98, 0x49, 0x24, 0xec, 0xbe, 0x30, 0x09, 0xa8,
0x07, 0x14, 0x8f, 0xce, 0x6f, 0xdd, 0xa9, 0x71, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54,
0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a,
0x49, 0x4d, 0x30, 0xc2, 0xb2, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0xed, 0x68, 0x0c,
0x84, 0x99, 0x99, 0x99, 0xcc, 0x78, 0xcc, 0xcc, 0x33, 0x87, 0xbf, 0x10, 0x01, 0xf8, 0xb9, 0xce, 0x34, 0x2b, 0xa5,
0x70, 0x0e, 0x19, 0xb9, 0x6e, 0xdd, 0x87, 0x2f, 0x17, 0x88, 0x7c, 0xf4, 0x83, 0xfd, 0x16, 0xf9, 0xf0, 0x1a, 0xcc,
0xc4, 0xea, 0xf5, 0xba, 0x03, 0x19, 0x4a, 0x1b, 0x5d, 0x52, 0xe2, 0xfd, 0x78, 0xb1, 0x3d, 0xd3, 0x03, 0x25, 0x35,
0x11, 0x34, 0x47, 0x4d, 0xd6, 0x17, 0x27, 0xbf, 0xde, 0x87, 0x2c, 0xde, 0x46, 0x07, 0xcd, 0x2f, 0xa1, 0x31, 0x26,
0xe5, 0x8d, 0x87, 0xe1, 0xda, 0xac, 0xfe, 0x37, 0x97, 0x79, 0x72, 0xc7, 0x3f, 0x28, 0x4a, 0x5e, 0x01, 0x00, 0x00,
0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a,
0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0xe4, 0xc5, 0xbd, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xf7, 0xec, 0x75, 0x26, 0xcc, 0xcc, 0xcc, 0xcc, 0x65, 0x6a, 0x65, 0x66, 0x9b, 0x95, 0x3e,
0x32, 0x03, 0xe8, 0x2d, 0x6c, 0x9e, 0x81, 0xef, 0x51, 0x2b, 0x4b, 0x2b, 0x4c, 0x98, 0x97, 0x8e, 0x45, 0xb3, 0xc4,
0x59, 0xb6, 0x35, 0x71, 0xaf, 0xe7, 0x73, 0x4c, 0x03, 0xe2, 0x69, 0x50, 0x19, 0x36, 0x65, 0x43, 0xd5, 0x33, 0x7f,
0x31, 0xf1, 0x0e, 0x89, 0xa3, 0x4f, 0x55, 0xdd, 0xf3, 0x67, 0x57, 0xf1, 0xcb, 0xe8, 0x3c, 0x7f, 0x68, 0x6a, 0x29,
0xe5, 0x60, 0x35, 0x3e, 0x72, 0x40, 0x3c, 0x8b, 0x39, 0x01, 0xa5, 0x9e, 0x73, 0x99, 0x7f, 0x87, 0x18, 0xb4, 0x68,
0xc6, 0xd2, 0x7b, 0xa9, 0x71, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d,
0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0xac,
0x68, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xca, 0xff, 0x91, 0x99, 0x65, 0x66, 0x66,
0x66, 0x32, 0xe3, 0x31, 0x33, 0xcf, 0x1c, 0xfe, 0x42, 0x04, 0xe0, 0xe7, 0x3a, 0xd3, 0xac, 0x94, 0xc2, 0x39, 0x64,
0xe4, 0xba, 0x75, 0x1f, 0xbe, 0x5c, 0x71, 0x02, 0x27, 0x40, 0x1d, 0xe1, 0x11, 0xee, 0x3b, 0xc9, 0x13, 0xe2, 0x90,
0x29, 0xf8, 0x0b, 0x70, 0x4b, 0x7b, 0xfa, 0xd2, 0xd6, 0xf5, 0x8f, 0x45, 0xff, 0xc8, 0xc4, 0xc9, 0x4c, 0xf9, 0x5e,
0x0e, 0x66, 0x85, 0x5f, 0x88, 0xff, 0xd3, 0xb6, 0xd8, 0x0a, 0x67, 0xd6, 0x91, 0x18, 0x5b, 0xe5, 0xc9, 0xa9, 0xd9,
0x55, 0xb4, 0x30, 0xde, 0x83, 0xe5, 0x10, 0x9a, 0x05, 0x55, 0x53, 0xde, 0x55, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa,
0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58, 0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda,
0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0xd5, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x46, 0xc8, 0xcc, 0xcc, 0x32, 0x33, 0x33, 0x33, 0x99, 0xf1, 0x98, 0x99, 0x67, 0x0e, 0x7f, 0x21, 0x02, 0xf0,
0x73, 0x9d, 0x69, 0x56, 0x4a, 0xe1, 0x1c, 0x32, 0x72, 0xdd, 0xba, 0x0f, 0x5f, 0x2e, 0x01, 0x00, 0x00, 0xc0, 0xa9,
0xaa, 0xaa, 0x6a, 0x54, 0xd4, 0x53, 0x15, 0x58, 0xd6, 0x6d, 0x37, 0x5a, 0x5b, 0xd4, 0x08, 0xb2, 0xb0, 0x9f, 0xd9,
0x2c, 0x08, 0x7b, 0x3b, 0x0c, 0x84, 0x44, 0x6a, 0x08, 0x75, 0x50, 0x67, 0x4d, 0xe0, 0x04, 0xae, 0x38, 0x92, 0x4a,
0x99, 0x8b, 0x1e, 0xbb, 0x5f, 0x89, 0x70, 0x45, 0x82, 0x02, 0xe8, 0xe3, 0xe9, 0xea, 0x60, 0x2a, 0xd0, 0xa1, 0xe3,
0x59, 0x47, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x54, 0x3d, 0x54, 0x55, 0x57, 0x6d, 0x7e, 0xe2, 0x58,
0xe5, 0x6b, 0x06, 0xb0, 0x3a, 0xd1, 0xcc, 0xda, 0xa8, 0x13, 0x71, 0x37, 0x1a, 0x49, 0x4d, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x66, 0x66, 0xe6, 0x98, 0x99, 0x99, 0x19, 0xcc, 0xa6,
0xcb, 0x4c, 0x35, 0x59, 0x9e, 0xba, 0x03, 0xe4, 0x8a, 0xd3, 0x38, 0x17, 0x42, 0x8a, 0xb2, 0xd7, 0x87, 0x03, 0x87,
0x5b, 0x26, 0x51, 0x01, 0x00, 0x00, 0x40, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xc4, 0xfe, 0x3f, 0x02, 0x3b, 0xce, 0xfe,
0x03, 0x62, 0x39, 0x07, 0x06, 0x62, 0x6b, 0x26, 0xf6, 0x1d, 0x36, 0x5f, 0x7e, 0x3d, 0xf2, 0x56, 0x34, 0x33, 0x33,
0x33, 0xcc, 0xcc, 0xcc, 0xcc, 0x65, 0x6a, 0x65, 0x66, 0x9b, 0x95, 0x3e, 0x32, 0x03, 0xe8, 0x2d, 0x6c, 0x9e, 0x81,
0xef, 0x51, 0x2b, 0x4b, 0x2b, 0x4c, 0x98, 0x97, 0x8e, 0x45};
unsigned int constants_2_len = 11904;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,72 @@
#!/usr/bin/env python3
# 0.3.7
import galois
import numpy as np
# pip install poseidon-hash
from poseidon import round_constants as rc, round_numbers as rn
# Modify these
arity = 11
p = 0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001 # bls12-381
# p = 0x12ab655e9a2ca55660b44d1e5c37b00159aa76fed00000010a11800000000001 # bls12-377
# p = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 # bn254
# p = 0x1ae3a4617c510eac63b05c06ca1493b1a22d9f300f5138f1ef3622fba094800170b5d44300000008508c00000000001 # bw6-761
prime_bit_len = 255
field_bytes = 32
# leave set to -1 if not sure
full_round = -1
half_full_round = full_round // 2
# leave set to -1 if not sure
partial_round = -1
security_level = 128
# May speed up Galois significantly. If not sure - set it to None
# You can get primitive element fast by using sage
# p = ...
# F = GF(p)
# F.primitive_element()
#
# primitive_element = None
primitive_element = 7 # bls12-381
# primitive_element = 22 # bls12-377
# primitive_element = 5 # bn254
# primitive_element = 15 # bw6-761
# currently we only support alpha 5, if you need alpha other than 5 - feal free to reach out
alpha = 5
t = arity + 1
def flatten(xss):
return [x for xs in xss for x in xs]
if __name__ == "__main__":
if full_round == -1 or partial_round == -1:
full_round, partial_round, half_full_round = rn.calc_round_numbers(prime_bit_len, security_level, t, alpha, True)
print("Half full rounds:", half_full_round)
print("Partial rounds:", partial_round)
print("Loading galois... This might take from several minutes to an hour")
field_p = galois.GF(p, 1, verify=False, primitive_element=primitive_element)
print("Galois loaded")
mds_matrix = rc.mds_matrix_generator(field_p, t)
non_opt_rc = rc.calc_round_constants(t, full_round, partial_round, p, field_p, alpha, prime_bit_len)
split_rc = [field_p(x.tolist()) for x in np.array_split(non_opt_rc, len(non_opt_rc) / t)]
opt_rc = rc.optimized_rc(split_rc, half_full_round, partial_round, mds_matrix)
pre_matrix, sparse_matrices = rc.optimized_matrix(mds_matrix, partial_round, field_p)
sparse_aligned = []
for m in sparse_matrices:
m = flatten(m.tolist())
for j in range(0, t * t, t):
sparse_aligned.append(m[j])
for j in range(1, t):
sparse_aligned.append(m[j])
with open("constants.bin", "wb") as constants_file:
for l in [opt_rc, flatten(mds_matrix), flatten(pre_matrix), sparse_aligned]:
for c in l:
constants_file.write(int(c).to_bytes(field_bytes, byteorder='little'))

View File

@@ -0,0 +1,175 @@
#include "poseidon.cuh"
namespace poseidon {
template <typename S, int T>
__global__ void prepare_poseidon_states(S* states, size_t number_of_states, S domain_tag, bool aligned)
{
int idx = blockIdx.x * blockDim.x + threadIdx.x;
int state_number = idx / T;
if (state_number >= number_of_states) { return; }
int element_number = idx % T;
S prepared_element;
// Domain separation
if (element_number == 0) {
prepared_element = domain_tag;
} else {
if (aligned) {
prepared_element = states[idx];
} else {
prepared_element = states[idx - 1];
}
}
// We need __syncthreads here if the state is not aligned
// because then we need to shift the vector [A, B, 0] -> [D, A, B]
if (!aligned) { __syncthreads(); }
// Store element in state
states[idx] = prepared_element;
}
template <typename S>
__device__ __forceinline__ S sbox_alpha_five(S element)
{
S result = S::sqr(element);
result = S::sqr(result);
return result * element;
}
template <typename S, int T>
__device__ S vecs_mul_matrix(S element, S* matrix, int element_number, int vec_number, S* shared_states)
{
__syncthreads();
shared_states[threadIdx.x] = element;
__syncthreads();
typename S::Wide element_wide = S::mul_wide(shared_states[vec_number * T], matrix[element_number]);
#pragma unroll
for (int i = 1; i < T; i++) {
element_wide = element_wide + S::mul_wide(shared_states[vec_number * T + i], matrix[i * T + element_number]);
}
return S::reduce(element_wide);
}
template <typename S, int T>
__device__ S full_round(
S element,
size_t rc_offset,
int local_state_number,
int element_number,
bool multiply_by_mds,
bool add_pre_round_constants,
bool skip_rc,
S* shared_states,
const PoseidonConstants<S>& constants)
{
if (add_pre_round_constants) {
element = element + constants.round_constants[rc_offset + element_number];
rc_offset += T;
}
element = sbox_alpha_five(element);
if (!skip_rc) { element = element + constants.round_constants[rc_offset + element_number]; }
// Multiply all the states by mds matrix
S* matrix = multiply_by_mds ? constants.mds_matrix : constants.non_sparse_matrix;
return vecs_mul_matrix<S, T>(element, matrix, element_number, local_state_number, shared_states);
}
template <typename S, int T>
__global__ void full_rounds(
S* states, size_t number_of_states, size_t rc_offset, bool first_half, const PoseidonConstants<S> constants)
{
extern __shared__ S shared_states[];
int idx = (blockIdx.x * blockDim.x) + threadIdx.x;
int state_number = idx / T;
if (state_number >= number_of_states) { return; }
int local_state_number = threadIdx.x / T;
int element_number = idx % T;
S new_el = states[idx];
bool add_pre_round_constants = first_half;
for (int i = 0; i < constants.full_rounds_half; i++) {
new_el = full_round<S, T>(
new_el, rc_offset, local_state_number, element_number, !first_half || (i < (constants.full_rounds_half - 1)),
add_pre_round_constants, !first_half && (i == constants.full_rounds_half - 1), shared_states, constants);
rc_offset += T;
if (add_pre_round_constants) {
rc_offset += T;
add_pre_round_constants = false;
}
}
states[idx] = new_el;
}
template <typename S, int T>
__device__ S partial_round(S state[T], size_t rc_offset, int round_number, const PoseidonConstants<S>& constants)
{
S element = state[0];
element = sbox_alpha_five(element);
element = element + constants.round_constants[rc_offset];
S* sparse_matrix = &constants.sparse_matrices[(T * 2 - 1) * round_number];
typename S::Wide state_0_wide = S::mul_wide(element, sparse_matrix[0]);
#pragma unroll
for (int i = 1; i < T; i++) {
state_0_wide = state_0_wide + S::mul_wide(state[i], sparse_matrix[i]);
}
state[0] = S::reduce(state_0_wide);
#pragma unroll
for (int i = 1; i < T; i++) {
state[i] = state[i] + (element * sparse_matrix[T + i - 1]);
}
}
template <typename S, int T>
__global__ void
partial_rounds(S* states, size_t number_of_states, size_t rc_offset, const PoseidonConstants<S> constants)
{
int idx = (blockIdx.x * blockDim.x) + threadIdx.x;
if (idx >= number_of_states) { return; }
S state[T];
#pragma unroll
for (int i = 0; i < T; i++) {
state[i] = states[idx * T + i];
}
for (int i = 0; i < constants.partial_rounds; i++) {
partial_round<S, T>(state, rc_offset, i, constants);
rc_offset++;
}
#pragma unroll
for (int i = 0; i < T; i++) {
states[idx * T + i] = state[i];
}
}
// These function is just doing copy from the states to the output
template <typename S, int T>
__global__ void get_hash_results(S* states, size_t number_of_states, S* out)
{
int idx = (blockIdx.x * blockDim.x) + threadIdx.x;
if (idx >= number_of_states) { return; }
out[idx] = states[idx * T + 1];
}
template <typename S, int T>
__global__ void copy_recursive(S* state, size_t number_of_states, S* out)
{
int idx = (blockIdx.x * blockDim.x) + threadIdx.x;
if (idx >= number_of_states) { return; }
state[(idx / (T - 1) * T) + (idx % (T - 1)) + 1] = out[idx];
}
} // namespace poseidon

View File

@@ -1,269 +1,110 @@
#include "../../utils/error_handler.cuh"
#include "poseidon.cuh"
#include "constants.cu"
#include "kernels.cu"
template <typename S>
__global__ void
prepare_poseidon_states(S* states, size_t number_of_states, S domain_tag, const PoseidonConfiguration<S> config)
{
int idx = (blockIdx.x * blockDim.x) + threadIdx.x;
int state_number = idx / config.t;
if (state_number >= number_of_states) { return; }
int element_number = idx % config.t;
namespace poseidon {
template <typename S, int T>
cudaError_t
permute_many(S* states, size_t number_of_states, const PoseidonConstants<S>& constants, cudaStream_t& stream)
{
size_t rc_offset = 0;
S prepared_element;
full_rounds<S, T><<<
PKC<T>::number_of_full_blocks(number_of_states), PKC<T>::number_of_threads,
sizeof(S) * PKC<T>::hashes_per_block * T, stream>>>(
states, number_of_states, rc_offset, FIRST_FULL_ROUNDS, constants);
rc_offset += T * (constants.full_rounds_half + 1);
// Domain separation
if (element_number == 0) {
prepared_element = domain_tag;
} else {
prepared_element = states[state_number * config.t + element_number - 1];
partial_rounds<S, T>
<<<PKC<T>::number_of_singlehash_blocks(number_of_states), PKC<T>::singlehash_block_size, 0, stream>>>(
states, number_of_states, rc_offset, constants);
rc_offset += constants.partial_rounds;
full_rounds<S, T><<<
PKC<T>::number_of_full_blocks(number_of_states), PKC<T>::number_of_threads,
sizeof(S) * PKC<T>::hashes_per_block * T, stream>>>(
states, number_of_states, rc_offset, SECOND_FULL_ROUNDS, constants);
return CHK_LAST();
}
// Add pre-round constant
prepared_element = prepared_element + config.round_constants[element_number];
template <typename S, int T>
cudaError_t poseidon_hash(
S* input, S* output, size_t number_of_states, const PoseidonConstants<S>& constants, const PoseidonConfig& config)
{
CHK_INIT_IF_RETURN();
cudaStream_t& stream = config.ctx.stream;
S* states;
if (config.input_is_a_state) {
states = input;
} else {
// allocate memory for {number_of_states} states of {t} scalars each
CHK_IF_RETURN(cudaMallocAsync(&states, number_of_states * T * sizeof(S), stream))
// Store element in state
states[idx] = prepared_element;
}
// This is where the input matrix of size Arity x NumberOfBlocks is
// padded and copied to device in a T x NumberOfBlocks matrix
CHK_IF_RETURN(cudaMemcpy2DAsync(
states, T * sizeof(S), // Device pointer and device pitch
input, (T - 1) * sizeof(S), // Host pointer and pitch
(T - 1) * sizeof(S), number_of_states, // Size of the source matrix (Arity x NumberOfBlocks)
cudaMemcpyHostToDevice, stream));
}
template <typename S>
__device__ __forceinline__ S sbox_alpha_five(S element)
{
S result = S::sqr(element);
result = S::sqr(result);
return result * element;
}
S* output_device;
if (config.are_outputs_on_device) {
output_device = output;
} else {
CHK_IF_RETURN(cudaMallocAsync(&output_device, number_of_states * sizeof(S), stream))
}
template <typename S>
__device__ S vecs_mul_matrix(S element, S* matrix, int element_number, int vec_number, int size, S* shared_states)
{
shared_states[threadIdx.x] = element;
__syncthreads();
prepare_poseidon_states<S, T>
<<<PKC<T>::number_of_full_blocks(number_of_states), PKC<T>::number_of_threads, 0, stream>>>(
states, number_of_states, constants.domain_tag, config.aligned);
element = S::zero();
for (int i = 0; i < size; i++) {
element = element + (shared_states[vec_number * size + i] * matrix[i * size + element_number]);
}
__syncthreads();
return element;
}
cudaError_t hash_error = permute_many<S, T>(states, number_of_states, constants, stream);
CHK_IF_RETURN(hash_error);
template <typename S>
__device__ S full_round(
S element,
size_t rc_offset,
int local_state_number,
int element_number,
bool multiply_by_mds,
bool add_round_constant,
S* shared_states,
const PoseidonConfiguration<S> config)
{
element = sbox_alpha_five(element);
if (add_round_constant) { element = element + config.round_constants[rc_offset + element_number]; }
get_hash_results<S, T>
<<<PKC<T>::number_of_singlehash_blocks(number_of_states), PKC<T>::singlehash_block_size, 0, stream>>>(
states, number_of_states, output_device);
// Multiply all the states by mds matrix
S* matrix = multiply_by_mds ? config.mds_matrix : config.non_sparse_matrix;
return vecs_mul_matrix(element, matrix, element_number, local_state_number, config.t, shared_states);
}
if (config.loop_state) {
copy_recursive<S, T>
<<<PKC<T>::number_of_singlehash_blocks(number_of_states), PKC<T>::singlehash_block_size, 0, stream>>>(
states, number_of_states, output_device);
}
// Execute full rounds
template <typename S>
__global__ void full_rounds(
S* states, size_t number_of_states, size_t rc_offset, bool first_half, const PoseidonConfiguration<S> config)
{
extern __shared__ S shared_states[];
if (!config.input_is_a_state) CHK_IF_RETURN(cudaFreeAsync(states, stream));
int idx = (blockIdx.x * blockDim.x) + threadIdx.x;
int state_number = idx / config.t;
if (state_number >= number_of_states) { return; }
int local_state_number = threadIdx.x / config.t;
int element_number = idx % config.t;
if (!config.are_outputs_on_device) {
CHK_IF_RETURN(
cudaMemcpyAsync(output, output_device, number_of_states * sizeof(S), cudaMemcpyDeviceToHost, stream));
CHK_IF_RETURN(cudaFreeAsync(output_device, stream));
}
for (int i = 0; i < config.full_rounds_half - 1; i++) {
states[idx] =
full_round(states[idx], rc_offset, local_state_number, element_number, true, true, shared_states, config);
rc_offset += config.t;
if (!config.is_async) return CHK_STICKY(cudaStreamSynchronize(stream));
return CHK_LAST();
}
states[idx] = full_round(
states[idx], rc_offset, local_state_number, element_number, !first_half, first_half, shared_states, config);
}
template <typename S>
__device__ S partial_round(S* state, size_t rc_offset, int round_number, const PoseidonConfiguration<S> config)
{
S element = state[0];
element = sbox_alpha_five(element);
element = element + config.round_constants[rc_offset];
S* sparse_matrix = &config.sparse_matrices[(config.t * 2 - 1) * round_number];
state[0] = element * sparse_matrix[0];
for (int i = 1; i < config.t; i++) {
state[0] = state[0] + (state[i] * sparse_matrix[i]);
extern "C" cudaError_t CONCAT_EXPAND(CURVE, PoseidonHash)(
curve_config::scalar_t* input,
curve_config::scalar_t* output,
int number_of_states,
int arity,
const PoseidonConstants<curve_config::scalar_t>& constants,
PoseidonConfig& config)
{
switch (arity) {
case 2:
return poseidon_hash<curve_config::scalar_t, 3>(input, output, number_of_states, constants, config);
case 4:
return poseidon_hash<curve_config::scalar_t, 5>(input, output, number_of_states, constants, config);
case 8:
return poseidon_hash<curve_config::scalar_t, 9>(input, output, number_of_states, constants, config);
case 11:
return poseidon_hash<curve_config::scalar_t, 12>(input, output, number_of_states, constants, config);
default:
THROW_ICICLE_ERR(IcicleError_t::InvalidArgument, "PoseidonHash: #arity must be one of [2, 4, 8, 11]");
}
return CHK_LAST();
}
for (int i = 1; i < config.t; i++) {
state[i] = state[i] + (element * sparse_matrix[config.t + i - 1]);
}
}
// Execute partial rounds
template <typename S>
__global__ void
partial_rounds(S* states, size_t number_of_states, size_t rc_offset, const PoseidonConfiguration<S> config)
{
int idx = (blockIdx.x * blockDim.x) + threadIdx.x;
if (idx >= number_of_states) { return; }
S* state = &states[idx * config.t];
for (int i = 0; i < config.partial_rounds; i++) {
partial_round(state, rc_offset, i, config);
rc_offset++;
}
}
// These function is just doing copy from the states to the output
template <typename S>
__global__ void get_hash_results(S* states, size_t number_of_states, S* out, int t)
{
int idx = (blockIdx.x * blockDim.x) + threadIdx.x;
if (idx >= number_of_states) { return; }
out[idx] = states[idx * t + 1];
}
template <typename S>
__host__ cudaError_t
Poseidon<S>::hash_blocks(const S* inp, size_t blocks, S* out, HashType hash_type, cudaStream_t stream)
{
CHK_INIT_IF_RETURN();
S* states;
// allocate memory for {blocks} states of {t} scalars each
CHK_IF_RETURN(cudaMallocAsync(&states, blocks * this->t * sizeof(S), stream));
// This is where the input matrix of size Arity x NumberOfBlocks is
// padded and copied to device in a T x NumberOfBlocks matrix
CHK_IF_RETURN(cudaMemcpy2DAsync(
states, this->t * sizeof(S), // Device pointer and device pitch
inp, (this->t - 1) * sizeof(S), // Host pointer and pitch
(this->t - 1) * sizeof(S), blocks, // Size of the source matrix (Arity x NumberOfBlocks)
cudaMemcpyHostToDevice, stream));
size_t rc_offset = 0;
// The logic behind this is that 1 thread only works on 1 element
// We have {t} elements in each state, and {blocks} states total
int number_of_threads = (256 / this->t) * this->t;
int hashes_per_block = number_of_threads / this->t;
int total_number_of_threads = blocks * this->t;
int number_of_blocks =
total_number_of_threads / number_of_threads + static_cast<bool>(total_number_of_threads % number_of_threads);
// The partial rounds operates on the whole state, so we define
// the parallelism params for processing a single hash preimage per thread
int singlehash_block_size = 128;
int number_of_singlehash_blocks = blocks / singlehash_block_size + static_cast<bool>(blocks % singlehash_block_size);
// Pick the domain_tag accordinaly
S domain_tag;
switch (hash_type) {
case HashType::ConstInputLen:
domain_tag = this->const_input_no_pad_domain_tag;
break;
case HashType::MerkleTree:
domain_tag = this->tree_domain_tag;
}
#if !defined(__CUDA_ARCH__) && defined(DEBUG)
auto start_time = std::chrono::high_resolution_clock::now();
#endif
// Domain separation and adding pre-round constants
prepare_poseidon_states<<<number_of_blocks, number_of_threads, 0, stream>>>(states, blocks, domain_tag, this->config);
rc_offset += this->t;
#if !defined(__CUDA_ARCH__) && defined(DEBUG)
CHK_IF_RETURN(cudaStreamSynchronize(stream));
std::cout << "Domain separation: " << rc_offset << std::endl;
// print_buffer_from_cuda<S>(states, blocks * this->t);
auto end_time = std::chrono::high_resolution_clock::now();
auto elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
std::cout << "Elapsed time: " << elapsed_time.count() << " ms" << std::endl;
start_time = std::chrono::high_resolution_clock::now();
#endif
// execute half full rounds
full_rounds<<<number_of_blocks, number_of_threads, this->t * sizeof(S) * hashes_per_block, stream>>>(
states, blocks, rc_offset, true, this->config);
rc_offset += this->t * this->config.full_rounds_half;
#if !defined(__CUDA_ARCH__) && defined(DEBUG)
CHK_IF_RETURN(cudaStreamSynchronize(stream));
std::cout << "Full rounds 1. RCOFFSET: " << rc_offset << std::endl;
// print_buffer_from_cuda<S>(states, blocks * this->t);
end_time = std::chrono::high_resolution_clock::now();
elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
std::cout << "Elapsed time: " << elapsed_time.count() << " ms" << std::endl;
start_time = std::chrono::high_resolution_clock::now();
#endif
// execute partial rounds
partial_rounds<<<number_of_singlehash_blocks, singlehash_block_size, 0, stream>>>(
states, blocks, rc_offset, this->config);
rc_offset += this->config.partial_rounds;
#if !defined(__CUDA_ARCH__) && defined(DEBUG)
CHK_IF_RETURN(cudaStreamSynchronize(stream));
std::cout << "Partial rounds. RCOFFSET: " << rc_offset << std::endl;
// print_buffer_from_cuda<S>(states, blocks * this->t);
end_time = std::chrono::high_resolution_clock::now();
elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
std::cout << "Elapsed time: " << elapsed_time.count() << " ms" << std::endl;
start_time = std::chrono::high_resolution_clock::now();
#endif
// execute half full rounds
full_rounds<<<number_of_blocks, number_of_threads, this->t * sizeof(S) * hashes_per_block, stream>>>(
states, blocks, rc_offset, false, this->config);
#if !defined(__CUDA_ARCH__) && defined(DEBUG)
CHK_IF_RETURN(cudaStreamSynchronize(stream));
std::cout << "Full rounds 2. RCOFFSET: " << rc_offset << std::endl;
// print_buffer_from_cuda<S>(states, blocks * this->t);
end_time = std::chrono::high_resolution_clock::now();
elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
std::cout << "Elapsed time: " << elapsed_time.count() << " ms" << std::endl;
start_time = std::chrono::high_resolution_clock::now();
#endif
// get output
S* out_device;
CHK_IF_RETURN(cudaMalloc(&out_device, blocks * sizeof(S)));
get_hash_results<<<number_of_singlehash_blocks, singlehash_block_size, 0, stream>>>(
states, blocks, out_device, this->config.t);
#if !defined(__CUDA_ARCH__) && defined(DEBUG)
CHK_IF_RETURN(cudaStreamSynchronize(stream));
std::cout << "Get hash results" << std::endl;
end_time = std::chrono::high_resolution_clock::now();
elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
std::cout << "Elapsed time: " << elapsed_time.count() << " ms" << std::endl;
#endif
CHK_IF_RETURN(cudaMemcpyAsync(out, out_device, blocks * sizeof(S), cudaMemcpyDeviceToHost, stream));
CHK_IF_RETURN(cudaFreeAsync(out_device, stream));
CHK_IF_RETURN(cudaFreeAsync(states, stream));
#if !defined(__CUDA_ARCH__) && defined(DEBUG)
CHK_IF_RETURN(cudaDeviceReset());
#endif
return CHK_LAST();
}
} // namespace poseidon

View File

@@ -1,157 +1,140 @@
#pragma once
#include "constants.cuh"
#ifndef POSEIDON_H
#define POSEIDON_H
#if !defined(__CUDA_ARCH__) && defined(DEBUG)
#include <chrono>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <cstdint>
#include <stdexcept>
#include "utils/device_context.cuh"
#include "curves/curve_config.cuh"
#include "utils/error_handler.cuh"
#include "utils/utils.h"
template <typename S>
__host__ void print_buffer_from_cuda(S* device_ptr, size_t size, size_t t)
{
S* buffer = static_cast<S*>(malloc(size * sizeof(S)));
CHK_LOG(checudaMemcpy(buffer, device_ptr, size * sizeof(S), cudaMemcpyDeviceToHost));
/**
* @namespace poseidon
* Implementation of the [Poseidon hash function](https://eprint.iacr.org/2019/458.pdf)
* Specifically, the optimized [Filecoin version](https://spec.filecoin.io/algorithms/crypto/poseidon/)
*/
namespace poseidon {
#define FIRST_FULL_ROUNDS true
#define SECOND_FULL_ROUNDS false
std::cout << "Start print" << std::endl;
for (int i = 0; i < size / t; i++) {
std::cout << "State #" << i << std::endl;
for (int j = 0; j < t; j++) {
std::cout << buffer[i * t + j] << std::endl;
}
std::cout << std::endl;
}
std::cout << std::endl;
free(buffer);
}
#endif
/**
* For most of the Poseidon configurations this is the case
* To-do: Add support for different full rounds numbers
*/
const int FULL_ROUNDS_DEFAULT = 4;
#ifdef DEBUG
template <typename S>
__device__ void print_scalar(S element, int data)
{
printf(
"D# %d, T# %d: 0x%08x%08x%08x%08x%08x%08x%08x%08x\n", data, threadIdx.x, element.limbs_storage.limbs[0],
element.limbs_storage.limbs[1], element.limbs_storage.limbs[2], element.limbs_storage.limbs[3],
element.limbs_storage.limbs[4], element.limbs_storage.limbs[5], element.limbs_storage.limbs[6],
element.limbs_storage.limbs[7]);
}
#endif
template <typename S>
struct PoseidonConfiguration {
uint32_t partial_rounds, full_rounds_half, t;
S *round_constants, *mds_matrix, *non_sparse_matrix, *sparse_matrices;
};
template <typename S>
class Poseidon
{
public:
uint32_t t;
PoseidonConfiguration<S> config;
enum HashType {
ConstInputLen,
MerkleTree,
/**
* @struct PoseidonConstants
* This constants are enough to define a Poseidon instantce
* @param round_constants A pointer to round constants allocated on the device
* @param mds_matrix A pointer to an mds matrix allocated on the device
* @param non_sparse_matrix A pointer to non sparse matrix allocated on the device
* @param sparse_matrices A pointer to sparse matrices allocated on the device
*/
template <typename S>
struct PoseidonConstants {
int arity;
int partial_rounds;
int full_rounds_half;
S* round_constants = nullptr;
S* mds_matrix = nullptr;
S* non_sparse_matrix = nullptr;
S* sparse_matrices = nullptr;
S domain_tag;
};
Poseidon(const uint32_t arity, cudaStream_t stream)
/**
* @class PoseidonKernelsConfiguration
* Describes the logic of deriving CUDA kernels parameters
* such as the number of threads and the number of blocks
*/
template <int T>
class PoseidonKernelsConfiguration
{
t = arity + 1;
this->config.t = t;
this->stream = stream;
public:
// The logic behind this is that 1 thread only works on 1 element
// We have {T} elements in each state, and {number_of_states} states total
static const int number_of_threads = 256 / T * T;
// Pre-calculate domain tags
// Domain tags will vary for different applications of Poseidon
uint32_t tree_domain_tag_value = 1;
tree_domain_tag_value = (tree_domain_tag_value << arity) - tree_domain_tag_value;
tree_domain_tag = S::from(tree_domain_tag_value);
// The partial rounds operates on the whole state, so we define
// the parallelism params for processing a single hash preimage per thread
static const int singlehash_block_size = 128;
const_input_no_pad_domain_tag = S::one();
static const int hashes_per_block = number_of_threads / T;
// TO-DO: implement binary shifts for scalar type
// const_input_no_pad_domain_tag = S::one() << 64;
// const_input_no_pad_domain_tag *= S::from(arity);
static int number_of_full_blocks(size_t number_of_states)
{
int total_number_of_threads = number_of_states * T;
return total_number_of_threads / number_of_threads +
static_cast<bool>(total_number_of_threads % number_of_threads);
}
this->config.full_rounds_half = FULL_ROUNDS_DEFAULT;
this->config.partial_rounds = partial_rounds_number_from_arity(arity);
static int number_of_singlehash_blocks(size_t number_of_states)
{
return number_of_states / singlehash_block_size + static_cast<bool>(number_of_states % singlehash_block_size);
}
};
uint32_t round_constants_len = t * this->config.full_rounds_half * 2 + this->config.partial_rounds;
uint32_t mds_matrix_len = t * t;
uint32_t sparse_matrices_len = (t * 2 - 1) * this->config.partial_rounds;
template <int T>
using PKC = PoseidonKernelsConfiguration<T>;
// All the constants are stored in a single file
S* constants = load_constants<S>(arity);
/**
* @struct NTTConfig
* Struct that encodes various Poseidon parameters.
*/
struct PoseidonConfig {
device_context::DeviceContext ctx; /**< Details related to the device such as its id and stream id. */
bool are_inputs_on_device; /**< True if inputs are on device and false if they're on host. Default value: false. */
bool are_outputs_on_device; /**< If true, output is preserved on device, otherwise on host. Default value: false. */
bool input_is_a_state; /**< If true, input is considered to be a states vector, holding the preimages
* in aligned or not aligned format. Memory under the input pointer will be used for states
* If false, fresh states memory will be allocated and input will be copied into it */
bool aligned; /**< If true - input should be already aligned for poseidon permutation.
* Aligned format: [0, A, B, 0, C, D, ...] (as you might get by using loop_state)
* not aligned format: [A, B, 0, C, D, 0, ...] (as you might get from cudaMemcpy2D) */
bool loop_state; /**< If true, hash results will also be copied in the input pointer in aligned format */
bool is_async; /**< Whether to run the Poseidon asynchronously. If set to `true`, the poseidon_hash function will be
* non-blocking and you'd need to synchronize it explicitly by running
* `cudaStreamSynchronize` or `cudaDeviceSynchronize`. If set to false, the poseidon_hash
* function will block the current CPU thread. */
};
S* mds_offset = constants + round_constants_len;
S* non_sparse_offset = mds_offset + mds_matrix_len;
S* sparse_matrices_offset = non_sparse_offset + mds_matrix_len;
#if !defined(__CUDA_ARCH__) && defined(DEBUG)
std::cout << "P: " << this->config.partial_rounds << " F: " << this->config.full_rounds_half << std::endl;
#endif
// Create streams for copying constants
cudaStream_t stream_copy_round_constants, stream_copy_mds_matrix, stream_copy_non_sparse,
stream_copy_sparse_matrices;
cudaStreamCreate(&stream_copy_round_constants);
cudaStreamCreate(&stream_copy_mds_matrix);
cudaStreamCreate(&stream_copy_non_sparse);
cudaStreamCreate(&stream_copy_sparse_matrices);
// Create events for copying constants
cudaEvent_t event_copied_round_constants, event_copy_mds_matrix, event_copy_non_sparse, event_copy_sparse_matrices;
cudaEventCreateWithFlags(&event_copied_round_constants, cudaEventDisableTiming);
cudaEventCreateWithFlags(&event_copy_mds_matrix, cudaEventDisableTiming);
cudaEventCreateWithFlags(&event_copy_non_sparse, cudaEventDisableTiming);
cudaEventCreateWithFlags(&event_copy_sparse_matrices, cudaEventDisableTiming);
// Malloc memory for copying constants
cudaMallocAsync(&this->config.round_constants, sizeof(S) * round_constants_len, stream_copy_round_constants);
cudaMallocAsync(&this->config.mds_matrix, sizeof(S) * mds_matrix_len, stream_copy_mds_matrix);
cudaMallocAsync(&this->config.non_sparse_matrix, sizeof(S) * mds_matrix_len, stream_copy_non_sparse);
cudaMallocAsync(&this->config.sparse_matrices, sizeof(S) * sparse_matrices_len, stream_copy_sparse_matrices);
// Copy constants
cudaMemcpyAsync(
this->config.round_constants, constants, sizeof(S) * round_constants_len, cudaMemcpyHostToDevice,
stream_copy_round_constants);
cudaMemcpyAsync(
this->config.mds_matrix, mds_offset, sizeof(S) * mds_matrix_len, cudaMemcpyHostToDevice, stream_copy_mds_matrix);
cudaMemcpyAsync(
this->config.non_sparse_matrix, non_sparse_offset, sizeof(S) * mds_matrix_len, cudaMemcpyHostToDevice,
stream_copy_non_sparse);
cudaMemcpyAsync(
this->config.sparse_matrices, sparse_matrices_offset, sizeof(S) * sparse_matrices_len, cudaMemcpyHostToDevice,
stream_copy_sparse_matrices);
// Record finished copying event for streams
cudaEventRecord(event_copied_round_constants, stream_copy_round_constants);
cudaEventRecord(event_copy_mds_matrix, stream_copy_mds_matrix);
cudaEventRecord(event_copy_non_sparse, stream_copy_non_sparse);
cudaEventRecord(event_copy_sparse_matrices, stream_copy_sparse_matrices);
// Main stream waits for copying to finish
cudaStreamWaitEvent(stream, event_copied_round_constants);
cudaStreamWaitEvent(stream, event_copy_mds_matrix);
cudaStreamWaitEvent(stream, event_copy_non_sparse);
cudaStreamWaitEvent(stream, event_copy_sparse_matrices);
template <typename S>
PoseidonConfig default_poseidon_config(int t)
{
device_context::DeviceContext ctx = device_context::get_default_device_context();
PoseidonConfig config = {
ctx, // ctx
false, // are_inputes_on_device
false, // are_outputs_on_device
false, // input_is_a_state
false, // aligned
false, // loop_state
false, // is_async
};
return config;
}
~Poseidon()
{
cudaFreeAsync(this->config.round_constants, this->stream);
cudaFreeAsync(this->config.mds_matrix, this->stream);
cudaFreeAsync(this->config.non_sparse_matrix, this->stream);
cudaFreeAsync(this->config.sparse_matrices, this->stream);
}
/**
* Loads pre-calculated optimized constants, moves them to the device
*/
template <typename S>
cudaError_t init_optimized_poseidon_constants(device_context::DeviceContext& ctx, PoseidonConstants<S>* constants);
// Hash multiple preimages in parallel
cudaError_t hash_blocks(const S* inp, size_t blocks, S* out, HashType hash_type, cudaStream_t stream);
/**
* Compute the poseidon hash over a sequence of preimages.
* Takes {number_of_states * (T-1)} elements of input and computes {number_of_states} hash images
* @param T size of the poseidon state, should be equal to {arity + 1}
* @param input a pointer to the input data. May be allocated on device or on host, regulated
* by the config. May point to a string of preimages or a string of states filled with preimages.
* @param output a pointer to the output data. May be allocated on device or on host, regulated
* by the config. Must be at least of size [number_of_states](@ref number_of_states)
* @param number_of_states number of input blocks of size T-1 (arity)
*/
template <typename S, int T>
cudaError_t poseidon_hash(
S* input, S* output, size_t number_of_states, const PoseidonConstants<S>& constants, const PoseidonConfig& config);
} // namespace poseidon
private:
S tree_domain_tag, const_input_no_pad_domain_tag;
cudaStream_t stream;
};
#endif

View File

@@ -1,67 +1,57 @@
// TODO: add to gtest or remove
// #define DEBUG
#include "../../curves/bls12_381/curve_config.cuh"
#include "../../curves/bls12_381/poseidon.cu"
#define CURVE_ID 2
#include "../../curves/curve_config.cuh"
#include "../../utils/device_context.cuh"
#include "poseidon.cu"
#ifndef __CUDA_ARCH__
#include <cassert>
#include <chrono>
#include <fstream>
#include <iostream>
using namespace poseidon;
using namespace curve_config;
#define A 2
#define T (A + 1)
#define START_TIMER(timer) auto timer##_start = std::chrono::high_resolution_clock::now();
#define END_TIMER(timer, msg) \
printf("%s: %.0f ms\n", msg, FpMilliseconds(std::chrono::high_resolution_clock::now() - timer##_start).count());
int main(int argc, char* argv[])
{
using FpMilliseconds = std::chrono::duration<float, std::chrono::milliseconds::period>;
using FpMicroseconds = std::chrono::duration<float, std::chrono::microseconds::period>;
const int arity = 2;
const int t = arity + 1;
// Load poseidon constants
START_TIMER(timer_const);
device_context::DeviceContext ctx = device_context::get_default_device_context();
PoseidonConstants<scalar_t> constants;
init_optimized_poseidon_constants<scalar_t>(A, ctx, &constants);
END_TIMER(timer_const, "Load poseidon constants");
cudaStream_t stream;
cudaStreamCreate(&stream);
cudaEvent_t start_event, end_event;
cudaEventCreate(&start_event);
cudaEventCreate(&end_event);
cudaEventRecord(start_event, stream);
auto start_time1 = std::chrono::high_resolution_clock::now();
Poseidon<BLS12_381::scalar_t> poseidon(arity, stream);
auto end_time1 = std::chrono::high_resolution_clock::now();
auto elapsed_time1 = std::chrono::duration_cast<std::chrono::microseconds>(end_time1 - start_time1);
printf("Elapsed time poseidon: %.0f us\n", FpMicroseconds(elapsed_time1).count());
int number_of_blocks = 1024;
BLS12_381::scalar_t input = BLS12_381::scalar_t::zero();
BLS12_381::scalar_t* in_ptr =
static_cast<BLS12_381::scalar_t*>(malloc(number_of_blocks * arity * sizeof(BLS12_381::scalar_t)));
for (uint32_t i = 0; i < number_of_blocks * arity; i++) {
START_TIMER(allocation_timer);
// Prepare input data of [0, 1, 2 ... (number_of_blocks * arity) - 1]
int number_of_blocks = argc > 1 ? 1 << atoi(argv[1]) : 1024;
scalar_t input = scalar_t::zero();
scalar_t* in_ptr = static_cast<scalar_t*>(malloc(number_of_blocks * A * sizeof(scalar_t)));
for (uint32_t i = 0; i < number_of_blocks * A; i++) {
in_ptr[i] = input;
input = input + BLS12_381::scalar_t::one();
input = input + scalar_t::one();
}
std::cout << std::endl;
END_TIMER(allocation_timer, "Allocate mem and fill input");
BLS12_381::scalar_t* out_ptr =
static_cast<BLS12_381::scalar_t*>(malloc(number_of_blocks * sizeof(BLS12_381::scalar_t)));
scalar_t* out_ptr = static_cast<scalar_t*>(malloc(number_of_blocks * sizeof(scalar_t)));
auto start_time = std::chrono::high_resolution_clock::now();
START_TIMER(poseidon_timer);
PoseidonConfig config = default_poseidon_config<scalar_t>(T);
poseidon_hash<curve_config::scalar_t, T>(in_ptr, out_ptr, number_of_blocks, constants, config);
END_TIMER(poseidon_timer, "Poseidon")
poseidon.hash_blocks(in_ptr, number_of_blocks, out_ptr, Poseidon<BLS12_381::scalar_t>::HashType::MerkleTree, stream);
auto end_time = std::chrono::high_resolution_clock::now();
auto elapsed_time = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);
printf("Elapsed time hash: %.0f us\n", FpMicroseconds(elapsed_time).count());
cudaEventRecord(end_event, stream);
cudaEventSynchronize(end_event);
float elapsedTime;
cudaEventElapsedTime(&elapsedTime, start_event, end_event);
printf("Elapsed time: %8.3f ms\n", elapsedTime);
cudaEventDestroy(start_event);
cudaEventDestroy(end_event);
BLS12_381::scalar_t expected[1024] = {
scalar_t expected[1024] = {
{2583881727, 773864502, 2634393245, 2801510707, 49275233, 1939738585, 1584833899, 962922711},
{1482052501, 2945755510, 2790332687, 3994795689, 2690398473, 2055226187, 3927265331, 526041267},
{908959580, 3968357170, 168369822, 4279251122, 172491869, 1810633943, 1108167336, 461319268},
@@ -1087,14 +1077,15 @@ int main(int argc, char* argv[])
{3906397586, 3290474371, 1675512984, 4178526889, 2952985253, 3592646253, 1572272369, 508740262},
{4126315652, 3786312147, 3699923125, 3674356834, 1459586774, 373249965, 2436297855, 1091365949}};
for (int i = 0; i < number_of_blocks; i++) {
assert((out_ptr[i] == expected[i]));
if (number_of_blocks == 1024) {
for (int i = 0; i < number_of_blocks; i++) {
#ifdef DEBUG
std::cout << out_ptr[i] << std::endl;
std::cout << out_ptr[i] << std::endl;
#endif
assert((out_ptr[i] == expected[i]));
}
printf("Expected output matches\n");
}
printf("Expected output matches\n");
free(in_ptr);
free(out_ptr);

1
icicle/appUtils/tree/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
test_merkle

View File

@@ -0,0 +1,3 @@
test_merkle:
nvcc -o test_merkle -I. -I../.. test.cu
./test_merkle

View File

@@ -0,0 +1,280 @@
#include "merkle.cuh"
namespace merkle {
/// Flattens the tree digests and sum them up to get
/// the memory needed to contain all the digests
template <typename S>
size_t get_digests_len(uint32_t height, uint32_t arity)
{
size_t digests_len = 0;
size_t row_length = 1;
for (int i = 1; i < height; i++) {
digests_len += row_length;
row_length *= arity;
}
return digests_len;
}
/// Constructs merkle subtree without parallelization
/// The digests are aligned sequentially per row
/// Example:
///
/// Big tree:
///
/// 1
/// / \
/// 2 3
/// / \ / \
/// 4 5 6 7
///
/// Subtree 1 Subtree 2
/// 2 3
/// / \ / \
/// 4 5 6 7
///
/// Digests array for subtree 1:
/// [4 5 . . 2 . .]
/// | | |
/// ----- V
/// | Segment (offset = 4, subtree_idx = 0)
/// v
/// Segment (offset = 0, subtree_idx = 0)
///
/// Digests array for subtree 2:
/// [. . 6 7 . 3 .]
/// | |
/// -----
/// |
/// v
/// Segment (offset = 0, subtree_idx = 1)
///
/// Total digests array:
/// [4 5 6 7 2 3 .]
template <typename S, int T>
cudaError_t build_merkle_subtree(
S* state,
S* digests,
size_t subtree_idx,
size_t subtree_height,
S* big_tree_digests,
size_t start_segment_size,
size_t start_segment_offset,
int keep_rows,
const PoseidonConstants<S>& poseidon,
cudaStream_t& stream)
{
int arity = T - 1;
PoseidonConfig config = default_poseidon_config<S>(T);
config.are_inputs_on_device = true;
config.are_outputs_on_device = true;
config.input_is_a_state = true;
config.loop_state = true;
config.ctx.stream = stream;
size_t leaves_size = pow(arity, subtree_height - 1);
uint32_t number_of_blocks = leaves_size / arity;
size_t segment_size = start_segment_size;
size_t segment_offset = start_segment_offset;
while (number_of_blocks > 0) {
cudaError_t poseidon_res = poseidon_hash<S, T>(state, digests, number_of_blocks, poseidon, config);
CHK_IF_RETURN(poseidon_res);
if (!keep_rows || subtree_height <= keep_rows + 1) {
S* digests_with_offset = big_tree_digests + segment_offset + subtree_idx * number_of_blocks;
CHK_IF_RETURN(
cudaMemcpyAsync(digests_with_offset, digests, number_of_blocks * sizeof(S), cudaMemcpyDeviceToHost, stream));
segment_offset += segment_size;
}
segment_size /= arity;
subtree_height--;
number_of_blocks /= arity;
config.aligned = true;
}
return CHK_LAST();
}
template <typename S, int T>
cudaError_t build_merkle_tree(
const S* leaves,
S* digests,
uint32_t height,
const poseidon::PoseidonConstants<S>& poseidon,
const TreeBuilderConfig& config)
{
CHK_INIT_IF_RETURN();
cudaStream_t& stream = config.ctx.stream;
int arity = T - 1;
uint32_t number_of_leaves = pow(arity, (height - 1));
// This will determine how much splitting do we need to do
// `number_of_streams` subtrees should fit in the device
// This means each subtree should fit in `STREAM_CHUNK_SIZE` memory
uint32_t number_of_subtrees = 1;
uint32_t subtree_height = height;
uint32_t subtree_leaves_size = pow(arity, height - 1);
uint32_t subtree_state_size = subtree_leaves_size / arity * T;
uint32_t subtree_digests_size = get_digests_len<S>(subtree_height, arity);
size_t subtree_memory_required = sizeof(S) * (subtree_state_size + subtree_digests_size);
while (subtree_memory_required > STREAM_CHUNK_SIZE) {
number_of_subtrees *= arity;
subtree_height--;
subtree_leaves_size /= arity;
subtree_state_size = subtree_leaves_size / arity * T;
subtree_digests_size = subtree_state_size / arity;
subtree_memory_required = sizeof(S) * (subtree_state_size + subtree_digests_size);
}
int cap_height = height - subtree_height + 1;
size_t caps_len = pow(arity, cap_height - 1);
size_t available_memory, _total_memory;
CHK_IF_RETURN(cudaMemGetInfo(&available_memory, &_total_memory));
available_memory -= GIGA / 8; // Leave 128 MB
// We can effectively parallelize memory copy with streams
// as long as they don't operate on more than `STREAM_CHUNK_SIZE` bytes
const size_t number_of_streams = std::min((uint32_t)(available_memory / STREAM_CHUNK_SIZE), number_of_subtrees);
cudaStream_t* streams = static_cast<cudaStream_t*>(malloc(sizeof(cudaStream_t) * number_of_streams));
for (size_t i = 0; i < number_of_streams; i++) {
CHK_IF_RETURN(cudaStreamCreate(&streams[i]));
}
#if !defined(__CUDA_ARCH__) && defined(MERKLE_DEBUG)
std::cout << "Available memory = " << available_memory / 1024 / 1024 << " MB" << std::endl;
std::cout << "Number of streams = " << number_of_streams << std::endl;
std::cout << "Number of subtrees = " << number_of_subtrees << std::endl;
std::cout << "Height of a subtree = " << subtree_height << std::endl;
std::cout << "Cutoff height = " << height - subtree_height + 1 << std::endl;
std::cout << "Number of leaves in a subtree = " << subtree_leaves_size << std::endl;
std::cout << "State of a subtree = " << subtree_state_size << std::endl;
std::cout << "Digest elements for a subtree = " << get_digests_len<S>(subtree_height, arity) << std::endl;
std::cout << "Size of 1 subtree states = " << subtree_state_size * sizeof(S) / 1024 / 1024 << " MB" << std::endl;
std::cout << "Size of 1 subtree digests = " << subtree_digests_size * sizeof(S) / 1024 / 1024 << " MB" << std::endl;
#endif
// Allocate memory for the leaves and digests
// These are shared by streams in a pool
S *states_ptr, *digests_ptr;
CHK_IF_RETURN(cudaMallocAsync(&states_ptr, subtree_state_size * number_of_streams * sizeof(S), stream))
CHK_IF_RETURN(cudaMallocAsync(&digests_ptr, subtree_digests_size * number_of_streams * sizeof(S), stream))
// Wait for these allocations to finish
CHK_IF_RETURN(cudaStreamSynchronize(stream));
bool caps_mode = config.keep_rows && config.keep_rows < cap_height;
S* caps;
if (caps_mode) { caps = static_cast<S*>(malloc(caps_len * sizeof(S))); }
for (size_t subtree_idx = 0; subtree_idx < number_of_subtrees; subtree_idx++) {
size_t stream_idx = subtree_idx % number_of_streams;
cudaStream_t subtree_stream = streams[stream_idx];
const S* subtree_leaves = leaves + subtree_idx * subtree_leaves_size;
S* subtree_state = states_ptr + stream_idx * subtree_state_size;
S* subtree_digests = digests_ptr + stream_idx * subtree_digests_size;
// Copy the first level from RAM / device to device
// The pitch property of cudaMemcpy2D resolves shape differences
CHK_IF_RETURN(cudaMemcpy2DAsync(
subtree_state, T * sizeof(S), // Device pointer and device pitch
subtree_leaves, arity * sizeof(S), // Host pointer and pitch
arity * sizeof(S), // Size of the source matrix (Arity)
subtree_leaves_size / arity, // Size of the source matrix (Number of blocks)
config.are_inputs_on_device ? cudaMemcpyDeviceToDevice : cudaMemcpyHostToDevice, subtree_stream));
int subtree_keep_rows = 0;
if (config.keep_rows) {
int diff = config.keep_rows - cap_height + 1;
subtree_keep_rows = diff <= 0 ? 1 : diff;
}
size_t start_segment_size = number_of_leaves / arity;
cudaError_t subtree_result = build_merkle_subtree<S, T>(
subtree_state, // state
subtree_digests, // digests
subtree_idx, // subtree_idx
subtree_height, // subtree_height
caps_mode ? caps : digests, // big_tree_digests
start_segment_size, // start_segment_size
0, // start_segment_offset
subtree_keep_rows, // keep_rows
poseidon, // hash
subtree_stream // stream
);
CHK_IF_RETURN(subtree_result);
}
for (size_t i = 0; i < number_of_streams; i++) {
CHK_IF_RETURN(cudaStreamSynchronize(streams[i]));
}
// Finish the top-level tree if any
if (cap_height > 1) {
size_t start_segment_size = caps_len / arity;
size_t start_segment_offset = 0;
if (!caps_mode) {
size_t layer_size = pow(arity, config.keep_rows - 1);
for (int i = 0; i < config.keep_rows - cap_height + 1; i++) {
start_segment_offset += layer_size;
layer_size /= arity;
}
}
CHK_IF_RETURN(cudaMemcpy2DAsync(
states_ptr, T * sizeof(S), caps_mode ? caps : (digests + start_segment_offset - caps_len), arity * sizeof(S),
arity * sizeof(S),
caps_len / arity, // Size of the source
cudaMemcpyHostToDevice, stream)); // Direction and stream
cudaError_t top_tree_result = build_merkle_subtree<S, T>(
states_ptr, // state
digests_ptr, // digests
0, // subtree_idx
cap_height, // subtree_height
digests, // big_tree_digests
start_segment_size, // start_segment_size
start_segment_offset, // start_segment_offset
config.keep_rows, // keep_rows
poseidon, // hash
stream // stream
);
CHK_IF_RETURN(top_tree_result);
if (caps_mode) { free(caps); }
}
CHK_IF_RETURN(cudaFreeAsync(states_ptr, stream));
CHK_IF_RETURN(cudaFreeAsync(digests_ptr, stream));
if (!config.is_async) return CHK_STICKY(cudaStreamSynchronize(stream));
for (size_t i = 0; i < number_of_streams; i++) {
CHK_IF_RETURN(cudaStreamSynchronize(streams[i]));
CHK_IF_RETURN(cudaStreamDestroy(streams[i]));
}
free(streams);
return CHK_LAST();
}
extern "C" cudaError_t CONCAT_EXPAND(CURVE, BuildPoseidonMerkleTree)(
const curve_config::scalar_t* leaves,
curve_config::scalar_t* digests,
uint32_t height,
int arity,
PoseidonConstants<curve_config::scalar_t>& constants,
TreeBuilderConfig& config)
{
switch (arity) {
case 2:
return build_merkle_tree<curve_config::scalar_t, 3>(leaves, digests, height, constants, config);
case 4:
return build_merkle_tree<curve_config::scalar_t, 5>(leaves, digests, height, constants, config);
case 8:
return build_merkle_tree<curve_config::scalar_t, 9>(leaves, digests, height, constants, config);
case 11:
return build_merkle_tree<curve_config::scalar_t, 12>(leaves, digests, height, constants, config);
default:
THROW_ICICLE_ERR(IcicleError_t::InvalidArgument, "BuildPoseidonMerkleTree: #arity must be one of [2, 4, 8, 11]");
}
return CHK_LAST();
}
} // namespace merkle

View File

@@ -0,0 +1,75 @@
#pragma once
#ifndef MERKLE_H
#define MERKLE_H
#include "utils/device_context.cuh"
#include "utils/error_handler.cuh"
#include "utils/utils.h"
#include "appUtils/poseidon/poseidon.cuh"
#include <iostream>
#include <math.h>
using namespace poseidon;
/**
* @namespace merkle
* Implementation of the [Poseidon](@ref poseidon) [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree) builder,
* parallelized for the use on GPU
*/
namespace merkle {
static constexpr size_t GIGA = 1024 * 1024 * 1024;
/// Bytes per stream
static constexpr size_t STREAM_CHUNK_SIZE = 1024 * 1024 * 1024;
/**
* @struct TreeBuilderConfig
* Struct that encodes various Tree builder parameters.
*/
struct TreeBuilderConfig {
device_context::DeviceContext ctx; /**< Details related to the device such as its id and stream id. */
int keep_rows; /**< How many rows of the Merkle tree rows should be written to output. '0' means all of them */
bool are_inputs_on_device; /**< True if inputs are on device and false if they're on host. Default value: false. */
bool is_async; /**< Whether to run the tree builder asynchronously. If set to `true`, the build_merkle_tree
* function will be non-blocking and you'd need to synchronize it explicitly by running
* `cudaStreamSynchronize` or `cudaDeviceSynchronize`. If set to false, the
* function will block the current CPU thread. */
};
template <typename S>
TreeBuilderConfig default_merkle_config()
{
device_context::DeviceContext ctx = device_context::get_default_device_context();
TreeBuilderConfig config = {
ctx, // ctx
0, // keep_rows
false, // are_inputes_on_device
false, // is_async
};
return config;
}
/**
* Builds the Poseidon Merkle tree
*
* @param leaves a pointer to the leaves layer. May be allocated on device or on host, regulated by the config
* Expected to have arity ^ (height - 1) elements
* @param digests a pointer to the digests storage. May only be allocated on the host
* Expected to have `sum(arity ^ (i)) for i in [0..height-1]`
* @param height the height of the merkle tree
* # Algorithm
* The function will split large tree into many subtrees of size that will fit `STREAM_CHUNK_SIZE`.
* Each subtree is build in it's own stream (there is a maximum number of streams)
* After all subtrees are constructed - the function will combine the resulting sub-digests into the final top-tree
*/
template <typename S, int T>
cudaError_t build_merkle_tree(
const S* leaves,
S* digests,
uint32_t height,
const PoseidonConstants<S>& poseidon,
const TreeBuilderConfig& config);
} // namespace merkle
#endif

View File

@@ -0,0 +1,232 @@
// #define DEBUG
#define MERKLE_DEBUG
#define CURVE_ID 2
#include "../../curves/curve_config.cuh"
#include "../poseidon/poseidon.cu"
#include "merkle.cu"
#ifndef __CUDA_ARCH__
#include <cassert>
#include <chrono>
#include <fstream>
#include <iostream>
#include <math.h>
using namespace poseidon;
using namespace merkle;
using namespace curve_config;
using FpMilliseconds = std::chrono::duration<float, std::chrono::milliseconds::period>;
// Arity
#define A 2
#define T (A + 1)
#define START_TIMER(timer) auto timer##_start = std::chrono::high_resolution_clock::now();
#define END_TIMER(timer, msg) \
printf("%s: %.0f ms\n", msg, FpMilliseconds(std::chrono::high_resolution_clock::now() - timer##_start).count());
int main(int argc, char* argv[])
{
// Load poseidon constants
START_TIMER(timer_const);
device_context::DeviceContext ctx = device_context::get_default_device_context();
PoseidonConstants<scalar_t> constants;
init_optimized_poseidon_constants<scalar_t>(A, ctx, &constants);
END_TIMER(timer_const, "Load poseidon constants");
/// Tree of height N and arity A contains \sum{A^i} for i in 0..N-1 elements
uint32_t tree_height = argc > 1 ? atoi(argv[1]) : 28;
uint32_t number_of_leaves = pow(A, (tree_height - 1));
/// Use keep_rows to specify how many rows do you want to store
int keep_rows = argc > 2 ? atoi(argv[2]) : 7;
size_t digests_len = get_digests_len<scalar_t>(keep_rows + 1, A);
/// Fill leaves with scalars [0, 1, ... 2^{tree_height - 1} - 1]
START_TIMER(timer_allocation);
scalar_t input = scalar_t::zero();
size_t leaves_mem = number_of_leaves * sizeof(scalar_t);
scalar_t* leaves = static_cast<scalar_t*>(malloc(leaves_mem));
for (uint32_t i = 0; i < number_of_leaves; i++) {
leaves[i] = input;
input = input + scalar_t::one();
}
END_TIMER(timer_allocation, "Allocated memory for leaves: ");
/// Allocate memory for digests of {keep_rows} rows of a tree
START_TIMER(timer_digests);
size_t digests_mem = digests_len * sizeof(scalar_t);
scalar_t* digests = static_cast<scalar_t*>(malloc(digests_mem));
END_TIMER(timer_digests, "Allocated memory for digests");
std::cout << "Memory for leaves = " << leaves_mem / 1024 / 1024 << " MB; " << leaves_mem / 1024 / 1024 / 1024 << " GB"
<< std::endl;
std::cout << "Number of leaves = " << number_of_leaves << std::endl;
std::cout << "Memory for digests = " << digests_mem / 1024 / 1024 << " MB; " << digests_mem / 1024 / 1024 / 1024
<< " GB" << std::endl;
std::cout << "Number of digest elements = " << digests_len << std::endl;
std::cout << "Total RAM consumption = " << (digests_mem + leaves_mem) / 1024 / 1024 << " MB; "
<< (digests_mem + leaves_mem) / 1024 / 1024 / 1024 << " GB" << std::endl;
TreeBuilderConfig config = default_merkle_config<scalar_t>();
config.keep_rows = keep_rows;
START_TIMER(timer_merkle);
build_merkle_tree<scalar_t, T>(leaves, digests, tree_height, constants, config);
END_TIMER(timer_merkle, "Merkle tree built: ")
// Use this to generate test vectors
// for (int i = 0; i < digests_len; i++) {
// std::cout << "{";
// for (int j = 0; j < 8; j++) {
// std::cout << ((uint32_t*)&digests[i].limbs_storage)[j];
// if (j != 7) { std::cout << ", "; }
// }
// std::cout << "}," << std::endl;
// }
/// These scalars are digests of top-7 rows of a Merkle tree.
/// Arity = 2, Tree height = 28, keep_rows = 7
/// They are aligned in the following format:
/// L-7 L-6 L-5 L-4 L-3 L-2 L-1
/// [0..63, 64..95, 96..111, 112..119, 120..123, 124..125, 126]
scalar_t expected[127] = {
{4014678573, 3239475319, 2314653449, 2957861895, 184634791, 3566699115, 1715234110, 445241356},
{3505304837, 808365954, 2275962701, 1423516791, 1541423175, 1724600321, 226400287, 1785337258},
{2768067495, 1388793239, 3997362411, 3464112194, 1973409933, 3681177968, 369038929, 472214073},
{3654540770, 1469373240, 3282216438, 3185867716, 967978472, 1078033214, 78178652, 1830882032},
{490654422, 2525841937, 626688402, 392783721, 58008344, 2145494226, 3242212931, 274501152},
{3208477883, 3672804666, 239023996, 4035696816, 3265333325, 958951383, 181142437, 1239596452},
{2969561146, 4232772549, 1556993315, 1662679755, 2909096842, 1439765953, 667526824, 31896728},
{270711313, 12541629, 3347448212, 371567649, 1759208919, 1723057963, 674629473, 1013019629},
{3017193939, 2312231485, 3737856857, 1313052851, 1196989094, 1495382607, 1023560618, 191925995},
{293080341, 2290910595, 1543157595, 1127848979, 1815285163, 1706352673, 992727722, 591498292},
{3549180392, 2881478531, 2660879925, 1033961139, 2665175480, 2168183735, 3470220482, 849297604},
{2676239944, 3976356940, 2496525380, 1062384559, 686775848, 383379365, 3327679344, 1084443735},
{1971496657, 3091116054, 3681796601, 1418643776, 3299382044, 3857762866, 3539539764, 1326628306},
{630542774, 2408275928, 345917713, 687603493, 3754703591, 3550860419, 2083130565, 770159409},
{870888363, 224377249, 3918057820, 3375742820, 2591003788, 2510070975, 3414099060, 340676877},
{1875497580, 1000647891, 3811413293, 2001777831, 2253197473, 2328265571, 3906271342, 325320055},
{1211415187, 488575334, 500057698, 3280169717, 3697121693, 1438922954, 931086076, 620808784},
{1983293587, 2168562163, 3150824284, 3953229527, 436705204, 778341443, 3005081450, 615688285},
{8576698, 3039838264, 671609957, 2809480667, 1620765047, 2117613498, 845941068, 455906025},
{3906454348, 552283113, 645539850, 1316302434, 475485784, 2970197657, 274367582, 652809948},
{1495892355, 1668646651, 14573046, 3197699206, 1489087180, 3696907100, 1481991676, 1713300937},
{3908239800, 3651019608, 3657128408, 1619241101, 2332486430, 639066149, 2163145035, 1704519873},
{601030174, 3024947211, 2825655316, 3863514365, 749861326, 2966866369, 3590896102, 1433089687},
{2395125120, 2965783566, 2438173617, 591910928, 2454874264, 961201946, 3519059509, 1507770765},
{4191182380, 3986691529, 626672198, 248199214, 2672873299, 1206537516, 4088647235, 643192311},
{117373628, 2684708015, 1169533105, 4189512228, 39193015, 2348829360, 1044287150, 1200318263},
{1641468310, 184141241, 1225984447, 3048243879, 3866424998, 3590741693, 2510098016, 274097253},
{805243263, 2967911161, 448427804, 518401505, 404860884, 1593480904, 2843784918, 1217394583},
{1068910868, 4126158631, 3428265735, 1108050638, 1809164812, 2970655045, 916421502, 271686161},
{2733872014, 3489610640, 2253010322, 2061408208, 209418696, 317724116, 2610511126, 777106564},
{922170442, 222695863, 4287857383, 1811357610, 3378048665, 339334666, 156256237, 1016584661},
{2369411073, 3974740601, 3926942050, 1092836585, 2755312327, 492736530, 1777994166, 1608349307},
{3791947957, 2178563469, 2708512205, 2104428978, 4281280169, 3158413195, 3714473637, 1932398193},
{447850698, 872401916, 1547172058, 1242046677, 1896710579, 1137814221, 1602961798, 1571006781},
{3469094617, 1395163063, 1061899259, 4059174800, 2083256644, 1034754354, 941298869, 416224305},
{3468873946, 3282426941, 497353671, 953582605, 1587266512, 1740183981, 671088260, 1875939615},
{3123987458, 2866544187, 2718390509, 359710965, 259964033, 4177846877, 740787103, 1674220305},
{783470818, 2334330296, 116871553, 1683603268, 759950366, 3727285592, 3616407101, 525075992},
{3899236298, 1976585972, 3822298095, 748204764, 2978121214, 200932224, 1340374715, 79506611},
{4128139198, 1939423978, 3727937787, 1855257827, 630776586, 3470334826, 2498269262, 1495682436},
{1348892970, 629870637, 3595702369, 770535689, 2256370736, 3444863052, 891128487, 33159341},
{2967968932, 4261332726, 2399871138, 3313204386, 3991787238, 3531989490, 1624489585, 1792543190},
{2179587180, 3737311899, 6357632, 3727566655, 3147613619, 2123011127, 3097377147, 992191510},
{2182997143, 4101668140, 1996814613, 3581332946, 867002512, 1843201087, 4223363546, 1928077695},
{3028933890, 3818130584, 3557387839, 2374442829, 911787393, 770676400, 337096879, 240174663},
{3043672379, 1899171261, 504182702, 3054796849, 3828295843, 671705157, 3825173996, 1414902488},
{376706115, 4194608423, 3594444823, 1625946449, 403768163, 2456846785, 44584848, 84202017},
{3064390814, 2716996388, 3897127426, 2552999677, 3013912747, 1452854892, 3978189518, 1284784196},
{3199468428, 2903114874, 72247584, 2410968068, 2003524493, 4123634338, 435261870, 701486964},
{828746335, 3913770139, 1618218172, 3278121503, 2079247718, 1978636954, 910397289, 96552693},
{2211749314, 3357848024, 3537465382, 3532697268, 2132182736, 1966921197, 1315441937, 1777448477},
{2200248661, 391584476, 2234387592, 2850080717, 2653523993, 3210840164, 2903237762, 1794426612},
{554961316, 3167917252, 4221707975, 1012517545, 736753739, 110159932, 145505695, 227111964},
{3875411415, 1613987934, 1084962884, 3019340126, 2315514976, 2094187263, 3497819348, 291280352},
{1739154213, 3075259556, 3948002791, 1367585114, 1701733509, 3623455345, 3493135301, 1923303618},
{1827158133, 3185981038, 878567088, 2821327133, 3663194195, 4207640486, 3564098674, 1184657196},
{3758205899, 815333136, 2869440132, 1818787811, 1081254131, 1686248084, 1041506138, 381361493},
{3628751007, 777692999, 4180345157, 4240314964, 3494907162, 692170506, 2009935286, 1322780120},
{887413708, 23343153, 3555916162, 3334619375, 2777783869, 2643155742, 1986000428, 630137915},
{4096942349, 4145995447, 897723766, 2990805409, 4029167381, 2495258909, 4088391249, 609264602},
{612790660, 2661534473, 247248263, 1136855857, 1161947150, 1315488261, 3680235769, 1229555413},
{722881024, 192705355, 1754502243, 2697508301, 2945854931, 3504159885, 3445165630, 238076059},
{222219657, 3437798818, 2744917536, 224256950, 1904282056, 105073736, 772693683, 292110801},
{1796528306, 837471306, 1567726322, 1897641954, 3078024748, 3325524011, 3385455562, 951015556},
{2407829502, 3164486936, 1487657, 4182947136, 3849960947, 3830817126, 1138850753, 130868390},
{1678448871, 4289024418, 2650638643, 67119139, 3182458352, 3617797334, 628056031, 191722155},
{3324568052, 2439834493, 375051932, 2170654759, 2348874396, 3713035811, 4199471222, 283037510},
{773651958, 2125754199, 199104117, 213977251, 367104435, 3157051288, 2326732794, 275408701},
{2460311739, 1791145889, 3915204508, 1272346229, 2834150762, 3900627326, 148310662, 1750293843},
{2130280064, 2159064112, 1437026429, 2733029067, 294236910, 1031342011, 2050658606, 1599487766},
{1105683188, 141496307, 1055256037, 646629220, 2520569043, 2475709708, 3396934601, 14464324},
{2583712422, 1775337336, 2707695021, 1832388440, 4273340329, 2820244513, 2507193407, 1404928203},
{1269867733, 2223696603, 1861500459, 2421626755, 3502911196, 2815315379, 549184699, 16975667},
{426922700, 3601088398, 4218337357, 1790978582, 4136866168, 4035715442, 2326873078, 235501400},
{3402079655, 566662668, 553974537, 3190505572, 1786065459, 1789792927, 1424842205, 1816440650},
{3848752806, 4260216223, 570978505, 2129098465, 232557756, 7106551, 687416267, 1391264097},
{670044537, 331678659, 344646513, 3012645076, 36483293, 2753421815, 1891477861, 132172653},
{7972615, 3961288035, 486739371, 228490819, 1675833723, 2413801880, 3168288162, 1168358009},
{1158898905, 554620687, 1047964985, 3481289834, 4201243048, 75670413, 821867979, 231202146},
{520239323, 1484416551, 3247647509, 1403067553, 1541879850, 3785630405, 1117276684, 85930988},
{1931565112, 3470042772, 3537431410, 2318191566, 3569616629, 726769301, 1627396145, 328822231},
{1678673875, 3628799819, 1857961535, 915186097, 3107622699, 3356035428, 3569404983, 1026648178},
{913100268, 3846074625, 3740179720, 3454441980, 4093733473, 1346792008, 1163703973, 57404821},
{1519620766, 1446603960, 2393223713, 1459282094, 1023092791, 2199286418, 2096208360, 699051559},
{2219754787, 2500265080, 191792189, 1542645732, 1161454113, 2969905865, 3687236724, 10987900},
{1743254412, 82672506, 3135049363, 887561904, 928784698, 2657646376, 288207897, 435585205},
{3465517945, 3661905420, 2283191968, 3144869509, 3579046583, 2037447473, 1533911765, 211714799},
{2496160147, 1418604384, 1802507016, 1336309157, 1184402515, 206535898, 128178254, 1130299949},
{1961044967, 3019089825, 2997612316, 265811573, 3263223461, 3365214238, 1135984798, 1595605265},
{1483975027, 2808544081, 229106190, 450319819, 1309534945, 3830732771, 1124125013, 281790638},
{3747100452, 107125028, 607240855, 1725178756, 3597437256, 3348335091, 2059188672, 1306485825},
{79235387, 3022164429, 1311047830, 3155028639, 916354506, 2279445426, 83551349, 1018957398},
{2095857719, 3787514231, 1479065116, 482814220, 2228714165, 221954960, 4265971691, 1649347513},
{3188516880, 3597244272, 108997607, 353456206, 3223447432, 446895619, 2424498673, 1113641309},
{57226169, 310268999, 3047208456, 3667872449, 1342874028, 1962131723, 2203612281, 717247378},
{2523858162, 3604519783, 975513559, 4197094359, 2071686613, 2849799672, 321527978, 1319949504},
{1913310732, 564832490, 732308813, 2586688178, 4054037287, 2720587232, 909207782, 1001626679},
{4229590794, 3110228758, 4161959585, 4143451607, 1274301898, 499710233, 2801297171, 1696629637},
{4283094591, 2041587860, 4272909686, 3032518657, 1275782421, 638738833, 562322176, 1764251529},
{2166709661, 2507730084, 2167347676, 730353949, 4164042092, 702353215, 1692356174, 630374766},
{4139857027, 2019739214, 1365461556, 190724658, 3746881101, 1551936264, 2542716346, 641014892},
{3705816562, 1108147156, 1905372056, 2828808730, 1103318054, 1094456706, 1691237212, 559553895},
{4190357473, 2752760058, 3475907929, 138551448, 2308123641, 1481331661, 4250021059, 794197070},
{2204086692, 2614018999, 4066699829, 2915251952, 1331110005, 2166000553, 3772671276, 708029528},
{3657760721, 2239870589, 436271478, 2779795192, 24689995, 1297075264, 1671988556, 523339184},
{884023061, 652621983, 62069329, 1533423970, 279751135, 1384150035, 2787913666, 1184536494},
{3832683369, 3220024363, 3898646445, 3616156349, 1816361908, 58265014, 2258645744, 1322185408},
{2186663972, 992823504, 580939809, 4134674086, 3099356555, 1833300007, 689911450, 1656492952},
{702913923, 1119558248, 3418804320, 2560686829, 3844756959, 2394009955, 1587731805, 1401231027},
{3358386685, 971450419, 3942097807, 1751903613, 2010516544, 1867306049, 3690714201, 1210488397},
{3401698520, 536404836, 794977212, 3865401272, 293002110, 51547193, 2792668565, 794288943},
{2535122179, 3301198404, 1831226988, 939065116, 2928600716, 482930544, 1250716156, 1762491790},
{754984898, 38001923, 2689392564, 1026928647, 489115662, 455738937, 2410612830, 1307198941},
{172844518, 1699952276, 1485123703, 1156671318, 3179094813, 1188798431, 112551259, 1847940720},
{3545208113, 2289833538, 2649086240, 2023989097, 3349947852, 696893462, 687717267, 1856396125},
{3910459449, 3705304614, 3532442943, 3481251226, 895301896, 2986084489, 3299258047, 1761424967},
{2725674291, 116532222, 3565763434, 1451215632, 1092995942, 2035526239, 1045737445, 1613646521},
{4130593726, 3024687204, 1021306812, 2469430501, 497194315, 2974581792, 1255017536, 597255305},
{523876618, 761620533, 3490119046, 1051900320, 1477617871, 2040477122, 3099679769, 1824205879},
{4238414613, 3935907273, 2701284094, 1435298493, 2842895400, 3094051424, 2033163260, 1083623692},
{642361789, 1604941380, 2400216156, 566375932, 30286136, 1212333003, 4008803597, 1648114656},
{4152681515, 2343918434, 2094367525, 1848475261, 1968024081, 2889245354, 3005464900, 1786065752},
{1081950398, 3116162259, 3148939719, 2734634826, 499009055, 1696427234, 1357324515, 801013715},
{998077513, 4143316008, 3160753512, 1619454701, 647700665, 2041647364, 4250743448, 876294208},
{42525978, 2569996457, 3426184151, 3354583607, 4034764830, 1228943658, 894711986, 618735436},
{885919544, 1214434554, 3367715898, 2051767156, 3953912206, 4149513394, 37475677, 727749078},
{4011510622, 1671330619, 385068934, 1150628512, 1425291267, 724496539, 1547726122, 746328500},
};
for (int i = 0; i < digests_len; i++) {
scalar_t root = digests[i];
assert(root == expected[i]);
}
free(digests);
free(leaves);
}
#endif

View File

@@ -34,7 +34,7 @@ namespace utils_internal {
{
int tid = blockDim.x * blockIdx.x + threadIdx.x;
if (tid < n_elements * batch_size) {
int scalar_id = tid % n_elements;
int64_t scalar_id = tid % n_elements;
if (bitrev) scalar_id = __brev(scalar_id) >> (32 - logn);
out_vec[tid] = *(scalar_vec + ((scalar_id * step) % n_scalars)) * in_vec[tid];
}

View File

@@ -1,6 +1,6 @@
[package]
name = "icicle-core"
version = "1.0.0"
version = "1.2.0"
edition = "2021"
authors = [ "Ingonyama" ]
description = "A library for GPU ZK acceleration by Ingonyama"

View File

@@ -3,10 +3,12 @@ pub mod error;
pub mod field;
pub mod msm;
pub mod ntt;
pub mod poseidon;
#[cfg(feature = "arkworks")]
#[doc(hidden)]
pub mod tests;
pub mod traits;
pub mod tree;
pub trait SNARKCurve: curve::Curve + msm::MSM<Self>
where

View File

@@ -208,7 +208,7 @@ macro_rules! impl_ntt_tests {
(
$field:ident
) => {
const MAX_SIZE: u64 = 1 << 16;
const MAX_SIZE: u64 = 1 << 17;
static INIT: OnceLock<()> = OnceLock::new();
#[test]

View File

@@ -48,7 +48,7 @@ where
F::ArkEquivalent: FftField,
<F as FieldImpl>::Config: NTT<F> + GenerateRandom<F>,
{
let test_sizes = [1 << 4, 1 << 16];
let test_sizes = [1 << 4, 1 << 17];
for test_size in test_sizes {
let ark_domain = GeneralEvaluationDomain::<F::ArkEquivalent>::new(test_size).unwrap();
@@ -161,7 +161,7 @@ where
<F as FieldImpl>::Config: NTT<F> + GenerateRandom<F>,
{
let mut seed = test_rng();
let test_sizes = [1 << 4, 1 << 16];
let test_sizes = [1 << 4, 1 << 17];
for test_size in test_sizes {
let coset_generators = [
F::ArkEquivalent::rand(&mut seed),

View File

@@ -0,0 +1,307 @@
#[doc(hidden)]
pub mod tests;
use icicle_cuda_runtime::{
device_context::{get_default_device_context, DeviceContext},
memory::HostOrDeviceSlice,
};
use crate::{error::IcicleResult, traits::FieldImpl};
#[repr(C)]
#[derive(Debug, Clone)]
pub struct PoseidonConstants<'a, F: FieldImpl> {
arity: u32,
partial_rounds: u32,
full_rounds_half: u32,
/// These should be pointers to data allocated on device
round_constants: &'a [F],
mds_matrix: &'a [F],
non_sparse_matrix: &'a [F],
sparse_matrices: &'a [F],
/// Domain tag is the first element in the Poseidon state.
/// For the Merkle tree mode it should equal 2^arity - 1
domain_tag: F,
}
/// Struct that encodes Poseidon parameters to be passed into the [poseidon_hash_many](poseidon_hash_many) function.
#[repr(C)]
#[derive(Debug, Clone)]
pub struct PoseidonConfig<'a> {
/// Details related to the device such as its id and stream id. See [DeviceContext](@ref device_context::DeviceContext).
pub ctx: DeviceContext<'a>,
are_inputs_on_device: bool,
are_outputs_on_device: bool,
/// If true, input is considered to be a states vector, holding the preimages
/// in aligned or not aligned format. Memory under the input pointer will be used for states
/// If false, fresh states memory will be allocated and input will be copied into it
pub input_is_a_state: bool,
/// If true - input should be already aligned for poseidon permutation.
/// Aligned format: [0, A, B, 0, C, D, ...] (as you might get by using loop_state)
/// not aligned format: [A, B, 0, C, D, 0, ...] (as you might get from cudaMemcpy2D)
pub aligned: bool,
/// If true, hash results will also be copied in the input pointer in aligned format
pub loop_state: bool,
/// Whether to run Poseidon asynchronously. If set to `true`, Poseidon will be non-blocking
/// and you'd need to synchronize it explicitly by running `cudaStreamSynchronize` or `cudaDeviceSynchronize`.
/// If set to `false`, Poseidon will block the current CPU thread.
pub is_async: bool,
}
impl<'a> Default for PoseidonConfig<'a> {
fn default() -> Self {
let ctx = get_default_device_context();
Self {
ctx,
are_inputs_on_device: false,
are_outputs_on_device: false,
input_is_a_state: false,
aligned: false,
loop_state: false,
is_async: false,
}
}
}
pub trait Poseidon<F: FieldImpl> {
fn create_optimized_constants<'a>(
arity: u32,
full_rounds_half: u32,
partial_rounds: u32,
constants: &mut [F],
ctx: &DeviceContext,
) -> IcicleResult<PoseidonConstants<'a, F>>;
fn load_optimized_constants<'a>(arity: u32, ctx: &DeviceContext) -> IcicleResult<PoseidonConstants<'a, F>>;
fn poseidon_unchecked(
input: &mut HostOrDeviceSlice<F>,
output: &mut HostOrDeviceSlice<F>,
number_of_states: u32,
arity: u32,
constants: &PoseidonConstants<F>,
config: &PoseidonConfig,
) -> IcicleResult<()>;
}
/// Loads pre-calculated poseidon constants on the GPU.
pub fn load_optimized_poseidon_constants<'a, F>(
arity: u32,
ctx: &DeviceContext,
) -> IcicleResult<PoseidonConstants<'a, F>>
where
F: FieldImpl,
<F as FieldImpl>::Config: Poseidon<F>,
{
<<F as FieldImpl>::Config as Poseidon<F>>::load_optimized_constants(arity, ctx)
}
/// Creates new instance of poseidon constants on the GPU.
pub fn create_optimized_poseidon_constants<'a, F>(
arity: u32,
ctx: &DeviceContext,
full_rounds_half: u32,
partial_rounds: u32,
constants: &mut [F],
) -> IcicleResult<PoseidonConstants<'a, F>>
where
F: FieldImpl,
<F as FieldImpl>::Config: Poseidon<F>,
{
<<F as FieldImpl>::Config as Poseidon<F>>::create_optimized_constants(
arity,
full_rounds_half,
partial_rounds,
constants,
ctx,
)
}
/// Computes the poseidon hashes for multiple preimages.
///
/// # Arguments
///
/// * `input` - a pointer to the input data. May point to a vector of preimages or a vector of states filled with preimages.
///
/// * `output` - a pointer to the output data. Must be at least of size [number_of_states](number_of_states)
///
/// * `number_of_states` - number of input blocks of size `arity`
///
/// * `arity` - the arity of the hash function (the size of 1 preimage)
///
/// * `constants` - Poseidon constants.
///
/// * `config` - config used to specify extra arguments of the Poseidon.
pub fn poseidon_hash_many<F>(
input: &mut HostOrDeviceSlice<F>,
output: &mut HostOrDeviceSlice<F>,
number_of_states: u32,
arity: u32,
constants: &PoseidonConstants<F>,
config: &PoseidonConfig,
) -> IcicleResult<()>
where
F: FieldImpl,
<F as FieldImpl>::Config: Poseidon<F>,
{
let input_len_required = if config.input_is_a_state {
number_of_states * (arity + 1)
} else {
number_of_states * arity
};
if input.len() < input_len_required as usize {
panic!(
"input len is {}; but needs to be at least {}",
input.len(),
input_len_required
);
}
if output.len() < number_of_states as usize {
panic!(
"output len is {}; but needs to be at least {}",
output.len(),
number_of_states
);
}
let mut local_cfg = config.clone();
local_cfg.are_inputs_on_device = input.is_on_device();
local_cfg.are_outputs_on_device = output.is_on_device();
<<F as FieldImpl>::Config as Poseidon<F>>::poseidon_unchecked(
input,
output,
number_of_states,
arity,
constants,
&local_cfg,
)
}
#[macro_export]
macro_rules! impl_poseidon {
(
$field_prefix:literal,
$field_prefix_ident:ident,
$field:ident,
$field_config:ident
) => {
mod $field_prefix_ident {
use crate::poseidon::{$field, $field_config, CudaError, DeviceContext, PoseidonConfig, PoseidonConstants};
extern "C" {
#[link_name = concat!($field_prefix, "CreateOptimizedPoseidonConstants")]
pub(crate) fn _create_optimized_constants(
arity: u32,
full_rounds_half: u32,
partial_rounds: u32,
constants: *mut $field,
ctx: &DeviceContext,
poseidon_constants: *mut PoseidonConstants<$field>,
) -> CudaError;
#[link_name = concat!($field_prefix, "InitOptimizedPoseidonConstants")]
pub(crate) fn _load_optimized_constants(
arity: u32,
ctx: &DeviceContext,
constants: *mut PoseidonConstants<$field>,
) -> CudaError;
#[link_name = concat!($field_prefix, "PoseidonHash")]
pub(crate) fn hash_many(
input: *mut $field,
output: *mut $field,
number_of_states: u32,
arity: u32,
constants: &PoseidonConstants<$field>,
config: &PoseidonConfig,
) -> CudaError;
}
}
impl Poseidon<$field> for $field_config {
fn create_optimized_constants<'a>(
arity: u32,
full_rounds_half: u32,
partial_rounds: u32,
constants: &mut [$field],
ctx: &DeviceContext,
) -> IcicleResult<PoseidonConstants<'a, $field>> {
unsafe {
let mut poseidon_constants = MaybeUninit::<PoseidonConstants<'a, $field>>::uninit();
let err = $field_prefix_ident::_create_optimized_constants(
arity,
full_rounds_half,
partial_rounds,
constants as *mut _ as *mut $field,
ctx,
poseidon_constants.as_mut_ptr(),
)
.wrap();
err.and(Ok(poseidon_constants.assume_init()))
}
}
fn load_optimized_constants<'a>(
arity: u32,
ctx: &DeviceContext,
) -> IcicleResult<PoseidonConstants<'a, $field>> {
unsafe {
let mut constants = MaybeUninit::<PoseidonConstants<'a, $field>>::uninit();
let err = $field_prefix_ident::_load_optimized_constants(arity, ctx, constants.as_mut_ptr()).wrap();
err.and(Ok(constants.assume_init()))
}
}
fn poseidon_unchecked(
input: &mut HostOrDeviceSlice<$field>,
output: &mut HostOrDeviceSlice<$field>,
number_of_states: u32,
arity: u32,
constants: &PoseidonConstants<$field>,
config: &PoseidonConfig,
) -> IcicleResult<()> {
unsafe {
$field_prefix_ident::hash_many(
input.as_mut_ptr(),
output.as_mut_ptr(),
number_of_states,
arity,
constants,
config,
)
.wrap()
}
}
}
};
}
#[macro_export]
macro_rules! impl_poseidon_tests {
(
$field:ident,
$field_bytes:literal,
$field_prefix:literal,
$partial_rounds:literal
) => {
#[test]
fn test_poseidon_hash_many() {
check_poseidon_hash_many::<$field>()
}
#[test]
fn test_poseidon_custom_config() {
check_poseidon_custom_config::<$field>($field_bytes, $field_prefix, $partial_rounds)
}
};
}

View File

@@ -0,0 +1,104 @@
use crate::traits::FieldImpl;
use icicle_cuda_runtime::device_context::get_default_device_context;
use icicle_cuda_runtime::memory::HostOrDeviceSlice;
use std::io::Read;
use std::path::PathBuf;
use std::{env, fs::File};
use super::{
create_optimized_poseidon_constants, load_optimized_poseidon_constants, poseidon_hash_many, Poseidon,
PoseidonConfig, PoseidonConstants,
};
pub fn init_poseidon<'a, F: FieldImpl>(arity: u32) -> PoseidonConstants<'a, F>
where
<F as FieldImpl>::Config: Poseidon<F>,
{
let ctx = get_default_device_context();
load_optimized_poseidon_constants::<F>(arity, &ctx).unwrap()
}
pub fn _check_poseidon_hash_many<F: FieldImpl>(constants: PoseidonConstants<F>) -> (F, F)
where
<F as FieldImpl>::Config: Poseidon<F>,
{
let test_size = 1 << 10;
let arity = 2u32;
let inputs = vec![F::one(); test_size * arity as usize];
let outputs = vec![F::zero(); test_size];
let mut input_slice = HostOrDeviceSlice::on_host(inputs);
let mut output_slice = HostOrDeviceSlice::on_host(outputs);
let config = PoseidonConfig::default();
poseidon_hash_many::<F>(
&mut input_slice,
&mut output_slice,
test_size as u32,
arity as u32,
&constants,
&config,
)
.unwrap();
let a1 = output_slice[0..1][0];
let a2 = output_slice[output_slice.len() - 2..output_slice.len() - 1][0];
println!("first: {:?}, last: {:?}", a1, a2);
assert_eq!(a1, a2);
(a1, a2)
}
pub fn check_poseidon_hash_many<F: FieldImpl>()
where
<F as FieldImpl>::Config: Poseidon<F>,
{
let arity = 2u32;
let constants = init_poseidon::<F>(arity as u32);
_check_poseidon_hash_many(constants);
}
pub fn check_poseidon_custom_config<F: FieldImpl>(field_bytes: usize, field_prefix: &str, partial_rounds: u32)
where
<F as FieldImpl>::Config: Poseidon<F>,
{
let arity = 2u32;
let constants = init_poseidon::<F>(arity as u32);
let full_rounds_half = 4;
let ctx = get_default_device_context();
let cargo_manifest_dir = env!("CARGO_MANIFEST_DIR");
let constants_file = PathBuf::from(cargo_manifest_dir)
.join("tests")
.join(format!("{}_constants.bin", field_prefix));
let mut constants_buf = vec![];
File::open(constants_file)
.unwrap()
.read_to_end(&mut constants_buf)
.unwrap();
let mut custom_constants = vec![];
for chunk in constants_buf.chunks(field_bytes) {
custom_constants.push(F::from_bytes_le(chunk));
}
let custom_constants = create_optimized_poseidon_constants::<F>(
arity as u32,
&ctx,
full_rounds_half,
partial_rounds,
&mut custom_constants,
)
.unwrap();
let (a1, a2) = _check_poseidon_hash_many(constants);
let (b1, b2) = _check_poseidon_hash_many(custom_constants);
assert_eq!(a1, b1);
assert_eq!(a2, b2);
}

View File

@@ -0,0 +1,167 @@
use icicle_cuda_runtime::{
device_context::{get_default_device_context, DeviceContext},
memory::HostOrDeviceSlice,
};
use crate::{error::IcicleResult, poseidon::PoseidonConstants, traits::FieldImpl};
#[doc(hidden)]
pub mod tests;
/// Struct that encodes Tree Builder parameters to be passed into the [build_merkle_tree](build_merkle_tree) function.
#[repr(C)]
#[derive(Debug, Clone)]
pub struct TreeBuilderConfig<'a> {
/// Details related to the device such as its id and stream id. See [DeviceContext](@ref device_context::DeviceContext).
pub ctx: DeviceContext<'a>,
/// How many rows of the Merkle tree rows should be written to output. '0' means all of them
keep_rows: u32,
are_inputs_on_device: bool,
/// Whether to run build_merkle_tree asynchronously. If set to `true`, TreeBuilder will be non-blocking
/// and you'd need to synchronize it explicitly by running `cudaStreamSynchronize` or `cudaDeviceSynchronize`.
/// If set to `false`, build_merkle_tree will block the current CPU thread.
pub is_async: bool,
}
impl<'a> Default for TreeBuilderConfig<'a> {
fn default() -> Self {
let ctx = get_default_device_context();
Self {
ctx,
keep_rows: 0,
are_inputs_on_device: false,
is_async: false,
}
}
}
pub fn merkle_tree_digests_len(height: u32, arity: u32) -> usize {
let mut digests_len = 0usize;
let mut row_length = 1;
for _ in 1..height {
digests_len += row_length;
row_length *= arity as usize;
}
digests_len
}
pub trait TreeBuilder<F: FieldImpl> {
fn build_poseidon_tree_unchecked(
leaves: &mut HostOrDeviceSlice<F>,
digests: &mut [F],
height: u32,
arity: u32,
constants: &PoseidonConstants<F>,
config: &TreeBuilderConfig,
) -> IcicleResult<()>;
}
/// Builds a Poseidon Merkle tree.
///
/// # Arguments
///
/// * `leaves` - a pointer to the leaves layer. Expected to have arity ^ (height - 1) elements
///
/// * `digests` - a pointer to the digests storage. Expected to have `sum(arity ^ (i)) for i in [0..height-1]`
///
/// * `height` - the height of the merkle tree
///
/// * `config` - config used to specify extra arguments of the Tree builder.
pub fn build_poseidon_merkle_tree<F>(
leaves: &mut HostOrDeviceSlice<F>,
digests: &mut [F],
height: u32,
arity: u32,
constants: &PoseidonConstants<F>,
config: &TreeBuilderConfig,
) -> IcicleResult<()>
where
F: FieldImpl,
<F as FieldImpl>::Config: TreeBuilder<F>,
{
let leaves_len = 1 << (height - 1) as usize;
if leaves.len() != leaves_len {
panic!("Leaves len is {}; but needs to be exactly {}", leaves.len(), leaves_len,);
}
let digests_len = merkle_tree_digests_len(height, arity);
if digests.len() != digests_len as usize {
panic!(
"Digests len is {}; but needs to be exactly {}",
digests.len(),
digests_len
);
}
let mut local_cfg = config.clone();
local_cfg.are_inputs_on_device = leaves.is_on_device();
<<F as FieldImpl>::Config as TreeBuilder<F>>::build_poseidon_tree_unchecked(
leaves, digests, height, arity, constants, &local_cfg,
)
}
#[macro_export]
macro_rules! impl_tree_builder {
(
$field_prefix:literal,
$field_prefix_ident:ident,
$field:ident,
$field_config:ident
) => {
mod $field_prefix_ident {
use crate::tree::{$field, $field_config, CudaError, DeviceContext, TreeBuilderConfig};
use icicle_core::poseidon::PoseidonConstants;
extern "C" {
#[link_name = concat!($field_prefix, "BuildPoseidonMerkleTree")]
pub(crate) fn _build_poseidon_merkle_tree(
leaves: *mut $field,
digests: *mut $field,
height: u32,
arity: u32,
constants: &PoseidonConstants<$field>,
config: &TreeBuilderConfig,
) -> CudaError;
}
}
impl TreeBuilder<$field> for $field_config {
fn build_poseidon_tree_unchecked(
leaves: &mut HostOrDeviceSlice<$field>,
digests: &mut [$field],
height: u32,
arity: u32,
constants: &PoseidonConstants<$field>,
config: &TreeBuilderConfig,
) -> IcicleResult<()> {
unsafe {
$field_prefix_ident::_build_poseidon_merkle_tree(
leaves.as_mut_ptr(),
digests as *mut _ as *mut $field,
height,
arity,
constants,
config,
)
.wrap()
}
}
}
};
}
#[macro_export]
macro_rules! impl_tree_builder_tests {
(
$field:ident
) => {
#[test]
fn test_build_poseidon_merkle_tree() {
check_build_merkle_tree::<$field>()
}
};
}

View File

@@ -0,0 +1,30 @@
use icicle_cuda_runtime::memory::HostOrDeviceSlice;
use crate::{
poseidon::{tests::init_poseidon, Poseidon},
traits::FieldImpl,
tree::{build_poseidon_merkle_tree, merkle_tree_digests_len, TreeBuilderConfig},
};
use super::TreeBuilder;
pub fn check_build_merkle_tree<F: FieldImpl>()
where
<F as FieldImpl>::Config: TreeBuilder<F> + Poseidon<F>,
{
let height = 20;
let arity = 2;
let keep_rows = 1;
let leaves = vec![F::one(); 1 << (height - 1)];
let mut digests = vec![F::zero(); merkle_tree_digests_len(height, arity)];
let mut leaves_slice = HostOrDeviceSlice::on_host(leaves);
let constants = init_poseidon(arity as u32);
let mut config = TreeBuilderConfig::default();
config.keep_rows = keep_rows;
build_poseidon_merkle_tree::<F>(&mut leaves_slice, &mut digests, height, arity, &constants, &config).unwrap();
println!("Root: {:?}", digests[0..1][0]);
}

Binary file not shown.

Binary file not shown.

View File

@@ -1,6 +1,6 @@
[package]
name = "icicle-cuda-runtime"
version = "1.0.0"
version = "1.2.0"
edition = "2021"
authors = [ "Ingonyama" ]
description = "Ingonyama's Rust wrapper of CUDA runtime"

View File

@@ -1,6 +1,6 @@
[package]
name = "icicle-bls12-377"
version = "1.0.0"
version = "1.2.0"
edition = "2021"
authors = [ "Ingonyama" ]
description = "Rust wrapper for the CUDA implementation of BLS12-377 pairing friendly elliptic curve by Ingonyama"

View File

@@ -1,5 +1,7 @@
pub mod curve;
pub mod msm;
pub mod ntt;
pub mod poseidon;
pub mod tree;
impl icicle_core::SNARKCurve for curve::CurveCfg {}

View File

@@ -0,0 +1,27 @@
#[cfg(feature = "bw6-761")]
use crate::curve::{BaseCfg, BaseField};
use crate::curve::{ScalarCfg, ScalarField};
use icicle_core::error::IcicleResult;
use icicle_core::impl_poseidon;
use icicle_core::poseidon::{Poseidon, PoseidonConfig, PoseidonConstants};
use icicle_core::traits::IcicleResultWrap;
use icicle_cuda_runtime::device_context::DeviceContext;
use icicle_cuda_runtime::error::CudaError;
use icicle_cuda_runtime::memory::HostOrDeviceSlice;
use core::mem::MaybeUninit;
impl_poseidon!("bls12_377", bls12_377, ScalarField, ScalarCfg);
#[cfg(feature = "bw6-761")]
impl_poseidon!("bw6_761", bw6_761, BaseField, BaseCfg);
#[cfg(test)]
pub(crate) mod tests {
use crate::curve::ScalarField;
use icicle_core::impl_poseidon_tests;
use icicle_core::poseidon::tests::*;
impl_poseidon_tests!(ScalarField, 32, "bls12_377", 56);
}

View File

@@ -0,0 +1,26 @@
#[cfg(feature = "bw6-761")]
use crate::curve::{BaseCfg, BaseField};
use crate::curve::{ScalarCfg, ScalarField};
use icicle_core::error::IcicleResult;
use icicle_core::impl_tree_builder;
use icicle_core::poseidon::PoseidonConstants;
use icicle_core::traits::IcicleResultWrap;
use icicle_core::tree::{TreeBuilder, TreeBuilderConfig};
use icicle_cuda_runtime::device_context::DeviceContext;
use icicle_cuda_runtime::error::CudaError;
use icicle_cuda_runtime::memory::HostOrDeviceSlice;
impl_tree_builder!("bls12_377", bls12_377, ScalarField, ScalarCfg);
#[cfg(feature = "bw6-761")]
impl_tree_builder!("bw6_761", bw6_761, BaseField, BaseCfg);
#[cfg(test)]
pub(crate) mod tests {
use crate::curve::ScalarField;
use icicle_core::impl_tree_builder_tests;
use icicle_core::tree::tests::*;
impl_tree_builder_tests!(ScalarField);
}

View File

@@ -1,6 +1,6 @@
[package]
name = "icicle-bls12-381"
version = "1.0.0"
version = "1.2.0"
edition = "2021"
authors = [ "Ingonyama" ]
description = "Rust wrapper for the CUDA implementation of BLS12-381 pairing friendly elliptic curve by Ingonyama"

View File

@@ -1,5 +1,7 @@
pub mod curve;
pub mod msm;
pub mod ntt;
pub mod poseidon;
pub mod tree;
impl icicle_core::SNARKCurve for curve::CurveCfg {}

View File

@@ -0,0 +1,22 @@
use crate::curve::{ScalarCfg, ScalarField};
use icicle_core::error::IcicleResult;
use icicle_core::impl_poseidon;
use icicle_core::poseidon::{Poseidon, PoseidonConfig, PoseidonConstants};
use icicle_core::traits::IcicleResultWrap;
use icicle_cuda_runtime::device_context::DeviceContext;
use icicle_cuda_runtime::error::CudaError;
use icicle_cuda_runtime::memory::HostOrDeviceSlice;
use core::mem::MaybeUninit;
impl_poseidon!("bls12_381", bls12_381, ScalarField, ScalarCfg);
#[cfg(test)]
pub(crate) mod tests {
use crate::curve::ScalarField;
use icicle_core::impl_poseidon_tests;
use icicle_core::poseidon::tests::*;
impl_poseidon_tests!(ScalarField, 32, "bls12_381", 55);
}

View File

@@ -0,0 +1,21 @@
use crate::curve::{ScalarCfg, ScalarField};
use icicle_core::error::IcicleResult;
use icicle_core::impl_tree_builder;
use icicle_core::poseidon::PoseidonConstants;
use icicle_core::traits::IcicleResultWrap;
use icicle_core::tree::{TreeBuilder, TreeBuilderConfig};
use icicle_cuda_runtime::device_context::DeviceContext;
use icicle_cuda_runtime::error::CudaError;
use icicle_cuda_runtime::memory::HostOrDeviceSlice;
impl_tree_builder!("bls12_381", bls12_381, ScalarField, ScalarCfg);
#[cfg(test)]
pub(crate) mod tests {
use crate::curve::ScalarField;
use icicle_core::impl_tree_builder_tests;
use icicle_core::tree::tests::*;
impl_tree_builder_tests!(ScalarField);
}

View File

@@ -1,6 +1,6 @@
[package]
name = "icicle-bn254"
version = "1.0.0"
version = "1.2.0"
edition = "2021"
authors = [ "Ingonyama" ]
description = "Rust wrapper for the CUDA implementation of BN254 pairing friendly elliptic curve by Ingonyama"

View File

@@ -1,5 +1,7 @@
pub mod curve;
pub mod msm;
pub mod ntt;
pub mod poseidon;
pub mod tree;
impl icicle_core::SNARKCurve for curve::CurveCfg {}

View File

@@ -0,0 +1,22 @@
use crate::curve::{ScalarCfg, ScalarField};
use icicle_core::error::IcicleResult;
use icicle_core::impl_poseidon;
use icicle_core::poseidon::{Poseidon, PoseidonConfig, PoseidonConstants};
use icicle_core::traits::IcicleResultWrap;
use icicle_cuda_runtime::device_context::DeviceContext;
use icicle_cuda_runtime::error::CudaError;
use icicle_cuda_runtime::memory::HostOrDeviceSlice;
use core::mem::MaybeUninit;
impl_poseidon!("bn254", bn254, ScalarField, ScalarCfg);
#[cfg(test)]
pub(crate) mod tests {
use crate::curve::ScalarField;
use icicle_core::impl_poseidon_tests;
use icicle_core::poseidon::tests::*;
impl_poseidon_tests!(ScalarField, 32, "bn254", 56);
}

View File

@@ -0,0 +1,21 @@
use crate::curve::{ScalarCfg, ScalarField};
use icicle_core::error::IcicleResult;
use icicle_core::impl_tree_builder;
use icicle_core::poseidon::PoseidonConstants;
use icicle_core::traits::IcicleResultWrap;
use icicle_core::tree::{TreeBuilder, TreeBuilderConfig};
use icicle_cuda_runtime::device_context::DeviceContext;
use icicle_cuda_runtime::error::CudaError;
use icicle_cuda_runtime::memory::HostOrDeviceSlice;
impl_tree_builder!("bn254", bn254, ScalarField, ScalarCfg);
#[cfg(test)]
pub(crate) mod tests {
use crate::curve::ScalarField;
use icicle_core::impl_tree_builder_tests;
use icicle_core::tree::tests::*;
impl_tree_builder_tests!(ScalarField);
}

View File

@@ -1,6 +1,6 @@
[package]
name = "icicle-bw6-761"
version = "1.0.0"
version = "1.2.0"
edition = "2021"
authors = [ "Ingonyama" ]
description = "Rust wrapper for the CUDA implementation of BW6-761 pairing friendly elliptic curve by Ingonyama"

View File

@@ -1,5 +1,7 @@
pub mod curve;
pub mod msm;
pub mod ntt;
pub mod poseidon;
pub mod tree;
impl icicle_core::SNARKCurve for curve::CurveCfg {}

View File

@@ -0,0 +1,8 @@
#[cfg(test)]
pub(crate) mod tests {
use crate::curve::ScalarField;
use icicle_core::impl_poseidon_tests;
use icicle_core::poseidon::tests::*;
impl_poseidon_tests!(ScalarField, 48, "bw6-761", 56);
}

View File

@@ -0,0 +1,8 @@
#[cfg(test)]
pub(crate) mod tests {
use crate::curve::ScalarField;
use icicle_core::impl_tree_builder_tests;
use icicle_core::tree::tests::*;
impl_tree_builder_tests!(ScalarField);
}